Consolidate socials on index and add View Transitions API support

This commit is contained in:
Bad Manners 2024-08-20 00:16:28 -03:00
parent 580cd2da26
commit 483f406037
23 changed files with 493 additions and 458 deletions

View file

@ -1,3 +1,4 @@
src/components/AutoDarkMode.astro
src/components/AgeRestrictedScriptInline.astro
src/components/DarkModeScriptInline.astro
.astro/
dist/

View file

@ -30,8 +30,9 @@ npm run prettier # Prettier formatting
The following optional environment variable can be set within a `.env` file:
| Name | Type | Description |
| --------------- | ------- | ------------------------------------------------------------------------------------------------ |
| ------------------------- | ------- | ------------------------------------------------------------------------------------------------ |
| `APACHE_CONFIG` | boolean | If set to true, generates an `.htaccess` Apache config file at the root of the output directory. |
| `ENABLE_VIEW_TRANSITIONS` | boolean | If set to true, enables Astro's View Transitions API (i.e. Single-Page App mode). |
### Build and deploy to remote

View file

@ -15,11 +15,13 @@ export default defineConfig({
outDir: "./dist",
redirects: {
"/tos": "/terms_of_service",
"/contact": "/",
},
experimental: {
env: {
schema: {
APACHE_CONFIG: envField.boolean({ context: "server", access: "public", default: false }),
ENABLE_VIEW_TRANSITIONS: envField.boolean({ context: "client", access: "public", default: false }),
},
},
},

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "badmanners.xyz",
"version": "2.0.4",
"version": "2.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "badmanners.xyz",
"version": "2.0.4",
"version": "2.1.0",
"hasInstallScript": true,
"dependencies": {
"@astrojs/check": "^0.9.2",

View file

@ -1,7 +1,7 @@
{
"name": "badmanners.xyz",
"type": "module",
"version": "2.0.4",
"version": "2.1.0",
"scripts": {
"postinstall": "astro sync",
"dev": "astro dev",

View file

@ -8,7 +8,7 @@ The Jost* typeface is copyrighted by indestructible type* and is distributed und
The SVG icons for Bluesky, Codeberg, Discord, Itch.io, Keybase, GitHub, GitLab, Mastodon, Picarto, Signal, Steam, Telegram, Twitch, Weasyl, and X 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, Itaku, Neocities, SoFurry, and SubscribeStar were created by me (Bad Manners) from their respective logos. They are available for free use under the Creative Commons Zero v1.0 Universal license: https://creativecommons.org/publicdomain/zero/1.0/
The SVG icons for Cohost, Eka's Portal, Fur Affinity, Inkbunny, Itaku, Neocities, SoFurry, and SubscribeStar were edited by me (Bad Manners) from their respective logos, for personal use.
The generic SVG icons were created by Font Awesome and are distributed under CC-BY-4.0: https://creativecommons.org/licenses/by/4.0/

View file

@ -1,32 +1,37 @@
---
import AgeRestrictedScriptInline from "./AgeRestrictedScriptInline.astro";
import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
---
<template id="template-modal-age-restricted">
<div
style={{ display: "none" }}
id="modal-age-restricted"
class="fixed inset-0 bg-stone-50 dark:bg-zinc-900"
tabindex="-1"
role="dialog"
aria-hidden="false"
aria-labelledby="title-age-restricted"
>
<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-zinc-50">
<div
id="title-age-restricted"
class="pb-3 pt-2 text-3xl font-normal text-stone-700 sm:pb-4 sm:pt-2 dark:text-zinc-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-zinc-300 dark:text-zinc-50"
class="mx-6 mb-4 max-w-xl border-b border-stone-300 pb-4 text-xl font-medium text-stone-700 dark:border-zinc-300 dark:text-zinc-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-zinc-50">
<p class="px-8 text-lg font-normal leading-snug text-stone-700 sm:max-w-2xl dark:text-zinc-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">
<div
class="flex w-full max-w-md flex-col-reverse justify-evenly gap-y-5 px-6 pt-5 font-medium sm:max-w-2xl sm:flex-row"
>
<button
data-modal-reject
id="age-verification-reject"
@ -44,39 +49,39 @@ import IconTriangleExclamation from "./icons/IconTriangleExclamation.astro";
</div>
</div>
</div>
</template>
<AgeRestrictedScriptInline />
<script>
(function () {
if (localStorage.getItem("ageVerified") !== "true") {
const fragment = document
.querySelector<HTMLElementTagNameMap["template"]>("template#template-modal-age-restricted")!
.content.cloneNode(true) as DocumentFragment;
const modal = fragment.firstElementChild as HTMLElementTagNameMap["div"];
const controller = new AbortController();
const { signal } = controller;
modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-reject]")!.addEventListener(
"click",
(e: MouseEvent) => {
import { ENABLE_VIEW_TRANSITIONS } from "astro:env/client";
const ageRestrictedModalSetup = () => {
if (localStorage.ageVerified !== "true") {
const modal = document.querySelector<HTMLElementTagNameMap["div"]>("div#modal-age-restricted")!;
const rejectButton = modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-reject]")!;
const onRejectButtonClick = (e: MouseEvent) => {
e.preventDefault();
location.href = "about:blank";
},
{ signal },
);
};
rejectButton.addEventListener("click", onRejectButtonClick);
modal.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-accept]")!.addEventListener(
"click",
(e: MouseEvent) => {
e.preventDefault();
controller.abort();
localStorage.setItem("ageVerified", "true");
rejectButton.removeEventListener("click", onRejectButtonClick);
localStorage.ageVerified = "true";
document.body.style.overflow = "auto";
modal.remove();
document.querySelectorAll("body > :not(#modal-age-restricted)").forEach((el) => el.removeAttribute("inert"));
modal.style.display = "none";
},
{ signal },
{ once: true },
);
document.body.style.overflow = "hidden";
document.body.appendChild(fragment);
document.querySelector<HTMLElementTagNameMap["button"]>("button[data-modal-reject]")?.focus();
rejectButton.focus();
}
};
if (ENABLE_VIEW_TRANSITIONS) {
document.addEventListener("astro:page-load", ageRestrictedModalSetup);
} else {
ageRestrictedModalSetup();
}
})();
</script>

View file

@ -0,0 +1,4 @@
---
---
<script is:inline>function a(){let b=document,c="modal-age-restricted";localStorage.ageVerified!=="true"&&((b.body.style.overflow="hidden"),b.querySelectorAll("body > :not(#"+c+")").forEach(e=>e.setAttribute("inert","true")),(b.getElementById(c).style.display="block"));}document.addEventListener("astro:after-swap",a);a()</script>

View file

@ -1,4 +0,0 @@
---
---
<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>

View file

@ -1,13 +1,15 @@
---
import DarkModeInline from "./AutoDarkMode.astro";
import DarkModeScriptInline from "./DarkModeScriptInline.astro";
---
<DarkModeInline />
<DarkModeScriptInline />
<script>
(function () {
let colorScheme = localStorage.getItem("colorScheme");
if (colorScheme == null || colorScheme === "auto") {
import { ENABLE_VIEW_TRANSITIONS } from "astro:env/client";
const colorSchemeSetup = () => {
let colorScheme = localStorage.colorScheme;
if (!colorScheme || colorScheme === "auto") {
colorScheme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
document.querySelectorAll<HTMLElementTagNameMap["button"]>("button[data-dark-mode]").forEach((button) => {
@ -23,8 +25,13 @@ import DarkModeInline from "./AutoDarkMode.astro";
colorScheme = "dark";
document.body.classList.add("dark");
}
localStorage.setItem("colorScheme", colorScheme);
localStorage.colorScheme = colorScheme;
});
});
})();
};
if (ENABLE_VIEW_TRANSITIONS) {
document.addEventListener("astro:page-load", colorSchemeSetup);
} else {
colorSchemeSetup();
}
</script>

View file

@ -0,0 +1,4 @@
---
---
<script is:inline>function a(){var b="dark",c="auto",d="colorScheme",e=document.body.classList,f=localStorage,g=f[d];g&&g!==c?g===b&&e.add(b):(f[d]=c,matchMedia("(prefers-color-scheme: dark)").matches&&e.add(b))};document.addEventListener("astro:after-swap",a);a()</script>

View file

@ -8,7 +8,6 @@ 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;

View file

@ -1,15 +0,0 @@
---
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

@ -1,15 +0,0 @@
---
import SVGIcon from "./SVGIcon.astro";
type Props = {
width: string;
height: string;
class?: string;
};
---
<SVGIcon {...Astro.props} viewBox="0 0 448 512">
<path
d="M8 256a56 56 0 1 1 112 0A56 56 0 1 1 8 256zm160 0a56 56 0 1 1 112 0 56 56 0 1 1 -112 0zm216-56a56 56 0 1 1 0 112 56 56 0 1 1 0-112z"
></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 40.820312,8.9199219 C 36.257148,8.8605533 31.440597,9.5093383 26.373047,10.867188 20.554621,12.426215 15.490193,14.614059 11.177734,17.429688 6.8652759,20.245279 3.7316853,23.583136 1.7773438,27.441406 -0.17700239,31.299712 -0.52034379,35.598908 0.75,40.339844 c 1.263096,4.714066 3.7087126,8.259066 7.3339844,10.636718 3.6252706,2.377654 8.0124156,3.722128 13.1621096,4.03125 5.149729,0.309125 10.632671,-0.316018 16.451172,-1.875 5.238854,-1.403695 9.865205,-3.334276 13.876953,-5.791015 -0.477048,0.03214 -0.964536,0.04687 -1.462891,0.04687 -2.118726,0 -4.038277,-0.285908 -5.759766,-0.859375 -1.721487,-0.57383 -3.090342,-1.430597 -4.105468,-2.568359 -1.015125,-1.137763 -1.523438,-2.564783 -1.523438,-4.28125 0,-1.726256 0.508313,-3.156211 1.523438,-4.289063 1.015126,-1.132853 2.383981,-1.980915 4.105468,-2.544922 C 46.073051,32.281733 47.992602,32 50.111328,32 c 2.108615,0 4.021488,0.284633 5.738281,0.853516 1.153075,0.382214 2.149385,0.890534 2.988282,1.527343 0.409877,0.311002 0.78047,0.653647 1.115234,1.025391 0.681805,0.757172 1.135263,1.644203 1.361328,2.662109 0.319958,-0.521392 0.618124,-1.052349 0.892578,-1.59375 1.964164,-3.875351 2.313639,-8.170771 1.050781,-12.884765 -1.263217,-4.713996 -3.71256,-8.25072 -7.347656,-10.611328 C 54.717357,12.20395 53.445659,11.540917 52.091797,10.988281 49.319072,9.8567074 46.208157,9.1893425 42.759766,8.9882812 42.11818,8.9508774 41.472194,8.9284031 40.820312,8.9199219 Z M 22.804688,32 c 1.657604,0 3.187267,0.147162 4.589843,0.441406 1.402578,0.294246 2.607945,0.678586 3.613281,1.154297 1.005375,0.475712 1.732404,0.983987 2.183594,1.523438 l -3.765625,2.470703 c -0.549272,-0.598278 -1.383798,-1.075573 -2.501953,-1.433594 -1.118118,-0.357985 -2.475477,-0.537109 -4.074219,-0.537109 -0.784654,0 -1.577565,0.06103 -2.376953,0.183593 -0.799388,0.122639 -1.532255,0.330756 -2.199218,0.625 -0.666929,0.294246 -1.204303,0.706665 -1.611329,1.236328 -0.40706,0.529628 -0.609375,1.201538 -0.609375,2.015626 0,0.804263 0.202315,1.469424 0.609375,1.99414 0.407026,0.524753 0.9444,0.941056 1.611329,1.25 0.666963,0.30898 1.39983,0.526783 2.199218,0.654297 0.799388,0.127514 1.592299,0.191406 2.376953,0.191406 1.56931,0 2.929571,-0.195564 4.082032,-0.58789 1.15246,-0.392328 1.964746,-0.843499 2.435547,-1.353516 l 3.853515,2.457031 c -0.402149,0.431583 -1.109839,0.889287 -2.125,1.375 -1.015161,0.485355 -2.237912,0.894834 -3.669922,1.228516 -1.432008,0.333319 -2.973275,0.5 -4.621093,0.5 -1.441796,0 -2.85336,-0.138457 -4.236329,-0.417969 -1.382931,-0.279512 -2.633834,-0.723896 -3.751953,-1.332031 -1.118155,-0.608138 -2.007869,-1.401995 -2.669922,-2.382813 -0.662052,-0.980818 -0.99414,-2.173595 -0.99414,-3.576171 0,-1.422188 0.332088,-2.622661 0.99414,-3.603516 0.662053,-0.980818 1.551767,-1.770862 2.669922,-2.369141 1.118119,-0.598313 2.369022,-1.033008 3.751953,-1.302734 C 19.951328,32.134572 21.362892,32 22.804688,32 Z m 27.30664,3.677734 c -2.050114,0 -3.646459,0.355312 -4.789062,1.066407 -1.142604,0.711094 -1.714844,1.689877 -1.714844,2.935547 0,1.235846 0.57224,2.217532 1.714844,2.943359 1.142603,0.725792 2.738948,1.08789 4.789062,1.087891 2.049753,0 3.637466,-0.367901 4.765625,-1.103516 1.127798,-0.735614 1.693359,-1.711498 1.693359,-2.927734 0,-1.24567 -0.566364,-2.224453 -1.699218,-2.935547 -1.132853,-0.711095 -2.719763,-1.066407 -4.759766,-1.066407 z"
></path>
</SVGIcon>

View file

@ -1,11 +1,13 @@
---
import { ViewTransitions } from "astro:transitions";
import { ENABLE_VIEW_TRANSITIONS } from "astro:env/client";
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";
import AgeRestrictedModal from "../components/AgeRestrictedModal.astro";
type Props = {
pageTitle?: string;
@ -44,6 +46,7 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
<slot name="head-description" />
<meta property="og:url" content={Astro.url} />
<meta content="/logo.webp" property="og:image" />
{ENABLE_VIEW_TRANSITIONS ? <ViewTransitions /> : null}
</head>
<body>
<div class="flex min-h-screen flex-col">
@ -55,13 +58,13 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
</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"
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-normal 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"
class="sm:text-md flex flex-col items-center pt-9 text-sm font-medium tracking-wide text-black dark:text-white"
>
<div class="flex items-center">
<span id="copyright"
@ -81,7 +84,7 @@ const title = pageTitle ? `${pageTitle} | Bad Manners` : "Bad Manners";
<button
data-dark-mode
style={{ display: "none" }}
class="mt-2 print: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"
class="mt-2 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 print:hidden"
aria-labelledby="label-toggle-dark-mode"
>
<IconSun width="1.5rem" height="1.5rem" class="hidden dark:block" />

View file

@ -4,11 +4,13 @@ import { APACHE_CONFIG } from "astro:env/server";
const htaccess = `
ErrorDocument 404 /404.html
RedirectMatch 301 ^/tos(\/(index.html)?)?$ /terms_of_service/
RedirectMatch 301 ^/contact(\/(index.html)?)?$ /
Redirect 301 /@/aryion https://aryion.com/g4/user/BadManners
Redirect 301 /@/eka https://aryion.com/g4/user/BadManners
Redirect 301 /@/bluesky https://bsky.app/profile/badmanners.xyz
Redirect 301 /@/bsky https://bsky.app/profile/badmanners.xyz
Redirect 301 /@/codeberg https://codeberg.org/BadManners
Redirect 301 /@/cohost https://cohost.org/BadManners
Redirect 301 /@/fa https://www.furaffinity.net/user/badmanners
Redirect 301 /@/furaffinity https://www.furaffinity.net/user/badmanners
Redirect 301 /@/gallery https://gallery.badmanners.xyz

View file

@ -10,19 +10,21 @@ import ImageSamStickerJuicebox from "../assets/images/sam_sticker_juicebox.webp"
<article class="h-card" aria-labelledby="title-about-me">
<h1 id="title-about-me" class="text-2xl sm:text-3xl">About me</h1>
<section id="section-bad-manners">
<p class="mb-4 mt-5 sm:mb-3 sm:mt-6">
You can call me <b class="p-name">Bad Manners</b>, <b class="p-nickname">Manners</b>, <b class="p-nickname"
><abbr>BM</abbr></b
>, <b class="p-nickname">Bad</b>, <b class="p-nickname">Briefcase</b>... many choices to pick from! My pronouns
are "<span class="p-pronoun">he/him</span>". The term "<em>bad manners</em>" comes from gaming, where it's used
to refer to acting in a purposefully disrespectful and taunting manner towards other players, but I also chose
this moniker as it has plenty of personal meanings attached to it.
<p class="mb-4 mt-5 text-justify indent-6 sm:mb-3 sm:mt-6 sm:px-5 sm:indent-12">
You can call me <b class="p-name font-semibold">Bad Manners</b>, <b class="p-nickname font-semibold">Manners</b
>, <b class="p-nickname font-semibold"><abbr>BM</abbr></b>, <b class="p-nickname font-semibold">Bad</b>, <b
class="p-nickname font-semibold">Briefcase</b
>... many choices to pick from! My pronouns are <span class="p-pronoun font-medium">he/him</span>. The term "bad
manners" comes from gaming, where it's used to refer to acting in a purposefully disrespectful and taunting
manner towards other players, but I also chose this moniker as it has plenty of personal meanings attached to
it.
</p>
</section>
<section id="section-sam-brendan" class="h-card">
<p class="my-4 sm:mb-3 sm:mt-6">
You can also call me <b class="p-first-name">Sam</b>, which is my <span class="p-category">fursona</span>'s
name. He is a <span class="p-note">mimic and maned wolf hybrid</span>, and you can learn more about him by
<p class="my-4 text-justify indent-6 sm:mb-3 sm:px-5 sm:indent-12">
You can also call me <b class="p-first-name font-semibold">Sam</b>, which is my <span class="p-category"
>fursona</span
>'s name. He is a <span class="p-note">mimic and maned wolf hybrid</span>, and you can learn more about him by
clicking on him below!
</p>
<figure class="mt-4">
@ -42,11 +44,11 @@ import ImageSamStickerJuicebox from "../assets/images/sam_sticker_juicebox.webp"
<figcaption class="text-base">
A sticker of Sam by
<a
class="text-link transition-colors motion-reduce:transition-none"
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://olivecow.carrd.co/"
target="_blank"
>
<span class="underline">OliveCow</span>
<span class="p-name underline">OliveCow</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" />
</a>.
</figcaption>

View file

@ -1,190 +0,0 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconArrowUpRightFromSquare from "../components/icons/IconArrowUpRightFromSquare.astro";
import IconClone from "../components/icons/IconClone.astro";
import IconEnvelope from "../components/icons/IconEnvelope.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";
---
<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 through my main 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 group mx-1 select-none transition-colors aria-disabled:cursor-default aria-disabled:select-text aria-disabled:text-stone-700 motion-reduce:transition-none dark:aria-disabled:text-zinc-50"
aria-label="Copy my Discord username to clipboard"
aria-disabled="true"
>
<IconDiscord width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span class="p-nickname underline group-aria-disabled:cursor-text group-aria-disabled:no-underline"
>badmanners</span
>
<IconClone width="0.75rem" height="0.75rem" class="inline group-aria-disabled:hidden" />
</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 group mx-1 transition-colors aria-disabled:cursor-not-allowed aria-disabled:text-stone-700 motion-reduce:transition-none dark:aria-disabled:text-zinc-50"
aria-label="Copy my e-mail address to clipboard"
aria-disabled="true"
>
<IconEnvelope width="1.75rem" height="1.75rem" class="mr-1 inline" />
<span
class="underline group-aria-disabled:no-underline"
data-user="em"
data-website="zyx.srennamdab"
aria-label="Obfuscated e-mail address"></span>
<IconClone width="0.75rem" height="0.75rem" class="inline group-aria-disabled:hidden" />
</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, { hideAll } 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);
hideAll();
const text = discordButton.querySelector("span")!.innerText;
navigator.clipboard
.writeText(text)
.then(() => {
discordTooltip.setContent("Username copied to clipboard!");
discordTooltip.show();
tooltipTimeout = setTimeout(() => {
discordTooltip.hide();
}, 2000);
})
.catch((e) => {
console.error("Unable to copy Discord username to clipboard", e);
discordTooltip.setContent("Unable to copy username!");
discordTooltip.show();
tooltipTimeout = setTimeout(() => {
discordTooltip.hide();
}, 2000);
});
});
discordButton.setAttribute("aria-disabled", "false");
emailButton.addEventListener("click", (ev) => {
ev.preventDefault();
tooltipTimeout && clearTimeout(tooltipTimeout);
hideAll();
const emailData = emailButton.querySelector("span")!;
const text = `${emailData.dataset.website!}@${emailData.dataset.user!}`.split("").reverse().join("");
navigator.clipboard
.writeText(text)
.then(() => {
emailTooltip.setContent("E-mail address copied to clipboard!");
emailTooltip.show();
tooltipTimeout = setTimeout(() => {
emailTooltip.hide();
}, 2000);
})
.catch((e) => {
console.error("Unable to copy e-mail address to clipboard", e);
emailTooltip.setContent("Unable to copy e-mail address!");
emailTooltip.show();
tooltipTimeout = setTimeout(() => {
emailTooltip.hide();
}, 2000);
});
});
emailButton.setAttribute("aria-disabled", "false");
})();
</script>

View file

@ -1,12 +1,16 @@
---
import BaseLayout from "../layouts/BaseLayout.astro";
import IconEnvelope from "../components/icons/IconEnvelope.astro";
import IconBluesky from "../components/icons/brands/IconBluesky.astro";
import IconCodeberg from "../components/icons/brands/IconCodeberg.astro";
import IconCohost from "../components/icons/brands/IconCohost.astro";
import IconDiscord from "../components/icons/brands/IconDiscord.astro";
import IconEkasPortal from "../components/icons/brands/IconEkasPortal.astro";
import IconFurAffinity from "../components/icons/brands/IconFurAffinity.astro";
import IconGitHub from "../components/icons/brands/IconGitHub.astro";
import IconGitLab from "../components/icons/brands/IconGitLab.astro";
import IconInkbunny from "../components/icons/brands/IconInkbunny.astro";
import IconItaku from "../components/icons/brands/IconItaku.astro";
import IconItchIO from "../components/icons/brands/IconItchIO.astro";
import IconKeybase from "../components/icons/brands/IconKeybase.astro";
import IconMastodon from "../components/icons/brands/IconMastodon.astro";
@ -20,8 +24,6 @@ import IconTelegram from "../components/icons/brands/IconTelegram.astro";
import IconTwitch from "../components/icons/brands/IconTwitch.astro";
import IconWeasyl from "../components/icons/brands/IconWeasyl.astro";
import IconX from "../components/icons/brands/IconX.astro";
import IconEllipsis from "../components/icons/IconEllipsis.astro";
import IconItaku from "../components/icons/brands/IconItaku.astro";
---
<BaseLayout>
@ -30,7 +32,7 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
property="og:description"
content="Safe vore enthusiast, mimic hybrid, and occasional writer."
/>
<article class="h-card" aria-labelledby="title-home">
<article class="h-card" aria-label="Homepage of Bad Manners">
<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>
@ -47,9 +49,25 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
class="grid grid-cols-3 gap-x-4 gap-y-5 px-4 pt-8 sm:grid-cols-4 sm:px-20 md:px-32"
aria-label="Links"
>
<li>
<li id="e-mail">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-email text-link group block w-full transition-colors motion-reduce:transition-none"
href="mailto:me@badmanners.xyz"
target="_blank"
rel="me"
aria-label="me@badmanners.xyz"
>
<IconEnvelope
height="1.75rem"
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<p class="p-category sr-only">E-mail address</p>
</a>
</li>
<li id="bluesky">
<a
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://bsky.app/profile/badmanners.xyz"
target="_blank"
rel="me"
@ -60,12 +78,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">@badmanners.xyz on Bluesky</span>
<p class="sr-only">
<span class="p-nickname">@badmanners.xyz</span>
on
<span class="p-category">Bluesky</span>
</p>
</a>
</li>
<li>
<li id="codeberg">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://codeberg.org/BadManners"
target="_blank"
rel="me"
@ -76,12 +98,55 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Codeberg</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Codeberg</span>
</p>
</a>
</li>
<li>
<li id="cohost">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://cohost.org/BadManners"
target="_blank"
rel="me"
aria-label="Cohost"
>
<IconCohost
height="1.75rem"
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Cohost</span>
</p>
</a>
</li>
<li id="discord">
<button
class="text-link group block w-full transition-colors motion-reduce:transition-none"
aria-label="Discord"
data-username="badmanners"
aria-disabled="true"
>
<IconDiscord
height="1.75rem"
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<p class="sr-only">
<span class="p-nickname">badmanners</span>
on
<span class="p-category">Discord</span>
</p>
</button>
</li>
<li id="eka-s-portal">
<a
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://aryion.com/g4/user/BadManners"
target="_blank"
rel="me"
@ -92,12 +157,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Eka's Portal</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Eka's Portal</span>
</p>
</a>
</li>
<li>
<li id="fur-affinity">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://www.furaffinity.net/user/BadManners"
target="_blank"
rel="me"
@ -108,12 +177,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Fur Affinity</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Fur Affinity</span>
</p>
</a>
</li>
<li>
<li id="github">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://github.com/BadMannersXYZ"
target="_blank"
rel="me"
@ -124,12 +197,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadMannersXYZ on GitHub</span>
<p class="sr-only">
<span class="p-nickname">BadMannersXYZ</span>
on
<span class="p-category">GitHub</span>
</p>
</a>
</li>
<li>
<li id="gitlab">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://gitlab.com/Bad_Manners"
target="_blank"
rel="me"
@ -140,12 +217,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">Bad_Manners on GitLab</span>
<p class="sr-only">
<span class="p-nickname">Bad_Manners</span>
on
<span class="p-category">GitLab</span>
</p>
</a>
</li>
<li>
<li id="inkbunny">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://inkbunny.net/BadManners"
target="_blank"
rel="me"
@ -156,12 +237,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Inkbunny</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Inkbunny</span>
</p>
</a>
</li>
<li>
<li id="itaku">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://itaku.ee/profile/badmanners"
target="_blank"
rel="me"
@ -172,12 +257,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">badmanners on Itaku</span>
<p class="sr-only">
<span class="p-nickname">badmanners</span>
on
<span class="p-category">Itaku</span>
</p>
</a>
</li>
<li>
<li id="itch-io">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://bad-manners.itch.io"
target="_blank"
rel="me"
@ -188,12 +277,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">Bad Manners on Itch.io</span>
<p class="sr-only">
<span class="p-nickname">Bad Manners</span>
on
<span class="p-category">Itch.io</span>
</p>
</a>
</li>
<li>
<li id="keybase">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://keybase.io/badmanners"
target="_blank"
rel="me"
@ -204,12 +297,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">badmanners on Keybase</span>
<p class="sr-only">
<span class="p-nickname">badmanners</span>
on
<span class="p-category">Keybase</span>
</p>
</a>
</li>
<li>
<li id="mastodon">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://meow.social/@BadManners"
target="_blank"
rel="me"
@ -220,12 +317,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">@BadManners@meow.social on Mastodon</span>
<p class="sr-only">
<span class="p-nickname">@BadManners@meow.social</span>
on
<span class="p-category">Mastodon</span>
</p>
</a>
</li>
<li>
<li id="neocities">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://badmanners.neocities.org/"
target="_blank"
rel="me"
@ -236,12 +337,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">badmanners.neocities.org on Neocities</span>
<p class="sr-only">
<span class="p-nickname">badmanners.neocities.org</span>
on
<span class="p-category">Neocities</span>
</p>
</a>
</li>
<li>
<li id="picarto">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://www.picarto.tv/BadManners"
target="_blank"
rel="me"
@ -252,12 +357,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Picarto</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Picarto</span>
</p>
</a>
</li>
<li>
<li id="signal">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://signal.me/#eu/ytt_rk0fFmAB2JAW-x2PbUiJyc_H3kYmfL_Pq4QNh5QIDsiFtjdFHaqFRs1D36tB"
target="_blank"
rel="me"
@ -268,12 +377,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">badmanners.10 on Signal</span>
<p class="sr-only">
<span class="p-nickname">badmanners.10</span>
on
<span class="p-category">Signal</span>
</p>
</a>
</li>
<li>
<li id="sofurry">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://bad-manners.sofurry.com/"
target="_blank"
rel="me"
@ -284,12 +397,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">Bad Manners on SoFurry</span>
<p class="sr-only">
<span class="p-nickname">Bad Manners</span>
on
<span class="p-category">SoFurry</span>
</p>
</a>
</li>
<li>
<li id="steam">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://steamcommunity.com/id/badmanners_/"
target="_blank"
rel="me"
@ -300,12 +417,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">badmanners_ on Steam</span>
<p class="sr-only">
<span class="p-nickname">badmanners_</span>
on
<span class="p-category">Steam</span>
</p>
</a>
</li>
<li>
<li id="subscribestar">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://subscribestar.adult/bad-manners"
target="_blank"
rel="me"
@ -316,12 +437,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">Bad Manners on SubscribeStar</span>
<p class="sr-only">
<span class="p-nickname">Bad Manners</span>
on
<span class="p-category">SubscribeStar</span>
</p>
</a>
</li>
<li>
<li id="telegram">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://t.me/bad_manners"
target="_blank"
rel="me"
@ -332,12 +457,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">@bad_manners on Telegram</span>
<p class="sr-only">
<span class="p-nickname">@bad_manners</span>
on
<span class="p-category">Telegram</span>
</p>
</a>
</li>
<li>
<li id="twitch">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://www.twitch.tv/bad__manners"
target="_blank"
rel="me"
@ -348,12 +477,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">bad__manners on Twitch</span>
<p class="sr-only">
<span class="p-nickname">bad__manners</span>
on
<span class="p-category">Twitch</span>
</p>
</a>
</li>
<li>
<li id="weasyl">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://www.weasyl.com/~badmanners"
target="_blank"
rel="me"
@ -364,12 +497,16 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">BadManners on Weasyl</span>
<p class="sr-only">
<span class="p-nickname">BadManners</span>
on
<span class="p-category">Weasyl</span>
</p>
</a>
</li>
<li>
<li id="x">
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
class="u-url text-link group block w-full transition-colors motion-reduce:transition-none"
href="https://x.com/BadManners__"
target="_blank"
rel="me"
@ -380,20 +517,11 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<span class="p-nickname sr-only">@BadManners__ on X</span>
</a>
</li>
<li>
<a
class="u-url text-link group p-2 transition-colors motion-reduce:transition-none"
href="/contact"
aria-label="More socials..."
>
<IconEllipsis
height="1.75rem"
width="1.75rem"
class="inline transition-transform group-hover:scale-150 group-focus:scale-150 motion-reduce:transition-none motion-reduce:group-hover:scale-100 motion-reduce:group-focus:scale-100"
/>
<p class="sr-only">
<span class="p-nickname">@BadManners__</span>
on
<span class="p-category">X</span>
</p>
</a>
</li>
</ul>
@ -402,13 +530,99 @@ import IconItaku from "../components/icons/brands/IconItaku.astro";
</BaseLayout>
<script>
import tippy from "tippy.js";
import { ENABLE_VIEW_TRANSITIONS } from "astro:env/client";
import tippy, { hideAll } from "tippy.js";
import "tippy.js/dist/tippy.css";
(function () {
tippy(document.querySelectorAll("#links a"), {
type TimeoutTag = ReturnType<typeof setTimeout> | null;
const initIndexIcons = () => {
const indexLinks = document.querySelector<HTMLElementTagNameMap["ul"]>("ul#links");
if (!indexLinks) {
return;
}
const emailAnchor = indexLinks.querySelector<HTMLElementTagNameMap["a"]>("#links li#e-mail a");
const usernameButtons = indexLinks.querySelectorAll<HTMLElementTagNameMap["button"]>(
"#links li button[data-username]",
);
if (!emailAnchor) {
console.error("Missing e-mail anchor in #links list.");
return;
}
if (!usernameButtons.length) {
console.error("Missing username buttons in #links list.");
return;
}
// Instantiate hover tooltips
tippy("#links li button, #links li a", {
content: (el) => el.getAttribute("aria-label")!,
theme: "bm",
});
})();
// Add clipboard copy logic + manual tooltips to e-mail anchor and username buttons
let tooltipTimeoutTag: TimeoutTag = null;
const emailTooltip = tippy(emailAnchor, {
content: "E-mail address copied to clipboard!",
trigger: "manual",
theme: "bm",
});
emailAnchor.addEventListener("click", (ev) => {
ev.preventDefault();
tooltipTimeoutTag && clearTimeout(tooltipTimeoutTag);
hideAll();
navigator.clipboard
.writeText(emailAnchor.getAttribute("aria-label")!)
.then(() => {
emailTooltip.setContent("E-mail address copied to clipboard!");
emailTooltip.show();
})
.catch((e) => {
console.error("Unable to copy e-mail address to clipboard.", e);
emailTooltip.setContent("Unable to copy e-mail address!");
emailTooltip.show();
});
});
usernameButtons.forEach((button) => {
const label = button.getAttribute("aria-label");
if (!label) {
console.warn("Missing aria-label for button, ignoring...", button);
return;
}
const username = button.dataset.username;
if (!username) {
console.warn("Missing data-username for button, ignoring...", button);
return;
}
button.removeAttribute("aria-disabled");
button.removeAttribute("disabled");
const successContent = `${label} username copied to clipboard!`;
const buttonTooltip = tippy(button, {
content: successContent,
trigger: "manual",
theme: "bm",
});
button.addEventListener("click", (ev) => {
ev.preventDefault();
tooltipTimeoutTag && clearTimeout(tooltipTimeoutTag);
hideAll();
navigator.clipboard
.writeText(username)
.then(() => {
buttonTooltip.setContent(successContent);
buttonTooltip.show();
})
.catch((e) => {
console.error(`Unable to copy ${label} username to clipboard.`, e);
buttonTooltip.setContent(`Unable to copy ${label} username!`);
buttonTooltip.show();
});
});
});
};
if (ENABLE_VIEW_TRANSITIONS) {
document.addEventListener("astro:page-load", initIndexIcons);
} else {
initIndexIcons();
}
</script>

View file

@ -11,10 +11,9 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<article class="h-card" aria-labelledby="title-sam-brendan">
<h1 id="title-sam-brendan" class="p-name 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><span class="p-given-name">Sam</span> <span class="p-family-name">Brendan</span></b> (or simply <b
class="p-nickname">Sam</b
>), also going by <b class="p-nickname">Bad Manners</b>, is a <span class="p-note"
<p class="my-4 text-justify sm:my-6 sm:px-5">
<b class="font-semibold"><span class="p-given-name">Sam</span> <span class="p-family-name">Brendan</span></b> (or
simply Sam), also going by <b class="p-nickname font-semibold">Bad Manners</b>, is a <span class="p-note"
>mimic x maned wolf hybrid</span
>. 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.
@ -39,16 +38,16 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<figcaption class="text-base">
A reference sheet of Sam Brendan, by
<a
class="text-link transition-colors motion-reduce:transition-none"
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://linktr.ee/Rimmi1357"
target="_blank"
>
<span class="underline">Rimmi</span>
<span class="p-name underline">Rimmi</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" /></a
>. Click to view a high quality version.
</figcaption>
</figure>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-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;
@ -57,12 +56,12 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
&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">
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-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">
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-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
@ -91,30 +90,30 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
<figcaption class="text-base">
A set of stickers featuring Sam Brendan, by
<a
class="text-link transition-colors motion-reduce:transition-none"
class="h-card u-url text-link transition-colors motion-reduce:transition-none"
href="https://olivecow.carrd.co/"
target="_blank"
>
<span class="underline">OliveCow</span>
<span class="p-name underline">OliveCow</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="inline" /></a
>. Click to view a high quality version.
</figcaption>
</figure>
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-12 md:px-12">
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-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">
<p class="mt-3 text-justify indent-6 sm:mt-2 sm:px-5 sm:indent-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">
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-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
@ -124,7 +123,7 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
</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">
<ul class="mb-5 text-justify sm:mb-4 sm:px-5">
<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>
@ -147,36 +146,38 @@ import ImageSamRefsheet from "../assets/images/sam_refsheet.webp";
</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">
<p class="mb-5 mt-3 text-justify indent-6 sm:mb-4 sm:mt-2 sm:px-5 sm:indent-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">
<ul class="mb-5 mt-3 text-justify sm:mb-4 sm:mt-2 sm:px-5">
<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.
<b class="font-semibold">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.
<b class="font-semibold">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.
<b class="font-semibold">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).
<b class="font-semibold">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.
<b class="font-semibold">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>

View file

@ -11,7 +11,7 @@ import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/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">
<p class="mr-1 text-sm font-medium italic">
Last update: <time datetime={TOS_UPDATED_AT.toISOString().slice(0, 10)}
>{TOS_UPDATED_AT.toLocaleDateString("en-US", { dateStyle: "long" })}</time
>
@ -30,23 +30,23 @@ import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
</div>
{
TOS_COMMISSION_STATUS == "CLOSED" ? (
<p class="mb-4 text-2xl font-normal uppercase sm:mb-8 sm:px-5 md:px-6">
<p class="mb-4 text-2xl font-medium 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">
<p class="mb-4 text-2xl font-medium 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">
<p class="mb-4 text-2xl font-medium 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">
<p class="mb-4 text-2xl font-medium 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>
@ -58,11 +58,9 @@ import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
<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
done. My rate is <b class="font-semibold">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">
@ -75,7 +73,7 @@ import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
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">
<p id="note-1" class="mb-6 text-justify text-sm font-medium 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>
@ -131,9 +129,9 @@ import { TOS_COMMISSION_STATUS, TOS_UPDATED_AT } from "../data/tos";
</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
>!
<a href="/" class="text-link underline transition-colors motion-reduce:transition-none"
>please reach out through my socials</a
>! Preferably Discord or Telegram, where I'm more active.
</li>
</ul>
</section>

View file

@ -18,7 +18,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
<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">
<p class="mb-4 mt-5 text-justify indent-6 sm:mb-3 sm:mt-6 sm:px-5 sm:indent-12">
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"
@ -36,7 +36,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
>, 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="my-4 sm:mb-3 sm:mt-6">
<p class="my-4 text-justify indent-6 sm:mb-3 sm:mt-6 sm:px-5 sm:indent-12">
I've also made a game called
<a
class="text-link transition-colors motion-reduce:transition-none"
@ -44,9 +44,10 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
target="_blank"
><span class="underline">"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).
>, as part of Strawberry Jam 8 (a game jam hosted by eevee on Feb. 2024). It's been my biggest project so far,
and I'm very proud of it!
</p>
<p class="my-4 sm:mb-3 sm:mt-6">
<p class="my-4 text-justify indent-6 sm:mb-3 sm:mt-6 sm:px-5 sm:indent-12">
You can find my gallery on the following websites, which include all of my content. Aside from the first link,
these also include some of the art that I've gotten from others (commissions, gifts, etc.).
</p>
@ -128,7 +129,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
<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">
<p class="mb-6 mt-2 sm:mt-3 sm:px-5">
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
@ -136,7 +137,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
if interested.
</p>
) : TOS_COMMISSION_STATUS == "OPEN" ? (
<p class="mb-6 mt-2 sm:mt-3">
<p class="mb-6 mt-2 sm:mt-3 sm:px-5">
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
@ -144,7 +145,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
if interested.
</p>
) : TOS_COMMISSION_STATUS == "SEMI_OPEN" ? (
<p class="mb-6 mt-2 sm:mt-3">
<p class="mb-6 mt-2 sm:mt-3 sm:px-5">
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">
@ -153,7 +154,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
if interested.
</p>
) : TOS_COMMISSION_STATUS == "PRIVATE" ? (
<p class="mb-6 mt-2 sm:mt-3">
<p class="mb-6 mt-2 sm:mt-3 sm:px-5">
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">
@ -162,7 +163,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
if interested.
</p>
) : (
<p class="mb-6 mt-2 sm:mt-3">
<p class="mb-6 mt-2 sm:mt-3 sm:px-5">
Feel free to{" "}
<a href="/terms_of_service" class="text-link underline transition-colors motion-reduce:transition-none">
check out my Terms of Service
@ -215,7 +216,7 @@ import { SUBSCRIBESTAR_ENABLED } from "../data/subscribestar";
target="_blank"
class="text-link transition-colors motion-reduce:transition-none"
>
<span class="underline">writing code (such as both of my websites)</span>
<span class="underline">writing code (including for my websites)</span>
<IconArrowUpRightFromSquare width="0.75rem" height="0.75rem" class="mb-1 inline" />
</a>, and localizing
<a