Initial commit of Astro version

This commit is contained in:
Bad Manners 2024-08-15 21:27:00 -03:00
commit e3d1f6999b
79 changed files with 9880 additions and 0 deletions

26
.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
*.log
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# local project settings
.vscode/*
!.vscode/extensions.json
!.vscode/launch.json
!.vscode/settings.json

1
.prettierignore Normal file
View file

@ -0,0 +1 @@
src/components/DarkModeScript.astro

10
.prettierrc.mjs Normal file
View file

@ -0,0 +1,10 @@
/** @type {import("prettier").Config} */
export default {
trailingComma: "all",
tabWidth: 2,
semi: true,
singleQuote: false,
printWidth: 120,
bracketSpacing: true,
plugins: ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
};

3
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,3 @@
{
"recommendations": ["astro-build.astro-vscode", "bradlc.vscode-tailwindcss", "esbenp.prettier-vscode"]
}

11
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

16
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,16 @@
{
"files.associations": {
"*.css": "tailwindcss"
},
"[astro]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.configPath": ".prettierrc.mjs",
"editor.defaultFormatter": "esbenp.prettier-vscode"
}

1
LICENSE.md Normal file
View file

@ -0,0 +1 @@
See [public/licenses.txt](public/licenses.txt)

48
README.md Normal file
View file

@ -0,0 +1,48 @@
# badmanners.xyz
Bad Manners's personal website, built using Astro + Typescript + TailwindCSS.
## Requirements
- Node.js 20+
- (optional) rsync or LFTP, for remote deployment.
## Development
### Setup
```bash
git clone https://git.badmanners.xyz/badmanners/badmanners.xyz
cd badmanners.xyz
npm install
```
### Local development
```bash
npm run dev # Start development server (quit with Ctrl-C)
npm run prettier # Prettier formatting
```
### Build and deploy to remote
```bash
npm run build
```
Then, if using rsync, after configuring the `websitebm` host (or the name of your choosing) in `~/.ssh/config`, you can use a command like:
```bash
rsync --delete-after -acP dist/ websitebm:/home/public
```
Or if you prefer LFTP, create a `.env` file at the root of the project:
```env
DEPLOY_LFTP_HOST=https://example-webdav-server.com
DEPLOY_LFTP_USER=example_user
DEPLOY_LFTP_PASSWORD=sup3r_s3cr3t_password
DEPLOY_LFTP_TARGETFOLDER=sites/badmanners.xyz/
```
Then run `npm run deploy-lftp`.

19
astro.config.mjs Normal file
View file

@ -0,0 +1,19 @@
import { defineConfig } from "astro/config";
import tailwindIntegration from "@astrojs/tailwind";
// https://astro.build/config
export default defineConfig({
site: "https://badmanners.xyz",
integrations: [
tailwindIntegration({
applyBaseStyles: false,
}),
],
build: {
assets: "assets",
},
outDir: "./dist",
redirects: {
"/tos": "/terms_of_service",
},
});

7833
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

34
package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "badmanners.xyz",
"type": "module",
"version": "2.0.0",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro check && astro build",
"preview": "astro preview",
"sync": "astro sync",
"check": "astro check",
"astro": "astro",
"prettier": "prettier --write .",
"deploy-lftp": "dotenv tsx scripts/deploy-lftp.ts --"
},
"dependencies": {
"@astrojs/check": "^0.9.2",
"@astrojs/rss": "^4.0.7",
"@astrojs/tailwind": "^5.1.0",
"astro": "^4.13.3",
"date-fns": "^3.6.0",
"tailwindcss": "^3.4.9",
"tippy.js": "^6.3.7",
"typescript": "^5.5.4"
},
"devDependencies": {
"commander": "^12.1.0",
"dotenv-cli": "^7.4.2",
"prettier": "^3.3.3",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.6.6",
"tsx": "^4.17.0"
}
}

2
public/.htaccess Normal file
View file

@ -0,0 +1,2 @@
ErrorDocument 404 /404.html
RedirectMatch 301 ^/tos(\/(index.html)?)?$ /terms_of_service/

View file

@ -0,0 +1,28 @@
{
"subject": "acct:BadManners@meow.social",
"aliases": [
"https://meow.social/@BadManners",
"https://meow.social/users/BadManners"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://meow.social/@BadManners"
},
{
"rel": "http://webfinger.net/rel/avatar",
"type": "image/webp",
"href": "https://badmanners.xyz/logo.webp"
},
{
"rel": "self",
"type": "application/activity+json",
"href": "https://meow.social/users/BadManners"
},
{
"rel": "http://ostatus.org/schema/1.0/subscribe",
"template": "https://meow.social/authorize_interaction?uri={uri}"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

9
public/browserconfig.xml Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>
<TileColor>#37b340</TileColor>
</tile>
</msapplication>
</browserconfig>

BIN
public/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

BIN
public/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

13
public/licenses.txt Normal file
View file

@ -0,0 +1,13 @@
The briefcase logo and Sam Brendan are copyrighted and trademarked by me.
All attributed artwork is copyrighted by their respective creators and distributed under non-commercial use.
The Jost* typeface is copyrighted by indestructible type* and is distributed under the SIL Open Font License v1.1: https://opensource.org/license/ofl-1-1
The SVG icons for Discord, Telegram, Signal, Mastodon, Bluesky, Weasyl, X, and Keybase were created for the Simple Icons project and are distributed under the Creative Commons Zero v1.0 Universal license: https://creativecommons.org/publicdomain/zero/1.0/
The SVG icons for Eka's Portal, Fur Affinity, Inkbunny, SoFurry, and SubscribeStar were created by me for personal use, and are distributed under the Creative Commons Zero v1.0 Universal license: https://creativecommons.org/publicdomain/zero/1.0/
The generic SVG icons were created by Font Awesome and are distributed under CC-BY-4.0: https://creativecommons.org/licenses/by/4.0/
All third-party trademarks belong to their respective owners, and I'm not affiliated with any of them.

BIN
public/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
public/mstile-150x150.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View file

@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="400.000000pt" height="400.000000pt" viewBox="0 0 400.000000 400.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,400.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1600 2960 l0 -80 -320 0 -320 0 0 -80 0 -80 -80 0 -80 0 0 -880 0
-880 80 0 80 0 0 -80 0 -80 1040 0 1040 0 0 80 0 80 80 0 80 0 0 880 0 880
-80 0 -80 0 0 80 0 80 -320 0 -320 0 0 80 0 80 -400 0 -400 0 0 -80z m-160
-320 l0 -80 -160 0 -160 0 0 80 0 80 160 0 160 0 0 -80z m1440 0 l0 -80 -160
0 -160 0 0 80 0 80 160 0 160 0 0 -80z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 857 B

18
public/site.webmanifest Normal file
View file

@ -0,0 +1,18 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-384x384.png",
"sizes": "384x384",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff"
}

62
scripts/deploy-lftp.ts Normal file
View file

@ -0,0 +1,62 @@
import { spawn } from "node:child_process";
import { program, Option } from "commander";
interface DeployLftpOptions {
host: string;
user: string;
password: string;
targetFolder: string;
sourceFolder: string;
}
async function deployLftp({ host, user, password, targetFolder, sourceFolder }: DeployLftpOptions) {
const process = spawn(
"lftp",
[
"-c",
[
`open -u ${user},${password} ${host}`,
`mirror --reverse --include-glob assets/* --delete --only-missing --no-perms --verbose ${sourceFolder} ${targetFolder}`,
`mirror --reverse --exclude-glob assets/* --delete --no-perms --verbose ${sourceFolder} ${targetFolder}`,
`bye`,
].join("\n"),
],
{
stdio: "inherit",
},
);
await new Promise<void>((resolve, reject) => {
process.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(`deploy-lftp failed with code ${code}`);
}
});
});
}
await program
.name("deploy-lftp")
.description("Deploy files to remote server with LFTP")
.addOption(
new Option("-h, --host <hostname>", "Hostname of the LFTP (i.e. WebDav, SCP, SFTP...) remote.").env(
"DEPLOY_LFTP_HOST",
),
)
.addOption(new Option("-u, --user <username>", "Username portion of the LFTP credentials").env("DEPLOY_LFTP_USER"))
.addOption(
new Option("-p, --password <pass>", "Password portion of the LFTP credentials").env("DEPLOY_LFTP_PASSWORD"),
)
.addOption(
new Option("-t, --target-folder <remoteDir>", "Folder to mirror files to in the LFTP remote").env(
"DEPLOY_LFTP_TARGETFOLDER",
),
)
.addOption(
new Option("-s, --source-folder <localDir>", "Folder to read files from in the local machine")
.env("DEPLOY_LFTP_SOURCEFOLDER")
.default("dist/"),
)
.action(deployLftp)
.parseAsync();

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -0,0 +1,78 @@
---
import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
---
<template id="template-modal-age-restricted">
<div
id="modal-age-restricted"
class="fixed inset-0 bg-stone-100 dark:bg-stone-900"
tabindex="-1"
role="dialog"
aria-hidden="false"
>
<div class="mx-auto flex min-h-screen max-w-3xl flex-col items-center justify-center text-center tracking-tight">
<div class="text-bm-500 dark:text-bm-400">
<IconTriangleExclamation width="3rem" height="3rem" />
</div>
<div class="pb-3 pt-2 text-3xl font-light text-stone-700 sm:pb-4 sm:pt-2 dark:text-stone-50">
Age verification
</div>
<div
class="mx-6 mb-4 max-w-xl border-b border-stone-300 pb-4 text-xl text-stone-700 dark:border-stone-300 dark:text-stone-50"
>
You must be 18+ to access this page.
</div>
<p class="px-8 text-lg font-light leading-snug text-stone-700 sm:max-w-2xl dark:text-stone-50">
By confirming that you are at least 18 years old, your selection will be saved to your browser to prevent this
screen from appearing in the future.
</p>
<div class="flex w-full max-w-md flex-col-reverse justify-evenly gap-y-5 px-6 pt-5 sm:max-w-2xl sm:flex-row">
<button
data-modal-reject
id="age-verification-reject"
class="rounded bg-stone-400 py-3 text-lg text-stone-900 hover:bg-stone-500 hover:text-stone-50 focus:bg-stone-500 focus:text-stone-50 sm:px-9 dark:bg-stone-300 dark:text-stone-900 dark:hover:bg-stone-600 dark:hover:text-stone-50 dark:focus:bg-stone-600 dark:focus:text-stone-50"
>
Cancel
</button>
<button
data-modal-accept
id="age-verification-accept"
class="rounded bg-bm-500 py-3 text-lg text-stone-900 hover:bg-stone-500 hover:text-stone-50 focus:bg-stone-500 focus:text-stone-50 sm:px-9 dark:bg-bm-400 dark:text-stone-900 dark:hover:bg-stone-600 dark:hover:text-stone-50 dark:focus:bg-stone-600 dark:focus:text-stone-50"
>
I'm at least 18 years old
</button>
</div>
</div>
</div>
</template>
<script>
(function () {
if (localStorage.getItem("ageVerified") !== "true") {
document.body.appendChild(
document
.querySelector<HTMLElementTagNameMap["template"]>("template#template-modal-age-restricted")!
.content.cloneNode(true),
);
const modal = document.querySelector<HTMLElementTagNameMap["div"]>("body > div#modal-age-restricted")!;
const rejectButton = modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-reject]")!;
const acceptButton = modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-accept]")!;
function onRejectButtonClick(e: MouseEvent) {
e.preventDefault();
location.href = "about:blank";
}
function onAcceptButtonClick(e: MouseEvent) {
e.preventDefault();
rejectButton.removeEventListener("click", onRejectButtonClick);
acceptButton.removeEventListener("click", onAcceptButtonClick);
localStorage.setItem("ageVerified", "true");
document.body.style.overflow = "auto";
modal.remove();
}
rejectButton.addEventListener("click", onRejectButtonClick);
acceptButton.addEventListener("click", onAcceptButtonClick);
document.body.style.overflow = "hidden";
rejectButton.focus();
}
})();
</script>

View file

@ -0,0 +1,29 @@
---
---
<script is:inline>!function(){var a="dark",b="auto",c="colorScheme",d=document.body.classList,e=localStorage,f=e.getItem(c);f&&f!==b?f===a&&d.add(a):(e.setItem(c,b),matchMedia("(prefers-color-scheme: dark)").matches&&d.add(a))}();</script>
<script>
(function () {
let colorScheme = localStorage.getItem("colorScheme");
if (colorScheme == null || colorScheme === "auto") {
colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
document.querySelectorAll<HTMLElementTagNameMap["button"]>("button[data-dark-mode]").forEach((button) => {
button.classList.remove("hidden");
button.removeAttribute("aria-hidden");
button.addEventListener("click", (e) => {
e.preventDefault();
if (colorScheme === "dark") {
colorScheme = "light";
document.body.classList.remove("dark");
} else {
colorScheme = "dark";
document.body.classList.add("dark");
}
localStorage.setItem("colorScheme", colorScheme);
});
});
})();
</script>

View file

@ -0,0 +1,38 @@
---
interface NavRoute {
path: string;
name: string;
}
const navRoutes: NavRoute[] = [
{ path: "/", name: "Home" },
{ path: "/about", name: "About me" },
{ path: "/work", name: "My work" },
{ path: "/contact", name: "Contact" },
];
const isCurrentRoute = (navRoute: NavRoute) => Astro.url.pathname == navRoute.path;
---
<nav class="text-md pb-6 pt-2 sm:text-lg print:hidden">
<ul class="flex flex-wrap justify-center gap-x-4 gap-y-6 py-6 tracking-wide sm:gap-x-5 sm:gap-y-8">
{
navRoutes.map((route) => (
<li>
<a
href={route.path}
class:list={[
"rounded border px-6 py-2 uppercase transition-colors hover:underline focus:underline sm:px-8 sm:py-3",
isCurrentRoute(route)
? "border-bm-500 text-bm-500 dark:border-bm-400 dark:text-bm-400"
: "border-stone-600 text-stone-600 hover:border-bm-500 hover:text-bm-500 focus:border-bm-500 focus:text-bm-500 dark:border-zinc-300 dark:text-zinc-300 dark:hover:border-bm-400 dark:hover:text-bm-400 dark:focus:border-bm-400 dark:focus:text-bm-400",
]}
aria-current-value={isCurrentRoute(route).toString()}
>
{route.name}
</a>
</li>
))
}
</ul>
</nav>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M184 48l144 0c4.4 0 8 3.6 8 8l0 40L176 96l0-40c0-4.4 3.6-8 8-8zm-56 8l0 40L64 96C28.7 96 0 124.7 0 160l0 96 192 0 128 0 192 0 0-96c0-35.3-28.7-64-64-64l-64 0 0-40c0-30.9-25.1-56-56-56L184 0c-30.9 0-56 25.1-56 56zM512 288l-192 0 0 32c0 17.7-14.3 32-32 32l-64 0c-17.7 0-32-14.3-32-32l0-32L0 288 0 416c0 35.3 28.7 64 64 64l384 0c35.3 0 64-28.7 64-64l0-128z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 320 512">
<path
d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 320 512">
<path
d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M64 464H288c8.8 0 16-7.2 16-16V384h48v64c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V224c0-35.3 28.7-64 64-64h64v48H64c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16zM224 352c-35.3 0-64-28.7-64-64V64c0-35.3 28.7-64 64-64H448c35.3 0 64 28.7 64 64V288c0 35.3-28.7 64-64 64H224z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 384 512">
<path
d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M48 64C21.5 64 0 85.5 0 112c0 15.1 7.1 29.3 19.2 38.4L236.8 313.6c11.4 8.5 27 8.5 38.4 0L492.8 150.4c12.1-9.1 19.2-23.3 19.2-38.4c0-26.5-21.5-48-48-48H48zM0 176V384c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V176L294.4 339.2c-22.8 17.1-54 17.1-76.8 0L0 176z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 384 512">
<path
d="M223.5 32C100 32 0 132.3 0 256S100 480 223.5 480c60.6 0 115.5-24.2 155.8-63.4c5-4.9 6.3-12.5 3.1-18.7s-10.1-9.7-17-8.5c-9.8 1.7-19.8 2.6-30.1 2.6c-96.9 0-175.5-78.8-175.5-176c0-65.8 36-123.1 89.3-153.3c6.1-3.5 9.2-10.5 7.7-17.3s-7.3-11.9-14.3-12.5c-6.3-.5-12.6-.8-19-.8z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 448 512">
<path
d="M64 32C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM96 136c0-13.3 10.7-24 24-24c137 0 248 111 248 248c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-110.5-89.5-200-200-200c-13.3 0-24-10.7-24-24zm0 96c0-13.3 10.7-24 24-24c83.9 0 152 68.1 152 152c0 13.3-10.7 24-24 24s-24-10.7-24-24c0-57.4-46.6-104-104-104c-13.3 0-24-10.7-24-24zm0 120a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M361.5 1.2c5 2.1 8.6 6.6 9.6 11.9L391 121l107.9 19.8c5.3 1 9.8 4.6 11.9 9.6s1.5 10.7-1.6 15.2L446.9 256l62.3 90.3c3.1 4.5 3.7 10.2 1.6 15.2s-6.6 8.6-11.9 9.6L391 391 371.1 498.9c-1 5.3-4.6 9.8-9.6 11.9s-10.7 1.5-15.2-1.6L256 446.9l-90.3 62.3c-4.5 3.1-10.2 3.7-15.2 1.6s-8.6-6.6-9.6-11.9L121 391 13.1 371.1c-5.3-1-9.8-4.6-11.9-9.6s-1.5-10.7 1.6-15.2L65.1 256 2.8 165.7c-3.1-4.5-3.7-10.2-1.6-15.2s6.6-8.6 11.9-9.6L121 121 140.9 13.1c1-5.3 4.6-9.8 9.6-11.9s10.7-1.5 15.2 1.6L256 65.1 346.3 2.8c4.5-3.1 10.2-3.7 15.2-1.6zM160 256a96 96 0 1 1 192 0 96 96 0 1 1 -192 0zm224 0a128 128 0 1 0 -256 0 128 128 0 1 0 256 0z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 512 512">
<path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
></path>
</SVGIcon>

View file

@ -0,0 +1,16 @@
---
type Props = {
width: string;
height: string;
viewBox: string;
class?: string;
fill?: string;
children: any;
};
const { width, height, class: className, fill = "currentColor", viewBox } = Astro.props;
---
<svg style={{ width, height, fill }} class={className} viewBox={viewBox} aria-hidden>
<slot />
</svg>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"
></path>
</SVGIcon>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 72 72">
<path
d="M58.914 44.015c-.247.468-1.187 1.528-1.95 1.98-1.493.873-1.984 1.014-1.65.48.299-.483-1.403-.035-2.613.696-.55.326-1.211.898-1.326.783-.388-.388-1.69 3.786-1.535 4.82.174 2.417-1.846 3.545-3.733 5.455-1.885 1.908-3.682 3.793-3.816 3.793-.134 0-.756-.771-2.183-2.496-3.132-3.781-4.04-5.785-4.088-8.04.07-.472 1.116-.812 1.943-1.045 1.046-.29 2.79-1.198 2.79-1.198s2.917-1.63 3.026-1.694c.684-.35 1.374-.929 1.755-1.268 1.093-.943 1.736-2.4 1.929-3.16.134-.536-.288-1.356-.756-2.11l-.66-1.14.813-3.384c.467-1.795.455-4.315 1.54-5.147 2.835-.935 5.735-5.197 8.36-9.084 4.032-5.97 5.191-10.89 3.14-13.33-1.568-1.86-3.662-1.506-10.12 1.713-2.213 1.104-5.978 3.183-8.363 4.618-2.385 1.438-4.386 2.57-4.437 2.519-.05-.055.13-1.459.401-3.121.755-4.594.97-9.542.503-11.495C37.381 1.068 36.273-.016 34.83 0c-.864.008-1.846.413-2.893 1.234-4.306 3.384-9.427 10.025-11.403 16.29-1.62 5.134-3.793 16.975-3.538 19.958.52 6.127.924 5.226-.33 7.368C13.137 50.87 11 59.585 11 67.936c0 3.744-.43 4.064.125 4.064h2.986c.444-.083.287-.795.444-5.393.196-5.765.79-9.322 2.342-14.018.849-2.57 3.149-7.487 4.092-8.737.16-.216.44-.381.46-.577.035-.32-.134-.74-.516-1.534-.57-1.182-.837-2.047-.837-5.317.173-3.352.232-3.984.845-5.364.126-.291.524-1.019.68-1.56.33-1.125.311-2.488.546-3.863 1.584-9.31 3.232-14.91 8.526-20.452 3.168-2.848 4.552-3.734 4.54 1.023.334 4-1.334 12.948-3.056 17.381-.519 1.337-1.179 2.638-1.123 2.693.255.326 3.547-.484 3.91-1.597.133-.405.174-.398.318-.376.143.021 1.478 1.026 1.921 1.131.495.11 1.828-.515 1.876-.715.046-.2-2.593-1.83-2.774-1.997-.034-.388-.099-.453-.015-.825.084-.372.14-.615.899-1.105.758-.49 16.982-10.218 19.577-10.27.94-.039 1.35 1.426 1.368 1.935.017.509.04 2.72-.999 4.24-1.038 1.521-8.414 11.11-9.282 11.873-.867.763-2.678.75-3.319.386s-3.705-1.568-3.437-1.069c.271.503 1.255 1.324 1.79 1.933.369.44.99 1.265 1.38 1.835l.585.998-1.65 7.365.758 1.113c.597 1.014.605 1.143.126 2.142-.236.479-1.026 1.245-1.706 1.93-.31.286-4.578 3.006-6.96 3.776-1.781.515-1.615.295-3.12-.008-1.506-.302-4.52-2.453-5.124-1.858-.604.596.877 1.813 1.328 1.988.45.174.61.564 1.328.859.501.216 1.186 1.002 1.221 1.486.031.633.055.525.094 1.126.095 3.914.833 5.966 3.942 12.862 1.207 2.677 2.29 5.247 2.41 5.715.14.562.11.845.542.845h1.963c.597 0-.059-1.753-2.244-6.626-3.474-7.758-4.613-11.628-2.877-9.005.62 1.03 2.13 3.055 3.352 4.493 4.453 5.247 5.384 7.306 4.39 9.688-.42 1.002-.594 1.45-.26 1.45h2.09c.448 0 .579-.137.898-.896.573-1.378 1.536-2.204.95-3.842l-.243-1.14 2.936-4.048c1.064-1.399 2.203-3.179 2.738-3.761.538-.582.664-.797.664-.797.495.24 2.253-.09 2.522-.351.268-.261-.632-.143-.749-.538-.102-.169.707-.677 1.422-.965 1.418-.57 2.842-2.264 2.932-2.975.245-.297.267.551.277.93-.024.628-1.583 2.249-2.043 3.22-.46.973-.692 1.46-.543 2.44.149.98 1.215 1.857 2.648 1.787 1.432-.07 2.275-.936 2.295-2.316.02-1.565-1.778-6.887-1.667-6.864.11.028.382-7.094.22-7.793-.162-.699-.437-.795-.437-.795zm-33.575-7.213c.008.425 1.333.585 1.44-.075.157-.99 1.24-1.77 2.004-1.707.764.061 1.194.103 1.556.815.363.713.14 1.378.24 1.654.102.276 1.16.317 1.227-.133.066-.451.319-2.732-.927-3.389-1.247-.657-2.26-.776-3.545-.294-1.286.482-2.019 1.863-1.995 3.13zm15.64 1.859c.066-.21.446-.797.856-.574.41.222.252.614.245.81-.007.195.68.652.798.415.118-.237.186-.63.191-.795.006-.166.081-.753-.226-1.053-.307-.3-.894-.548-1.583-.33-.69.217-1.111.871-1.195 1.15-.084.277.846.587.913.377zm-6.251-1.067s.998 1.791 1.358 2.158c.36.367 1.502.72 2.08.456.578-.263 1.245-1.528 1.276-1.775a136.192 136.192 0 0 1-4.714-.84zm-.063 5.955c-.837.182-2.516.256-3.887-.012-1.371-.271-2.343.357-2.013.676.668.637 1.955.795 2.753.906 1.268.173 2.766-.017 3.207-.085.44-.068 1.364-.557 1.482-.47.06.044.616.507 1.245.822.596.298 1.47.464 1.727.503.256.039.982-.631.795-.747-.188-.115-.727-.27-1.018-.367a4.407 4.407 0 0 1-1.13-.542c-.533-.351-1.057-.865-1.2-1.23-.144-.366-.389.024-.7.157-.412.177-.424.205-1.261.39zm15.387 14.486c-1.143 1.549-1.374 1.888-2.364 3.201-1.446 1.91-2.78 3.526-2.955 3.585-.177.058-.511-.19-.735-.55-.374-.602-.146-.956 2.542-3.946 1.154-1.29 1.563-1.734 2.73-3.012.252-.272.458-.51.59-.503.132.006.631.368.732.44.1.072-.145.329-.54.785zM20.637 68.697c-.146 2.28-.126 3.301.118 3.301h1.764c.181 0 .123-.427.13-.887.031-3.03.812-8.371 1.31-12.02.116-.793-.597-1.307-1.142-1.115-.543.192-1.968 7.365-2.18 10.721z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M10.445 21.372a.953.953 0 1 1-.955-.954c.524 0 .951.43.951.955m5.923-.001a.953.953 0 1 1-.958-.954c.526 0 .954.43.954.955m4.544-9.16l-.156-.204c-.046-.06-.096-.116-.143-.175-.045-.06-.094-.113-.141-.169-.104-.12-.21-.239-.32-.359l-.075-.08-.091-.099-.135-.13c-.015-.019-.032-.035-.05-.054a10.87 10.87 0 0 0-3.955-2.504l-.23-.078.035-.083a4.109 4.109 0 0 0-.12-3.255 4.11 4.11 0 0 0-2.438-2.16c-.656-.216-1.23-.319-1.712-.305-.033-.105-.1-.577.496-1.848L10.662 0l-.287.399c-.33.455-.648.895-.945 1.328a1.857 1.857 0 0 0-1.245-.58L6.79 1.061h-.012c-.033-.003-.07-.003-.104-.003-.99 0-1.81.771-1.87 1.755l-.088 1.402v.003a1.876 1.876 0 0 0 1.755 1.98l1.002.06c-.065.84.073 1.62.405 2.306a11.28 11.28 0 0 0-3.66 2.484C.912 14.392.912 18.052.912 20.995v1.775l1.305-1.387c.266.93.652 1.807 1.145 2.615H5.06a9.197 9.197 0 0 1-1.68-3.848l1.913-2.03-.985 3.09 1.74-1.267c3.075-2.234 6.745-2.75 10.91-1.53 1.806.533 3.56.04 4.474-1.256l.104-.165c.09.498.14.998.14 1.496 0 1.563-.254 3.687-1.38 5.512h1.612c.776-1.563 1.181-3.432 1.181-5.512-.001-2.2-.786-4.421-2.184-6.274zM8.894 6.192c.122-1.002.577-1.949 1.23-2.97a1.36 1.36 0 0 0 1.283.749c.216-.008.604.025 1.233.232a2.706 2.706 0 0 1 1.608 1.425c.322.681.349 1.442.079 2.15a2.69 2.69 0 0 1-.806 1.108l-.408-.502-.002-.003a1.468 1.468 0 0 0-2.06-.205c-.334.27-.514.66-.534 1.058-1.2-.54-1.8-1.643-1.628-3.04zm4.304 5.11l-.52.425a.228.228 0 0 1-.323-.032l-.11-.135a.238.238 0 0 1 .034-.334l.51-.42-1.056-1.299a.307.307 0 0 1 .044-.436.303.303 0 0 1 .435.041l2.963 3.646a.309.309 0 0 1-.168.499.315.315 0 0 1-.31-.104l-.295-.365-1.045.854a.244.244 0 0 1-.154.055.237.237 0 0 1-.186-.09l-.477-.58a.24.24 0 0 1 .035-.335l1.05-.858-.425-.533zM7.752 4.866l-1.196-.075a.463.463 0 0 1-.435-.488l.09-1.4a.462.462 0 0 1 .461-.437h.024l1.401.091a.459.459 0 0 1 .433.488l-.007.101a9.27 9.27 0 0 0-.773 1.72zm12.525 11.482c-.565.805-1.687 1.08-2.924.718-3.886-1.141-7.397-.903-10.469.7l1.636-5.122-5.29 5.609c.098-3.762 2.452-6.967 5.757-8.312.471.373 1.034.66 1.673.841.16.044.322.074.48.102a1.41 1.41 0 0 0 .21 1.408l.075.09c-.172.45-.105.975.221 1.374l.476.582a1.39 1.39 0 0 0 1.079.513c.32 0 .635-.111.886-.314l.285-.232c.174.074.367.113.566.113a1.45 1.45 0 0 0 .928-.326c.623-.51.72-1.435.209-2.06l-1.67-2.057a4.07 4.07 0 0 0 .408-.38c.135.036.27.077.4.12.266.096.533.197.795.314a9.55 9.55 0 0 1 2.77 1.897c.03.03.06.055.086.083l.17.176c.038.039.076.079.11.12.08.085.16.175.24.267l.126.15c.045.053.086.104.13.16l.114.15c.04.05.079.102.117.154.838 1.149.987 2.329.404 3.157v.005zM7.718 4.115l-.835-.05.053-.836.834.051z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="m9.12.35.27 1.09a10.845 10.845 0 0 0-3.015 1.248l-.578-.964A11.955 11.955 0 0 1 9.12.35zm5.76 0-.27 1.09a10.845 10.845 0 0 1 3.015 1.248l.581-.964A11.955 11.955 0 0 0 14.88.35zM1.725 5.797A11.955 11.955 0 0 0 .351 9.119l1.09.27A10.845 10.845 0 0 1 2.69 6.374zm-.6 6.202a10.856 10.856 0 0 1 .122-1.63l-1.112-.168a12.043 12.043 0 0 0 0 3.596l1.112-.169A10.856 10.856 0 0 1 1.125 12zm17.078 10.275-.578-.964a10.845 10.845 0 0 1-3.011 1.247l.27 1.091a11.955 11.955 0 0 0 3.319-1.374zM22.875 12a10.856 10.856 0 0 1-.122 1.63l1.112.168a12.043 12.043 0 0 0 0-3.596l-1.112.169a10.856 10.856 0 0 1 .122 1.63zm.774 2.88-1.09-.27a10.845 10.845 0 0 1-1.248 3.015l.964.581a11.955 11.955 0 0 0 1.374-3.326zm-10.02 7.875a10.952 10.952 0 0 1-3.258 0l-.17 1.112a12.043 12.043 0 0 0 3.597 0zm7.125-4.303a10.914 10.914 0 0 1-2.304 2.302l.668.906a12.019 12.019 0 0 0 2.542-2.535zM18.45 3.245a10.914 10.914 0 0 1 2.304 2.304l.906-.675a12.019 12.019 0 0 0-2.535-2.535zM3.246 5.549A10.914 10.914 0 0 1 5.55 3.245l-.675-.906A12.019 12.019 0 0 0 2.34 4.874zm19.029.248-.964.577a10.845 10.845 0 0 1 1.247 3.011l1.091-.27a11.955 11.955 0 0 0-1.374-3.318zM10.371 1.246a10.952 10.952 0 0 1 3.258 0L13.8.134a12.043 12.043 0 0 0-3.597 0zM3.823 21.957 1.5 22.5l.542-2.323-1.095-.257-.542 2.323a1.125 1.125 0 0 0 1.352 1.352l2.321-.532zm-2.642-3.041 1.095.255.375-1.61a10.828 10.828 0 0 1-1.21-2.952l-1.09.27a11.91 11.91 0 0 0 1.106 2.852zm5.25 2.437-1.61.375.255 1.095 1.185-.275a11.91 11.91 0 0 0 2.851 1.106l.27-1.091a10.828 10.828 0 0 1-2.943-1.217zM12 2.25a9.75 9.75 0 0 0-8.25 14.938l-.938 4 4-.938A9.75 9.75 0 1 0 12 2.25z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 64 64">
<path
d="M16.998 56.139c-3.483-.313-6.077-1.151-8.817-2.85-1.2-.745-1.99-1.373-3.22-2.565-1.27-1.23-2.101-2.23-3.042-3.663C1.323 46.154.004 43.696 0 43.484c-.003-.127.642-.091 1.265.07.316.082.59.134.609.116.019-.019-.066-.356-.187-.75-.703-2.275-.741-5.302-.1-7.857.679-2.702 2.023-5.117 3.99-7.169l1-1.043.178.567c.098.311.18.697.184.856.004.159.034.29.067.29s.41-.214.839-.474c3.491-2.12 7.817-2.179 11.225-.154.818.486 2.199 1.744 2.774 2.528.545.742 1.16 2.037 1.461 3.08.431 1.489.534 3.07.317 4.867l-.052.428-.707-.185c-1.395-.363-3.021-.203-4.173.411-.818.437-1.795 1.463-2.194 2.306-.32.674-.34.79-.34 1.876 0 1.085.021 1.203.337 1.87.415.876 1.157 1.613 2.083 2.069.722.356 1.837.636 2.527.636 1.224 0 3.052-.707 4.125-1.596 1.722-1.427 2.622-3.392 2.738-5.979.126-2.809-.505-5.298-2.39-9.42-1.822-3.984-2.493-6.404-2.599-9.38-.047-1.327-.022-1.926.118-2.764.42-2.509 1.414-4.424 3.18-6.127 2.175-2.097 5.092-3.31 8.256-3.437 1.262-.05 2.372.037 3.056.241.138.041.18-.07.23-.611.094-1.003.13-1.059.533-.851 1.488.766 3.272 2.398 4.234 3.875 1.201 1.842 1.712 3.483 1.802 5.79.065 1.662-.025 2.381-.92 7.387-.766 4.282-.875 5.151-.878 7.034-.003 1.482.024 1.76.228 2.31.198.533.775 1.323.965 1.321.032 0 .246-.367.476-.816.76-1.484 2.01-3.227 3.211-4.478 1.323-1.376 2.578-2.318 4.01-3.01l1.006-.486-.208-.473c-.146-.332-.223-.795-.258-1.56l-.05-1.086.41.07c1.318.222 2.772.758 3.846 1.42.282.173.524.315.538.315.013 0 .033-.344.045-.764.01-.42.06-.873.11-1.006l.09-.243.646.432c.847.565 2.001 1.717 2.788 2.78.347.47.661.865.698.88.038.014.2-.227.361-.535.161-.308.43-.722.597-.92l.305-.361.321.702c1.306 2.858 1.648 8.456.85 13.93-.844 5.791-2.563 9.862-5.357 12.686-2.172 2.195-4.194 3.067-7.104 3.063-1.668-.002-2.857-.2-4.497-.747-1.439-.48-2.255-.873-3.407-1.639-.922-.612-2.01-1.65-1.907-1.818.087-.14.98-.44 1.32-.443.53-.003.57-.121.27-.797-1.435-3.23-1.511-7.903-.197-12.062.147-.466.228-.882.18-.925-.048-.042-.29-.155-.537-.25-1.532-.593-2.635-1.942-3.154-3.858-.153-.565-.185-1.116-.181-3.158.004-2.199.044-2.772.362-5.158.468-3.513.507-5.76.121-6.88-.694-2.01-2.422-3.286-4.155-3.069-1.851.232-3.602 2.117-3.965 4.27-.295 1.75.205 3.449 2.138 7.258 1.965 3.873 2.696 5.787 3.218 8.42.623 3.15.589 6.597-.091 9.26-1.628 6.378-6.329 10.363-13.474 11.424-1.344.2-4.718.285-6.158.156zm38.56-5.629c1.611-.75 3.426-3.418 4.488-6.598 1.291-3.866 1.285-8.376-.015-11.01-.432-.875-1.105-1.543-1.66-1.647-.81-.152-2.248.65-3.448 1.922-1.708 1.81-2.954 4.326-3.559 7.182-.318 1.505-.4 4.178-.17 5.572.39 2.366 1.245 3.993 2.402 4.57.69.343 1.238.346 1.961.01zm-42.375-7.5c.099-1.947.728-3.439 2.058-4.878 1.197-1.295 2.751-2.147 4.532-2.484.538-.101.962-.234.962-.3 0-.066-.094-.485-.208-.931-.487-1.904-1.661-3.363-3.396-4.22-1.314-.648-2.2-.823-3.87-.764-1.518.054-2.535.329-3.684.995-.964.56-2.384 1.997-2.976 3.014-.489.838-1.332 3.164-1.196 3.3.106.105 1.495-.15 1.873-.345.175-.091.342-.142.37-.114.028.028.155.692.282 1.475.127.783.253 1.446.281 1.474.028.027.642-.086 1.366-.252.724-.167 1.39-.305 1.479-.307.138-.004.156.142.119.988-.076 1.746.384 3.107 1.37 4.056.244.233.474.425.512.425.038 0 .095-.51.126-1.132z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 64 64">
<path
d="M 32.00019,0 C 14.32707,-1.3999794e-4 8.0001026e-5,14.32668 0,31.9998 -1.3999897e-4,49.67308 14.32691,64.00014 32.00019,64 49.67332,63.99992 64.00013,49.67293 64,31.9998 63.99992,14.32683 49.67316,8.0002052e-5 32.00019,0 Z m 0.59937,2.13593 c 16.26582,0.32617 29.28728,13.5948 29.30758,29.86387 -0.003,11.56 -6.66849,22.08294 -17.11825,27.02624 -0.38823,0.18558 -0.40009,0.008 -0.60055,-0.1596 l -0.86844,0.86197 c -0.31417,0.13143 -1.06182,0.3489 -1.3803,0.46952 l 1.80069,-1.97511 -0.46666,-0.54541 -3.0624,3.07012 c -0.4467,0.12767 -0.89632,0.24492 -1.34849,0.35164 l 3.95664,-3.98385 -0.4474,-0.56276 -4.79767,4.82239 c -0.40573,0.0784 -0.81303,0.14831 -1.22166,0.2098 l 5.55109,-5.57981 -0.46137,-0.51546 -6.30585,6.25013 c -0.32445,0.0319 -0.6494,0.0585 -0.97472,0.0798 l 6.75192,-6.82778 -0.50138,-0.50138 -7.3995,7.3912 c -0.33738,0.0143 -0.67498,0.0228 -1.01265,0.0257 l -0.0162,-0.001 7.85544,-7.86255 -0.48726,-0.52407 -8.37135,8.31511 C 14.89099,61.28604 2.12257,48.09919 2.09286,32.00003 2.09386,20.30825 8.90851,9.68931 19.53666,4.81693 c 1.19581,1.7845 2.51514,3.27807 4.26645,5.03912 1.75132,1.76104 6.44772,5.48119 7.91499,6.84554 1.46726,1.36434 2.41584,2.41692 3.34612,3.78118 l 0.56105,0.95773 -7.84438,7.85228 -0.28132,-0.78428 7.28373,-7.25646 -0.93311,-0.088 -6.4731,6.51037 -0.30818,-0.71355 5.84002,-5.89268 -0.89633,-0.0129 -4.94369,4.89374 v -0.99803 l 3.77363,-3.85462 -0.92809,-0.0127 -2.84554,2.83764 V 22.9375 l 0.74912,-0.7669 1.04323,-1.0717 -0.96634,-0.007 -0.82601,0.79666 V 19.8823 H 16.47155 v 8.77399 c 0.12888,1.92584 0.58901,3.60728 1.06383,5.24582 0.47482,1.63853 1.74991,4.37062 4.51414,6.46464 2.76423,2.09402 6.4746,2.89636 9.32771,2.90206 2.85311,0.006 3.7047,-0.15797 4.34121,-0.25629 l -0.0126,-6.47337 c -0.0513,-0.93381 -0.32624,-1.90006 -1.04615,-2.8081 -0.71992,-0.90804 -1.14254,-1.20068 -2.7282,-1.51607 l 8.87867,-8.85917 0.55575,0.38319 -8.3264,8.48792 0.74422,0.15835 8.23357,-8.20039 0.5148,0.46309 -7.90202,7.93746 0.53888,0.43714 7.87283,-7.86492 0.50178,0.50178 -7.76575,7.77998 0.38404,0.60688 7.85749,-7.83711 0.43651,0.55456 -7.93762,7.95145 0.27855,0.72541 8.06563,-8.05813 0.39762,0.54147 -8.27876,8.35112 v 0.70447 l 0.12252,0.18484 0.17736,-0.18484 8.35489,-8.34756 0.31761,0.66066 -8.88085,8.88627 v 1.00435 l 9.11595,-9.08168 0.30028,0.71119 -9.41623,9.38236 v 1.01146 l 9.72204,-9.68816 0.2187,0.61757 0.22738,0.0338 C 48.47546,28.67972 48.52088,26.17642 48.22126,21.76326 47.92164,17.3501 43.25847,11.18902 39.48979,7.95054 35.72111,4.71206 34.67294,4.32282 32.59958,2.13581 Z m 3.87241,19.4695 0.74153,0.25094 -8.91305,8.93204 -0.27855,-0.7258 z m 1.6219,0.44449 0.66738,0.2813 -9.35615,9.33674 -0.49817,-0.47417 z m 1.41605,0.57685 0.60688,0.38443 -9.09962,9.11345 -0.71158,-0.3066 z m 7.55469,9.62323 -9.99182,9.96819 v 1.01344 L 47.35707,32.97491 47.25192,32.27777 Z m 0.49622,1.53651 -10.48804,10.45461 v 0.61877 l 0.41736,0.009 10.2711,-10.27742 z m -31.51056,1.16353 -0.10746,0.4113 h 0.20229 z m 31.76549,0.60851 -9.31375,9.31055 h 1.00435 l 8.39871,-8.39159 z m -31.385,0.4555 -0.67168,0.7021 0.0317,0.89231 1.03272,-1.04285 z m 0.77361,1.25011 -1.46386,1.41328 v 1.01621 l 1.78784,-1.75466 z m 30.80295,0.16794 -7.48159,7.437 h 1.01186 l 6.47732,-6.52394 z m -30.04396,1.08256 -2.16447,2.16617 0.0784,0.85952 2.58191,-2.52984 z m 30.07009,0.9356 -5.48321,5.41884 h 1.01225 l 4.51722,-4.50259 z m -29.06099,0.0735 -3.00402,2.94968 0.17666,0.87532 3.32637,-3.32598 z m 1.03174,0.96321 -3.60742,3.61643 0.2098,0.80246 3.96802,-3.95301 z m 1.1745,0.84659 -4.40065,4.45219 0.34453,0.64995 4.68564,-4.73397 z m 26.84688,0.1914 -3.44886,3.34415 h 1.01463 l 2.45082,-2.45675 z m -25.50826,0.52154 -5.09356,5.07288 0.35785,0.66283 5.40809,-5.38919 z m 1.41016,0.55256 -5.812,5.8647 0.42118,0.66061 6.14306,-6.22011 z m 1.62423,0.48853 -6.67623,6.68497 0.36209,0.59718 7.06045,-7.06501 z m 1.67989,0.31315 -7.49699,7.49172 0.40656,0.62663 7.98431,-8.02674 z m 20.78489,0.14162 -1.40103,1.32675 h 1.02603 l 0.38195,-0.50166 z m -19.13524,0.19974 -8.31466,8.32082 0.39324,0.63042 8.4236,-8.42281 z m 0.87348,1.18651 -8.25965,8.25936 0.47344,0.54963 8.26353,-8.2924 z m 0.94597,1.06378 -8.26578,8.23972 0.53203,0.51024 8.23711,-8.2466 z m 1.01146,1.01146 -8.20718,8.20053 0.47343,0.53679 8.23712,-8.23395 z m 1.01147,1.01147 -8.20048,8.20714 0.49913,0.5037 8.20471,-8.20748 z m 1.15251,0.91229 -8.23158,8.19207 0.50218,0.50218 8.18219,-8.17508 z m 1.01248,0.91478 -8.14676,8.21373 0.52539,0.49739 8.19681,-8.23276 z m 1.17956,0.93272 -8.16758,8.12728 0.45951,0.55828 8.2699,-8.25687 z m 1.10589,0.88148 -8.20911,8.20917 0.50776,0.49535 8.24397,-8.24696 z m 1.13276,0.88622 -8.25814,8.24578 0.54849,0.47454 8.18219,-8.17508 z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.638 7.584H.474l8.6-9.83L0 1.154h7.594l5.243 6.932ZM17.61 20.644h2.039L6.486 3.24H4.298Z"
></path>
</SVGIcon>

View file

@ -0,0 +1,15 @@
---
import SVGIcon from "../SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 24 24">
<path
d="M21.23 4.156a8.488 8.488 0 0 0-5.871-1.857c-3.766.243-6.324 2.662-7.364 6.481-1.28-1.224-1.892-3.238-2.093-5.54-1.02.215-1.658.702-2.233 1.237.445 2.316 1.802 4.015 3.264 5.158-2.559.317-5.99 2.442-6.771 4.904-.507 1.598.258 3.415 1.283 4.52 1.237 1.333 3.75 1.998 6.355 1.754.037.362-.104.536-.058.907 4.067-.306 7.174-1.646 10.04-3.894 1.119-.877 2.659-2.037 3.756-3.227 1.101-1.192 2.296-2.578 2.443-4.52.21-2.79-1.236-4.694-2.751-5.923zm-1.434 10.938c-1.035 1.001-2.241 1.797-3.351 2.675-1.249-1.987-2.583-3.984-3.887-5.917.017 2.63.006 5.432.04 7.957-.78.381-1.789.558-2.744.763-1.935-2.917-3.968-5.99-5.961-8.908.693-.447 1.627-.785 2.478-1.075 1.419 2.05 2.729 4.253 4.171 6.333.019-3.113-.009-6.673-.061-9.919a14.175 14.175 0 0 0 1.527-.434c1.813 2.721 3.553 5.628 5.464 8.359a547.35 547.35 0 0 1-.018-9.768c.858-.282 1.803-.535 2.669-.809.02 3.499-.338 7.128-.327 10.743z"
></path>
</SVGIcon>

View file

@ -0,0 +1 @@
export const SUBSCRIBESTAR_ENABLED: boolean = false;

4
src/data/tos.ts Normal file
View file

@ -0,0 +1,4 @@
import { parse as dateParse } from "date-fns";
export const TOS_COMMISSION_STATUS: "CLOSED" | "OPEN" | "SEMI_OPEN" | "PRIVATE" = "OPEN";
export const TOS_UPDATED_AT: Date = dateParse("2024-07-11", "yyyy-MM-dd", 0);

2
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,2 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View file

@ -0,0 +1,95 @@
---
import "../styles/base.css";
import "../styles/fonts.css";
import DarkModeScript from "../components/DarkModeScript.astro";
import AgeRestrictedModal from "../components/AgeRestrictedModal.astro";
import NavHeader from "../components/NavHeader.astro";
import IconSun from "../components/icons/IconSun.astro";
import IconMoon from "../components/icons/IconMoon.astro";
type Props = {
pageTitle?: string;
};
const { pageTitle } = Astro.props;
const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
---
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="manifest" href="/site.webmanifest" />
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#37b340" />
<meta name="msapplication-TileColor" content="#37b340" />
<meta name="theme-color" content="#7DD05A" data-react-helmet="true" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<link
rel="alternate"
type="application/rss+xml"
href={new URL("/feed.xml", Astro.site)}
title="Commission status"
/>
<link
rel="alternate"
type="application/rss+xml"
href="https://gallery.badmanners.xyz/feed.xml"
title="Gallery feed"
/>
<meta property="og:title" content={title} />
<slot name="head-description" />
<meta property="og:url" content={Astro.url} />
<meta content="/logo.webp" property="og:image" />
</head>
<body>
<div class="flex min-h-screen flex-col">
<div
id="bg"
class="fixed h-screen w-screen bg-radial from-bm-300 to-bm-500 dark:from-green-800 dark:to-green-950 print:hidden"
aria-hidden
>
</div>
<div class="relative my-auto flex flex-col items-center justify-center px-3 py-8 sm:py-12 md:px-12 print:p-0">
<main
class="w-full max-w-5xl flex-shrink-0 flex-grow-0 justify-center rounded-xl bg-stone-50 px-4 pb-6 pt-4 text-center text-base font-light tracking-tight text-stone-700 sm:px-8 sm:pb-8 sm:pt-6 sm:text-lg dark:bg-zinc-800 dark:text-zinc-50 print:bg-transparent print:p-0"
>
<NavHeader />
<slot />
</main>
<footer
class="sm:text-md flex flex-col items-center pt-9 text-sm font-normal tracking-wide text-black dark:text-white"
>
<div class="flex items-center">
<span id="copyright">&copy; 2023&ndash;{new Date().getFullYear()} Bad Manners</span>
<span class="print:hidden" aria-hidden>&nbsp;|&nbsp;</span>
<a
href="/licenses.txt"
target="_blank"
class="transition-colors hover:text-white hover:underline focus:text-white focus:underline motion-reduce:transition-none dark:hover:text-bm-300 dark:focus:text-bm-300 print:hidden"
>
Licenses
</a>
</div>
<span class="mt-1 print:hidden"
><button
data-dark-mode
aria-labelledby="label-toggle-dark-mode"
aria-hidden
class="m-1 hidden p-2 transition-colors hover:text-green-700 focus:text-green-700 motion-reduce:transition-none dark:hover:text-bm-300 dark:focus:text-bm-300"
>
<IconSun width="1.5rem" height="1.5rem" class="hidden dark:block" />
<IconMoon width="1.5rem" height="1.5rem" class="block dark:hidden" />
<span id="label-toggle-dark-mode" class="hidden">Toggle dark mode</span>
</button>&nbsp;</span
>
</footer>
</div>
</div>
<DarkModeScript />
<AgeRestrictedModal />
</body>
</html>

15
src/pages/404.astro Normal file
View file

@ -0,0 +1,15 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
---
<BaseLayout pageTitle="Not found">
<meta slot="head-description" property="og:description" content="Not found." />
<article aria-labelledby="title-not-found">
<h1 id="title-not-found" class="text-2xl sm:text-3xl">404 &ndash; Not found</h1>
<section>
<p class="mb-2 mt-5 sm:mb-3 sm:mt-6 sm:px-5 md:px-6">
The requested resource does not exist! Use the header above to navigate between pages.
</p>
</section>
</article>
</BaseLayout>

46
src/pages/about.astro Normal file
View file

@ -0,0 +1,46 @@
---
import { Image } from "astro:assets";
import BaseLayout from "../layouts/BaseLayout.astro";
import IconArrowUpRightFromSquare from "../components/icons/IconArrowUpRightFromSquare.astro";
import samStickerJuicebox from "../assets/images/sam_sticker_juicebox.webp";
---
<BaseLayout pageTitle="About me">
<meta slot="head-description" property="og:description" content="About Bad Manners." />
<article aria-labelledby="title-about-me">
<h1 id="title-about-me" class="text-2xl sm:text-3xl">About me</h1>
<section>
<p class="my-5 sm:my-6 sm:px-5 md:px-6">
You can call me <b>Bad Manners</b>, <b>Manners</b>, <b>BM</b>, <b>Bad</b>, <b>Bananas</b>... many choices to
pick from! My pronouns are "he/him". You can also call me <b>Sam</b> &ndash; which is my fursona's name. He is a
mimic and maned wolf hybrid, and you can learn more about him by clicking on him below!
</p>
<div class="mt-4">
<a
href="/sam_brendan"
class="group mx-auto mb-1 block w-max max-w-full pb-0 sm:mb-3"
aria-label="Learn more about Sam"
>
<Image
src={samStickerJuicebox}
alt="Sam Brendan with googly eyes and fangs showing from his lips, sipping from the straw of a banana juice box as some of the liquid escapes his mouth. Art by OliveCow."
class="max-w-full transition-transform group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100"
loading="lazy"
height={320}
/>
</a>
<span class="text-base">
A sticker of Sam by
<a
class="text-link transition-colors motion-reduce:transition-none"
href="https://olivecow.carrd.co/"
target="_blank"
>
<span class="underline">OliveCow</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>.
</span>
</div>
</section>
</article>
</BaseLayout>

171
src/pages/contact.astro Normal file
View file

@ -0,0 +1,171 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconBluesky from "../components/icons/brands/IconBluesky.astro";
import IconDiscord from "../components/icons/brands/IconDiscord.astro";
import IconMastodon from "../components/icons/brands/IconMastodon.astro";
import IconSignal from "../components/icons/brands/IconSignal.astro";
import IconTelegram from "../components/icons/brands/IconTelegram.astro";
import IconArrowUpRightFromSquare from "../components/icons/IconArrowUpRightFromSquare.astro";
import IconClone from "../components/icons/IconClone.astro";
import IconEnvelope from "../components/icons/IconEnvelope.astro";
---
<BaseLayout pageTitle="Contact">
<meta slot="head-description" property="og:description" content="Where to find me." />
<article aria-labelledby="title-contact">
<h1 id="title-contact" class="text-2xl sm:text-3xl">Contact</h1>
<section>
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6 sm:px-5 md:px-6">
Feel free to reach out to me through any of my socials below, or by
<a href="/work" class="text-link underline transition-colors motion-reduce:transition-none"
>messaging me on any of my galleries</a
>, if you wanna talk about anything!
</p>
<ul class="h-card flex flex-col items-center">
<li class="mb-2 w-max sm:mb-1">
<button
id="discord"
class="text-link mx-1 transition-colors motion-reduce:transition-none"
aria-label="Copy my Discord username to clipboard"
>
<IconDiscord width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline">badmanners</span>
<IconClone width="0.75rem" height="0.75rem" class="inline" />
</button>
</li>
<li class="h-card mb-2 w-max sm:mb-1">
<a
class="u-url text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://t.me/bad_manners"
target="_blank"
aria-label="Add me on Telegram"
rel="me"
>
<IconTelegram width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline">@bad_manners</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="h-card mb-2 w-max sm:mb-1">
<a
class="u-url text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://signal.me/#eu/ytt_rk0fFmAB2JAW-x2PbUiJyc_H3kYmfL_Pq4QNh5QIDsiFtjdFHaqFRs1D36tB"
target="_blank"
aria-label="Add me on Signal"
rel="me"
>
<IconSignal width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline">badmanners.10</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="h-card mb-2 w-max sm:mb-1">
<a
class="u-url text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://meow.social/@BadManners"
target="_blank"
aria-label="Visit me on Mastodon"
rel="me"
>
<IconMastodon width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline">@BadManners@meow.social</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="h-card mb-2 w-max sm:mb-1">
<a
class="u-url text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://bsky.app/profile/badmanners.xyz"
target="_blank"
aria-label="Visit me on Bluesky"
rel="me"
>
<IconBluesky width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline">@badmanners.xyz</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<button
id="email"
class="text-link mx-1 transition-colors motion-reduce:transition-none"
aria-label="Copy my e-mail address to clipboard"
>
<IconEnvelope width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="underline" data-user="em" data-website="zyx.srennamdab" aria-label="Obfuscated e-mail address"
></span>
<IconClone width="0.75rem" height="0.75rem" class="inline" />
</button>
</li>
</ul>
</section>
</article>
</BaseLayout>
<style>
#email span:before {
content: attr(data-website) "\0040" attr(data-user);
unicode-bidi: bidi-override;
direction: rtl;
}
</style>
<script>
import tippy from "tippy.js";
import "tippy.js/dist/tippy.css";
(function () {
const discordButton = document.querySelector<HTMLElementTagNameMap["button"]>("button#discord")!;
const discordTooltip = tippy(discordButton, {
content: "Username copied to clipboard!",
trigger: "manual",
theme: "bm",
});
const emailButton = document.querySelector<HTMLElementTagNameMap["button"]>("button#email")!;
const emailTooltip = tippy(emailButton, {
content: "E-mail address copied to clipboard!",
trigger: "manual",
theme: "bm",
});
let tooltipTimeout: any = null;
discordButton.addEventListener("click", (ev) => {
ev.preventDefault();
tooltipTimeout && clearTimeout(tooltipTimeout);
discordTooltip.hide();
emailTooltip.hide();
const text = discordButton.querySelector("span")!.innerText;
navigator.clipboard
.writeText(text)
.then(() => {
discordTooltip.show();
tooltipTimeout = setTimeout(() => {
discordTooltip.hide();
}, 2000);
})
.catch((e) => {
console.error("Unable to copy text to clipboard", e);
});
});
emailButton.addEventListener("click", (ev) => {
ev.preventDefault();
tooltipTimeout && clearTimeout(tooltipTimeout);
discordTooltip.hide();
emailTooltip.hide();
const emailData = emailButton.querySelector("span")!;
const text = `${emailData.dataset.user!.split("").reverse().join("")}@${emailData.dataset.website!.split("").reverse().join("")}`;
navigator.clipboard
.writeText(text)
.then(() => {
emailTooltip.show();
tooltipTimeout = setTimeout(() => {
emailTooltip.hide();
}, 2000);
})
.catch((e) => {
console.error("Unable to copy text to clipboard", e);
});
});
})();
</script>

27
src/pages/feed.xml.ts Normal file
View file

@ -0,0 +1,27 @@
import rss from "@astrojs/rss";
import type { APIRoute } from "astro";
import { set as dateSet, subMinutes } from "date-fns";
import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
export const GET: APIRoute = async ({ site }) => {
return rss({
title: "Bad Manners",
description: "Bad Manners status updates",
site: site!,
items: [
{
title: {
CLOSED: "Story commissions are closed.",
OPEN: "Story commissions are open!",
SEMI_OPEN: "Story commissions are semi-open, and I may accept them less frequently.",
PRIVATE: "Story commissions are private; they are only open to select commissioners.",
}[TOS_COMMISSION_STATUS],
link: "https://badmanners.xyz/terms_of_service",
pubDate: subMinutes(
dateSet(TOS_UPDATED_AT, { hours: 12, minutes: 0, seconds: 0 }),
TOS_UPDATED_AT.getTimezoneOffset(),
),
},
],
});
};

183
src/pages/index.astro Normal file
View file

@ -0,0 +1,183 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconBluesky from "../components/icons/brands/IconBluesky.astro";
import IconEkasPortal from "../components/icons/brands/IconEkasPortal.astro";
import IconFurAffinity from "../components/icons/brands/IconFurAffinity.astro";
import IconInkbunny from "../components/icons/brands/IconInkbunny.astro";
import IconMastodon from "../components/icons/brands/IconMastodon.astro";
import IconSoFurry from "../components/icons/brands/IconSoFurry.astro";
import IconTwitter from "../components/icons/brands/IconTwitter.astro";
import IconWeasyl from "../components/icons/brands/IconWeasyl.astro";
import IconSignal from "../components/icons/brands/IconSignal.astro";
import IconTelegram from "../components/icons/brands/IconTelegram.astro";
import IconKeybase from "../components/icons/brands/IconKeybase.astro";
import IconSubscribeStar from "../components/icons/brands/IconSubscribeStar.astro";
---
<BaseLayout>
<meta
slot="head-description"
property="og:description"
content="Safe vore enthusiast, mimic hybrid, and occasional writer."
/>
<article class="h-card" aria-labelledby="title-home">
<h1 id="title-home" class="pb-4 text-3xl tracking-tight sm:text-5xl">
Hi, I'm <span class="p-name">Bad Manners</span>!
</h1>
<section>
<img
loading="eager"
src="/logo.webp"
alt="Logo for Bad Manners"
class="u-logo mx-auto my-4 h-screen max-h-48 rounded-full transition-transform hover:scale-110 motion-reduce:transition-none motion-reduce:hover:scale-100 sm:max-h-72"
/>
<p class="p-note mt-6 sm:px-5 md:px-6">I'm a safe vore enthusiast, and a furry who occasionally writes stuff.</p>
<ul class="grid grid-cols-3 gap-4 px-4 pt-8 sm:grid-cols-4 sm:px-20 md:px-32">
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
href="https://www.furaffinity.net/user/BadManners"
target="_blank"
rel="me"
title="Fur Affinity"
>
<IconFurAffinity height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">BadManners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
href="https://aryion.com/g4/user/BadManners"
target="_blank"
rel="me"
title="Eka's Portal"
>
<IconEkasPortal height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">BadManners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
href="https://inkbunny.net/BadManners"
target="_blank"
rel="me"
title="Inkbunny"
>
<IconInkbunny height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">BadManners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
href="https://bad-manners.sofurry.com/"
target="_blank"
rel="me"
title="SoFurry"
>
<IconSoFurry height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">Bad Manners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
href="https://www.weasyl.com/~badmanners"
target="_blank"
rel="me"
title="Weasyl"
>
<IconWeasyl height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">BadManners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://meow.social/@BadManners"
target="_blank"
title="Mastodon"
>
<IconMastodon height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">@BadManners@meow.social</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://bsky.app/profile/badmanners.xyz"
target="_blank"
title="Bluesky"
>
<IconBluesky height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">@badmanners.xyz</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://x.com/BadManners__"
target="_blank"
title="X"
>
<IconTwitter height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">@BadManners__</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://t.me/bad_manners"
target="_blank"
title="Telegram"
>
<IconTelegram height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">@bad_manners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://keybase.io/badmanners"
target="_blank"
title="Keybase"
>
<IconKeybase height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">badmanners</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://signal.me/#eu/ytt_rk0fFmAB2JAW-x2PbUiJyc_H3kYmfL_Pq4QNh5QIDsiFtjdFHaqFRs1D36tB"
target="_blank"
title="Signal"
>
<IconSignal height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">badmanners.10</span>
</a>
</li>
<li>
<a
class="u-url text-link transition-colors motion-reduce:transition-none"
rel="me"
href="https://subscribestar.adult/bad-manners"
target="_blank"
title="SubscribeStar"
>
<IconSubscribeStar height="1.75rem" width="1.75rem" class="inline" />
<span class="p-nickname hidden">bad-manners</span>
</a>
</li>
</ul>
</section>
</article>
</BaseLayout>

173
src/pages/sam_brendan.astro Normal file
View file

@ -0,0 +1,173 @@
---
import { Image } from "astro:assets";
import BaseLayout from "../layouts/BaseLayout.astro";
import IconArrowUpRightFromSquare from "../components/icons/IconArrowUpRightFromSquare.astro";
import samRefsheet from "../assets/images/sam_refsheet.webp";
import samAllStickers from "../assets/images/sam_all_stickers.webp";
---
<BaseLayout pageTitle="Sam Brendan">
<meta slot="head-description" property="og:description" content="Mimic x maned wolf hybrid." />
<article aria-labelledby="title-sam-brendan">
<h1 id="title-sam-brendan" class="text-2xl sm:text-3xl">Sam Brendan</h1>
<section>
<p class="my-4 text-justify sm:my-6 sm:px-5 md:px-12">
<b>Sam Brendan</b> (or simply <b>Sam</b>) is a mimic x maned wolf hybrid. The main color of his fur is lime
green, with light teal and white details. His most noticeable feature, however, is the metal briefcase that he
has in lieu of a face &ndash; the mimic portion of his body.
</p>
</section>
<section aria-labelledby="title-physical-description">
<h2 id="title-physical-description" class="my-4 text-lg sm:my-6 sm:text-2xl">Physical description</h2>
<div class="my-4">
<a
class="group mx-auto mb-1 block w-max max-w-full pb-0 sm:mb-3"
href="https://booru.badmanners.xyz/index.php?q=post/view/1"
target="_blank"
>
<Image
src={samRefsheet}
alt="A reference sheet of Sam Brendan, a mimic x maned wolf hybrid. Art by Rimmi."
class="max-w-full transition-transform group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100"
loading="lazy"
height={512}
/>
</a>
<span class="text-base">
A reference sheet of Sam Brendan, by
<a class="text-link transition-colors" href="https://linktr.ee/Rimmi1357" target="_blank">
<span class="underline">Rimmi</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" /></a
>. Click to view in higher quality.
</span>
</div>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
He has big, pointy, forward-facing ears, with big tufts in a light teal-to-white gradient coming out from the
inside. The rest of his head is different from a regular maned wolf's, however. Everything from the top of his
forehead down to the front of his neck is replaced by a metal briefcase ₋ including his eyes and snout &ndash;
due to partially being a mimic of the object. More specifically, the briefcase has a gray matte texture, with
its hinges at the bottom. On top, there are two latches by the sides, and a dark gray pull handle in the middle
&ndash; all three clamp down towards the front. There is no discernible unlocking mechanism for the latches. On
the flat sides, the briefcase has several thin ridges across the width.
</p>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
His mimic half can split in half longitudinally to reveal his green maw, with irregular and crooked white fangs,
muscular buccinators on the sides, and a long prehensile and sticky tongue that is pastel yellow at the tip. It
is connected to his lime-green digestive system.
</p>
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
Sam's body is slim, and his claws are short and white. His extremities are also white (instead of dark like most
maned wolves). His upper paws are similar to hands, his digitigrade feet have lime green pads, and his fluffy
tail is medium in size. These limbs connect to the lime green on his torso with a light teal gradient. The
center of his torso is white, with more light teal gradients on the sides of the ribs. He has some excess
fluffiness throughout his whole body, including a tuft above his chest. On his shoulders, the back of his head,
and the inner side of each thigh, he has small white markings of three triangles of differing sizes. He features
a similar but larger pattern on his back, containing five white triangles.
</p>
</section>
<section aria-labelledby="title-personality">
<h2 id="title-personality" class="my-4 text-lg sm:my-6 sm:text-2xl">Personality</h2>
<div class="my-4">
<a
class="group mx-auto mb-1 block w-max max-w-full pb-0 sm:mb-3"
href="https://booru.badmanners.xyz/index.php?q=post/view/2"
target="_blank"
>
<Image
src={samAllStickers}
alt="A set with ten stickers of Sam Brendan displaying different actions and emotions. Art by OliveCow."
class="max-w-full transition-transform group-hover:scale-105 motion-reduce:transition-none motion-reduce:group-hover:scale-100"
loading="lazy"
height={512}
/>
</a>
<span class="text-base">
A set of stickers featuring Sam Brendan, by
<a class="text-link transition-colors" href="https://olivecow.carrd.co/" target="_blank">
<span class="underline">OliveCow</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" /></a
>. Click to view in higher quality.
</span>
</div>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
Sam is normally solitary and anxious but friendly and extremely curious, with a particular interest in sciences.
Sometimes introverted and shy, sometimes loud and active, he's quite prone to mood swings, although he tries to
be reasonable and act on good intentions and manners whenever possible. He is quite fond of anagrams and
wordplay. His main weaknesses are ear scritches and constantly focusing on others' approval. His main strength
is finding humor in little things.
</p>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
Due to his inability to display facial expressions, he usually resorts to stereotypical conveyances of emotions
by sticking adhesive accessories on the briefcase. For this purpose, he can make use of googly eyes (his
favorite accessories), removable paint and powder, stickers (e.g. to simulate smiles or expressions), and more.
He dislikes being nicknamed Briefcase or anything along those lines but is fine being called by either of his
species. It's very rare for him to ever open his case.
</p>
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
He's quite self-conscious about his mimic maw and normally avoids showing it in public. These factors lead to
him usually eating alone, as well as being confused and flustered by people who seem keenly interested in his
maw. However, in specific moods (such as vorish ones), he has no qualms about displaying and using his maw. He
can be either predator or prey, depending on the situation. When prey, his predator may notice how his fur has a
citric and sweet taste and perfume, similar to lime pie.
</p>
</section>
<section aria-labelledby="title-abilities">
<h2 id="title-abilities" class="my-4 text-lg sm:my-6 sm:text-2xl">Abilities</h2>
<ul class="mb-5 text-justify sm:mb-4 sm:px-5 md:px-12">
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
Mimicking voices or simple sounds, and speak without moving his mouth.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
Materializing adhesive facial accessories out of thin air into his paws.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
Controlling the movable parts of his mimic half (hinges, latches, handle), like any other limb. Even when
unconscious, he's able to keep his case tightly shut.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
Opening his face like a normal briefcase would open (only when his maw is closed), revealing a single
compartment that exists extra-spatially &ndash; i.e. in a pocket dimension separate from the rest of his maw.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
Making his maned wolf body temporarily disappear, becoming indistinguishable from a briefcase mimic except for
a patch of green and white fur on the backside.
</li>
</ul>
</section>
<section aria-labelledby="title-sensorial-report">
<h2 id="title-sensorial-report" class="my-4 text-lg sm:my-6 sm:text-2xl">Sensorial report</h2>
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
His mimic parts (henceforth referred to as 'face') are mostly organic, but still contain traces of metallic
elements, making non-invasive examinations such as X-ray and MRI difficult. However, stimulation through short
electrical impulses at the junction between his head and his face results in messages reaching the brain,
indicating that there is a rich nervous network through the seam. The following data has been collected based on
empirical observations and Sam's self-reporting.
</p>
<ul class="mb-5 mt-3 text-justify sm:mb-4 sm:mt-2 sm:px-5 md:px-12">
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
<b>Sense of sight:</b> Mimics' skins are photosensitive, and Sam's face similarly has almost 360° of vision, with
minor loss of focus outside the center. This ability is restricted by perceiving the world as black and white,
therefore minimizing sensory overload, but he's able to focus on a single object to perceive trichromatic hues.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
<b>Sense of hearing:</b> Excellent hearing, unaffected by the obstruction from his face. Normal sense of balance.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
<b>Sense of touch:</b> He's able to perceive pressure on his face, albeit with considerably diminished capacity
than the rest of his body. The region also has even dimmer senses of temperature and pain. But being of magical
origin, it is sensitive to such energies.
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
<b>Sense of taste:</b> With the maw that appears in front of his face, Sam is capable of tasting and consuming
anything. He reports that "certain flavors are extremely vivid" (further clarification has been requested to amend
this report).
</li>
<li class="mx-7 mb-1 list-disc indent-0 sm:mx-10">
<b>Sense of smell:</b> Capable of discerning strong smells with chemoreceptors in his face, while normally breathing
through air diffusion instead of his lungs. Sam reports that most smells are quite weak, but those pertaining to
living beings are particularly pronounced.
</li>
</ul>
</section>
</article>
</BaseLayout>

View file

@ -0,0 +1,136 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconSquareRSS from "../components/icons/IconSquareRSS.astro";
import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
---
<BaseLayout pageTitle="Terms of Service">
<meta slot="head-description" property="og:description" content="My Terms of Service for story commissions." />
<article aria-labelledby="title-tos">
<h1 id="title-tos" class="mb-4 text-2xl sm:mb-5 sm:text-3xl">Terms of Service for story commissions</h1>
<section aria-label="Commission status">
<div class="my-2 flex items-center justify-end">
<p class="mr-1 text-sm font-normal italic">
Last update: {TOS_UPDATED_AT.toLocaleDateString("en-US", { dateStyle: "long" })}
</p>
<a
class="text-link transition-colors motion-reduce:transition-none"
href="/feed.xml"
rel="alternate"
type="application/rss+xml"
target="_blank"
title="RSS feed"><IconSquareRSS width="2rem" height="2rem" class="p-1" /></a
>
</div>
{
TOS_COMMISSION_STATUS == "CLOSED" ? (
<p class="mb-4 text-2xl font-normal uppercase sm:mb-8 sm:px-5 md:px-6">
Commissions are
<span class="text-red-600 dark:text-red-500">closed</span>.
</p>
) : TOS_COMMISSION_STATUS == "OPEN" ? (
<p class="mb-4 text-2xl font-normal uppercase sm:mb-8 sm:px-5 md:px-6">
Commissions are <span class="text-bm-500 dark:text-bm-400">open</span>.
</p>
) : TOS_COMMISSION_STATUS == "SEMI_OPEN" ? (
<p class="mb-4 text-2xl font-normal sm:mb-8 sm:px-5 md:px-6">
<span class="uppercase">
Commissions are <span class="text-orange-600 dark:text-orange-500">semi-open</span>
</span>
. I'll be more picky about which commissions to take.
</p>
) : TOS_COMMISSION_STATUS == "PRIVATE" ? (
<p class="mb-4 text-2xl font-normal sm:mb-8 sm:px-5 md:px-6">
<span class="uppercase">
Commissions are <span class="text-red-600 dark:text-red-500">private</span>
</span>
. I'm only taking commissions from select clients.
</p>
) : null
}
</section>
<section>
<p class="mb-2 text-justify indent-6 sm:px-5 sm:indent-12 md:px-6">
To stay flexible with both the word count and the deadline, I'm taking payments <em>after</em> the commission is
done. My rate is <b>US$&nbsp;1.50 per 100 words</b>, and the final price is only set when the story is finished<a
class="hover:underline"
href="#note-1"
aria-label="See note 1"><sup>[1]</sup></a
>. The word count will be rounded to the nearest hundredth for pricing purposes, and the finished story will be
published in my public galleries.
</p>
<p class="mb-2 text-justify indent-6 sm:px-5 sm:indent-12 md:px-6">
We can discuss your budget, taking into consideration how big I expect the project to turn out and how much
you're willing to pay. If the finished story ends up under the limit, then the actual price will be lowered to
match. But if I go over the budget instead, you're under no obligation to pay for anything other than what was
originally agreed.
</p>
<p class="mb-3 text-justify indent-6 sm:px-5 sm:indent-12 md:px-6">
By asking me for a commission, you acknowledge: a)&nbsp;that you are 18+ years old; b)&nbsp;that you are readily
committed to paying me right after the commission is done, and; c)&nbsp;that you agree to these terms.
</p>
<p id="note-1" class="mb-6 text-justify text-sm font-normal sm:px-5 md:px-6">
[1] For first-time commissioners requesting a budget of 8,000+ words, a deposit equivalent to the first 4,000
words <em>may</em> be requested. In such a case, the final payment will have that amount discounted.
</p>
</section>
<section aria-labelledby="title-commission-subjects">
<h2 id="title-commission-subjects" class="my-4 text-lg sm:my-6 sm:text-2xl">Commission subjects</h2>
<ul class="mb-8 text-justify text-base sm:px-3 sm:text-lg md:px-6">
<li class="mx-6 mb-1 list-disc sm:mx-8">
I can work with any kind of safe vore, as long as there's no digestion, fatality, reformation, absorption, or
disposal &ndash; in other words, anything considered "endosoma" is fine. As for characters, I can write any
ferals or anthros, of any gender or sexuality.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
I won't write real-life characters, or extreme content like non-consensual sex, underage characters in
sexual/fetishistic/suggestive situations, bestiality (sex with non-sapient ferals), scat, gore, or snuff.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
Despite my focus being on vore, other subjects and scenarios are generally fine to work with. I don't mind
working outside my comfort zone, save for the exceptions above &ndash; but reach out to discuss any subjects
beforehand.
</li>
</ul>
</section>
<section aria-labelledby="title-additional-information">
<h2 id="title-additional-information" class="my-4 text-lg sm:my-6 sm:text-2xl">Additional information</h2>
<ul class="mb-6 text-justify text-base sm:px-3 sm:text-lg md:px-6">
<li class="mx-6 mb-1 list-disc sm:mx-8">
These prices and terms, including my availability for commission work, are subject to change in the future.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
I reserve the right to refuse commission requests, before payment, for any reason.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
If you need any modifications to the concluded draft, let me know before I send you an invoice. Any minor
additions will be included in the final word count, according to the previously agreed budget. Major changes
or additions may incur extra fees.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
Currently, I can receive payments through PayPal or Pix (for Brazilian customers, after the appropriate price
conversion to BRL). For longer commissions, payment in multiple parts can be arranged as long as you request
it in advance.
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
You are allowed to distribute the final version of the story for free with proper attribution. But you are NOT
allowed to claim that you wrote it, sell it, monetize its distribution, use it for plagiarism, or modify it in
any form.
<ul>
<li class="ml-6 list-disc sm:ml-8">
As for any unfinished WIPs or unpaid commissions, on top of the restrictions above, you are NOT allowed to
save them, distribute them, or use them as a reference for someone else's work.
</li>
</ul>
</li>
<li class="mx-6 mb-1 list-disc sm:mx-8">
If you have any doubts, or are considering getting a commission from me,
<a href="/contact" class="text-link underline transition-colors motion-reduce:transition-none"
>please reach out</a
>!
</li>
</ul>
</section>
</article>
</BaseLayout>

236
src/pages/work.astro Normal file
View file

@ -0,0 +1,236 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconEkasPortal from "../components/icons/brands/IconEkasPortal.astro";
import IconFurAffinity from "../components/icons/brands/IconFurAffinity.astro";
import IconInkbunny from "../components/icons/brands/IconInkbunny.astro";
import IconSoFurry from "../components/icons/brands/IconSoFurry.astro";
import IconSubscribeStar from "../components/icons/brands/IconSubscribeStar.astro";
import IconWeasyl from "../components/icons/brands/IconWeasyl.astro";
import IconArrowUpRightFromSquare from "../components/icons/IconArrowUpRightFromSquare.astro";
import IconBriefcase from "../components/icons/IconBriefcase.astro";
import { TOS_COMMISSION_STATUS } from "../data/tos";
import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
---
<BaseLayout pageTitle="My work">
<meta slot="head-description" property="og:description" content="The things I've made." />
<article aria-labelledby="title-my-work" class="sm:px-5 md:px-6">
<h1 id="title-my-work" class="text-2xl sm:text-3xl">My work</h1>
<section>
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
I've been a lurker in the furry vore community for a long time before I decided to start writing
<a
class="text-link transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/stories/1"
target="_blank"
>
<span class="underline">safe vore stories</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>. Since there's a lot in furry endosoma that I enjoy, my stories tend to
<a
class="text-link underline transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/tags"
target="_blank"
>
<span class="underline">have all sorts of themes and scenarios</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>, including exotic ones. My content is all tagged with the appropriate content warnings, so if you enjoy
safe vore, chances are that you'll find something in my gallery that is right up your alley.
</p>
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
I've also made a game called
<a
class="text-link underline transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/games/crossing-over"
target="_blank"
>
<span>"Crossing Over"</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" /></a
>, as part of Strawberry Jam 8 (a game jam hosted by eevee on Feb. 2024).
</p>
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
You can find my gallery on the following websites, which include all of my content. Aside from the first link,
my other galleries also include some of the art that I've gotten from others (commissions, gifts, etc.).
</p>
<ul class="flex flex-col items-center">
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://gallery.badmanners.xyz/"
target="_blank"
>
<IconBriefcase height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">gallery.badmanners.xyz</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://www.furaffinity.net/user/BadManners"
target="_blank"
rel="me"
>
<IconFurAffinity height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">Fur Affinity</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://aryion.com/g4/user/BadManners"
target="_blank"
rel="me"
>
<IconEkasPortal height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">Eka's Portal</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://inkbunny.net/BadManners"
target="_blank"
rel="me"
>
<IconInkbunny height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">Inkbunny</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://bad-manners.sofurry.com/"
target="_blank"
rel="me"
>
<IconSoFurry height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">SoFurry</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
<li class="mb-2 w-max sm:mb-1">
<a
class="text-link mx-1 transition-colors motion-reduce:transition-none"
href="https://www.weasyl.com/~badmanners"
target="_blank"
rel="me"
>
<IconWeasyl height="1.75rem" width="1.75rem" class="mr-1 inline" />
<span class="underline">Weasyl</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
</li>
</ul>
</section>
<section aria-labelledby="title-story-commissions">
<h2 id="title-story-commissions" class="my-4 text-lg sm:my-6 sm:text-2xl">Story commissions</h2>
{
TOS_COMMISSION_STATUS == "CLOSED" ? (
<p class="mb-6 mt-2 sm:mt-3">
My story commissions are currently closed, but regardless, feel free to
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
</a>
if interested.
</p>
) : TOS_COMMISSION_STATUS == "OPEN" ? (
<p class="mb-6 mt-2 sm:mt-3">
My story commissions are currently open! Feel free to
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
</a>
if interested.
</p>
) : TOS_COMMISSION_STATUS == "SEMI_OPEN" ? (
<p class="mb-6 mt-2 sm:mt-3">
My story commissions are currently semi-open, meaning that I may take some commissions, but might reject or
postpone them more easily. Feel free to
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
</a>
if interested.
</p>
) : TOS_COMMISSION_STATUS == "PRIVATE" ? (
<p class="mb-6 mt-2 sm:mt-3">
My story commissions are currently private, meaning that I'm not offering slots publicly, but I may still
work on commissions for specific clients. Regardless of whether this applies to you or not, feel free to
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
</a>
if interested.
</p>
) : (
<p class="mb-6 mt-2 sm:mt-3">
Feel free to
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
</a>
if interested.
</p>
)
}
</section>
<section aria-labelledby="title-paid-subscriptions">
<h2 id="title-paid-subscriptions" class="my-4 text-lg sm:my-6 sm:text-2xl">Paid subscriptions and tips</h2>
{
SUBSCRIBESTAR_ENABLED ? (
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
For continued support, I'm using SubscribeStar as a patronage subscription service! Although I intend to
publish all of my stories publicly, I'm exploring options on how to reward people who are able to
financially support my work with recurring payments.
</p>
) : (
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
In the past, I've used SubscribeStar as a subscription service. Currently, this option is disabled, although
my profile page is still accessible through the link below.
</p>
)
}
<a
class="text-link mx-auto transition-colors motion-reduce:transition-none"
href="https://subscribestar.adult/bad-manners"
target="_blank"
>
<IconSubscribeStar width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="underline">Check out my SubscribeStar!</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>
{
SUBSCRIBESTAR_ENABLED ? (
<p class="mt-3 sm:mt-4">
If you'd like to tip me once, you can still do it through SubscribeStar! Simply subscribe through the link
above, then immediately cancel the renewal. That way, you'll even get subscriber benefits for that month!
</p>
) : null
}
</section>
<section aria-labelledby="title-other-work">
<h2 id="title-other-work" class="my-4 text-lg sm:my-6 sm:text-2xl">Other work</h2>
<p class="mb-6 mt-3 sm:mb-8 sm:mt-4">
Other than the above, I've done some miscellaneous work. This includes
<a
href="https://git.badmanners.xyz/badmanners?tab=repositories"
target="_blank"
class="text-link transition-colors motion-reduce:transition-none"
>
<span class="underline">writing code (such as both of my websites)</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="mb-1 inline" />
</a>, and localizing
<a
href="https://desiran.itch.io/wrangler"
target="_blank"
class="text-link transition-colors motion-reduce:transition-none"
>
<span class="underline">Wrangler</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="mb-1 inline" />
</a>
to Brazilian Portuguese.
</p>
</section>
</article>
</BaseLayout>

30
src/styles/base.css Normal file
View file

@ -0,0 +1,30 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Tippy tooltips */
.tippy-box[data-theme~="bm"] {
@apply bg-stone-800 font-sans text-sm text-stone-50 dark:bg-zinc-900 dark:text-zinc-100;
}
.tippy-box[data-theme~="bm"][data-placement^="top"] > .tippy-arrow::before {
@apply border-t-stone-800 dark:border-t-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="bottom"] > .tippy-arrow::before {
@apply border-b-stone-800 dark:border-b-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="left"] > .tippy-arrow::before {
@apply border-l-stone-800 dark:border-l-zinc-900;
}
.tippy-box[data-theme~="bm"][data-placement^="right"] > .tippy-arrow::before {
@apply border-r-stone-800 dark:border-r-zinc-900;
}
@layer components {
.text-link {
@apply text-stone-800 hover:text-bm-500 focus:text-bm-500 dark:text-zinc-300 dark:hover:text-bm-400 dark:focus:text-bm-400;
}
.button-close {
@apply rounded-full p-[6px] text-stone-800 hover:bg-stone-300 hover:text-stone-800 focus:bg-stone-300 focus:text-stone-800 sm:p-2 dark:text-zinc-300 dark:hover:bg-zinc-500 dark:hover:text-zinc-800 dark:focus:bg-zinc-500 dark:focus:text-zinc-800;
}
}

58
src/styles/fonts.css Normal file
View file

@ -0,0 +1,58 @@
/* jost-latin-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: normal;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-latin-wght-normal.woff2) format("woff2-variations");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* jost-latin-ext-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: normal;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-latin-ext-wght-normal.woff2) format("woff2-variations");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* jost-cyrillic-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: normal;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-cyrillic-wght-normal.woff2) format("woff2-variations");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* jost-latin-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: italic;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-latin-wght-italic.woff2) format("woff2-variations");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
/* jost-latin-ext-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: italic;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-latin-ext-wght-italic.woff2) format("woff2-variations");
unicode-range: U+0100-02AF, U+0304, U+0308, U+0329, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113,
U+2C60-2C7F, U+A720-A7FF;
}
/* jost-cyrillic-wght-normal */
@font-face {
font-family: "Jost Variable";
font-style: italic;
font-display: block;
font-weight: 100 900;
src: url(/fonts/jost-cyrillic-wght-italic.woff2) format("woff2-variations");
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

26
tailwind.config.mjs Normal file
View file

@ -0,0 +1,26 @@
import defaultTheme from "tailwindcss/defaultTheme";
/** @type {import('tailwindcss').Config} */
export default {
darkMode: ["variant", "@media not print { .dark & }"],
content: ["./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}"],
theme: {
extend: {
fontFamily: {
sans: ['"Jost Variable"', ...defaultTheme.fontFamily.sans],
},
colors: {
bm: {
300: "rgb(153 230 95 / <alpha-value>)",
400: "rgb(125 208 90 / <alpha-value>)",
500: "rgb(72 168 79 / <alpha-value>)",
600: "rgb(57 142 72 / <alpha-value>)",
},
},
backgroundImage: {
radial: "radial-gradient(var(--tw-gradient-stops))",
},
},
},
plugins: [],
};

4
tsconfig.json Normal file
View file

@ -0,0 +1,4 @@
{
"extends": "astro/tsconfigs/strict",
"exclude": ["dist"]
}