Salon — Stylist Profile
An editorial stylist profile card for boutique salons and barbershops, pairing a gradient initials avatar with name, role, star rating and specialty chips. A short bio, animated stats row for years, clients and rebook rate, and a portfolio gallery of gradient tiles that enlarge in a keyboard-friendly lightbox. A gold Book with Aria call to action and a Follow toggle fire elegant toasts, all in a self-contained vanilla HTML, CSS and JavaScript snippet.
MCP
Código
:root {
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--gold: #b08d57;
--gold-d: #8c6d3f;
--gold-soft: #efe2cf;
--rose: #c9a78f;
--rose-soft: #f3e6dc;
--ink: #1c1814;
--ink-2: #3d362f;
--muted: #8a7d70;
--cream: #f7f1e8;
--bg: #faf6ef;
--white: #ffffff;
--line: rgba(28, 24, 20, 0.1);
--line-2: rgba(28, 24, 20, 0.18);
--ok: #5f8a6b;
--warn: #c08a3e;
--danger: #b3503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--shadow-sm: 0 1px 2px rgba(28, 24, 20, 0.05),
0 4px 14px rgba(28, 24, 20, 0.06);
--shadow-lg: 0 18px 50px -18px rgba(28, 24, 20, 0.35),
0 6px 20px rgba(28, 24, 20, 0.08);
}
*,
*::before,
*::after {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
min-height: 100vh;
font-family: var(--sans);
font-size: 16px;
line-height: 1.5;
color: var(--ink);
background: var(--bg);
background-image: radial-gradient(
120% 80% at 12% -10%,
var(--rose-soft) 0%,
transparent 55%
),
radial-gradient(110% 90% at 110% 10%, var(--gold-soft) 0%, transparent 50%);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1,
h2,
h3 {
margin: 0;
font-family: var(--serif);
font-weight: 600;
letter-spacing: 0.01em;
}
.stage {
display: flex;
justify-content: center;
padding: clamp(20px, 5vw, 56px) 18px;
}
/* ---------- Card shell ---------- */
.profile {
width: 100%;
max-width: 540px;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-lg);
padding: clamp(20px, 4vw, 34px);
position: relative;
overflow: hidden;
}
.profile::before {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 4px;
background: linear-gradient(
90deg,
var(--gold) 0%,
var(--rose) 50%,
var(--gold-d) 100%
);
}
/* ---------- Header ---------- */
.profile__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
margin-bottom: 26px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
}
.brand__mark {
display: grid;
place-items: center;
width: 30px;
height: 30px;
border-radius: 50%;
font-family: var(--serif);
font-weight: 700;
font-size: 13px;
letter-spacing: 0.02em;
color: var(--ink);
background: var(--gold-soft);
border: 1px solid var(--gold);
}
.brand__name {
font-family: var(--serif);
font-size: 18px;
font-weight: 600;
color: var(--ink-2);
}
.badge-status {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 5px 12px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--ok);
background: rgba(95, 138, 107, 0.1);
border: 1px solid rgba(95, 138, 107, 0.28);
}
.badge-status .dot {
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--ok);
box-shadow: 0 0 0 0 rgba(95, 138, 107, 0.5);
animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(95, 138, 107, 0.45);
}
70%,
100% {
box-shadow: 0 0 0 7px rgba(95, 138, 107, 0);
}
}
/* ---------- Identity ---------- */
.identity {
display: flex;
gap: 20px;
align-items: flex-start;
}
.avatar {
position: relative;
flex: 0 0 auto;
width: 104px;
height: 104px;
border-radius: 50%;
display: grid;
place-items: center;
background: linear-gradient(150deg, var(--rose) 0%, var(--gold-d) 100%);
box-shadow: var(--shadow-sm);
}
.avatar__initials {
font-family: var(--serif);
font-weight: 700;
font-size: 40px;
letter-spacing: 0.02em;
color: var(--white);
text-shadow: 0 1px 8px rgba(28, 24, 20, 0.28);
}
.avatar__ring {
position: absolute;
inset: -5px;
border-radius: 50%;
border: 1.5px solid var(--gold);
opacity: 0.7;
}
.identity__body {
min-width: 0;
flex: 1;
}
.eyebrow {
margin: 2px 0 4px;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--gold-d);
}
.display {
font-size: clamp(30px, 7vw, 40px);
line-height: 1.05;
font-weight: 700;
color: var(--ink);
}
/* ---------- Rating ---------- */
.rating {
display: flex;
align-items: center;
gap: 8px;
margin: 10px 0 14px;
font-size: 14px;
}
.rating strong {
font-weight: 700;
color: var(--ink);
}
.rating__count {
color: var(--muted);
}
.stars {
position: relative;
display: inline-block;
width: 92px;
height: 16px;
font-size: 16px;
line-height: 1;
background: linear-gradient(var(--line-2), var(--line-2));
-webkit-mask: repeating-linear-gradient(90deg, #000 0 16px, transparent 16px 18.4px);
mask: repeating-linear-gradient(90deg, #000 0 16px, transparent 16px 18.4px);
}
.stars::before,
.stars__fill {
content: "";
position: absolute;
inset: 0;
}
.stars::before {
background: var(--line);
-webkit-mask: var(--star-mask);
mask: var(--star-mask);
}
.stars {
--star: "★★★★★";
}
.stars::after {
content: var(--star);
position: absolute;
inset: 0;
color: var(--line-2);
letter-spacing: 2.4px;
}
.stars__fill {
width: var(--pct, 0%);
overflow: hidden;
white-space: nowrap;
}
.stars__fill::after {
content: "★★★★★";
color: var(--gold);
letter-spacing: 2.4px;
}
/* ---------- Chips ---------- */
.chips {
list-style: none;
display: flex;
flex-wrap: wrap;
gap: 7px;
margin: 0;
padding: 0;
}
.chip {
padding: 5px 12px;
border-radius: 999px;
font-size: 12.5px;
font-weight: 500;
color: var(--ink-2);
background: var(--cream);
border: 1px solid var(--line);
transition: border-color 0.2s ease, background 0.2s ease;
}
.chip:hover {
border-color: var(--gold);
background: var(--gold-soft);
}
/* ---------- Bio ---------- */
.bio {
margin: 22px 0;
padding: 18px 20px;
font-size: 15px;
color: var(--ink-2);
background: linear-gradient(var(--cream), var(--cream)) padding-box;
border: 1px solid var(--line);
border-left: 3px solid var(--gold);
border-radius: var(--r-md);
}
/* ---------- Stats ---------- */
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin: 0 0 26px;
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
background: var(--white);
}
.stat {
padding: 16px 10px;
text-align: center;
}
.stat + .stat {
border-left: 1px solid var(--line);
}
.stat dt {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--muted);
}
.stat dd {
margin: 4px 0 0;
font-family: var(--serif);
font-size: 30px;
font-weight: 700;
line-height: 1;
color: var(--ink);
}
.stat .num {
font-variant-numeric: tabular-nums;
}
/* ---------- Portfolio ---------- */
.portfolio__head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px solid var(--line);
}
.section-label {
font-size: 22px;
font-weight: 600;
color: var(--ink);
}
.hint {
font-size: 12px;
color: var(--muted);
letter-spacing: 0.02em;
}
.gallery {
list-style: none;
margin: 0 0 28px;
padding: 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
.tile {
position: relative;
display: block;
width: 100%;
aspect-ratio: 1 / 1;
border: none;
border-radius: var(--r-md);
cursor: pointer;
overflow: hidden;
font: inherit;
color: var(--white);
box-shadow: var(--shadow-sm);
transition: transform 0.28s cubic-bezier(0.2, 0.7, 0.2, 1),
box-shadow 0.28s ease;
}
.tile::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(
180deg,
transparent 40%,
rgba(28, 24, 20, 0.55) 100%
);
}
.tile__cap {
position: absolute;
left: 10px;
right: 10px;
bottom: 9px;
z-index: 1;
font-size: 11.5px;
font-weight: 600;
letter-spacing: 0.01em;
text-align: left;
opacity: 0;
transform: translateY(6px);
transition: opacity 0.25s ease, transform 0.25s ease;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.4);
}
.tile:hover,
.tile:focus-visible {
transform: translateY(-3px);
box-shadow: var(--shadow-lg);
outline: none;
}
.tile:hover .tile__cap,
.tile:focus-visible .tile__cap {
opacity: 1;
transform: translateY(0);
}
.tile:focus-visible {
outline: 2px solid var(--gold);
outline-offset: 2px;
}
.g1 {
background: linear-gradient(150deg, #e8c79a, #b9854a);
}
.g2 {
background: linear-gradient(150deg, #8a7866, #463c34);
}
.g3 {
background: linear-gradient(150deg, #f4e4c4, #cbab73);
}
.g4 {
background: linear-gradient(150deg, #d99a6c, #a85a36);
}
.g5 {
background: linear-gradient(150deg, #f0e3da, #d3b6a3);
}
.g6 {
background: linear-gradient(150deg, #d8b48f, #9a6f4a);
}
/* ---------- Actions ---------- */
.actions {
display: flex;
gap: 12px;
}
.btn {
flex: 1;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 14px 18px;
border-radius: var(--r-md);
font-family: var(--sans);
font-size: 14.5px;
font-weight: 600;
letter-spacing: 0.01em;
cursor: pointer;
border: 1px solid transparent;
transition: transform 0.12s ease, box-shadow 0.2s ease,
background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.btn:active {
transform: translateY(1px);
}
.btn:focus-visible {
outline: 2px solid var(--gold-d);
outline-offset: 2px;
}
.btn--primary {
flex: 1.6;
color: var(--white);
background: linear-gradient(135deg, var(--gold) 0%, var(--gold-d) 100%);
box-shadow: 0 8px 22px -8px rgba(140, 109, 63, 0.7);
}
.btn--primary:hover {
box-shadow: 0 12px 28px -8px rgba(140, 109, 63, 0.78);
}
.btn--ghost {
color: var(--ink);
background: var(--white);
border-color: var(--line-2);
}
.btn--ghost:hover {
border-color: var(--gold);
background: var(--cream);
}
.btn--ghost[aria-pressed="true"] {
color: var(--gold-d);
background: var(--gold-soft);
border-color: var(--gold);
}
.btn--ghost[aria-pressed="true"] .btn__label::before {
content: "✓ ";
}
/* ---------- Lightbox ---------- */
.lightbox {
position: fixed;
inset: 0;
z-index: 50;
display: grid;
place-items: center;
padding: 24px;
background: rgba(28, 24, 20, 0.62);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
opacity: 0;
animation: fade 0.22s ease forwards;
}
.lightbox[hidden] {
display: none;
}
@keyframes fade {
to {
opacity: 1;
}
}
.lightbox__close {
position: absolute;
top: 18px;
right: 22px;
width: 42px;
height: 42px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.3);
background: rgba(255, 255, 255, 0.12);
color: var(--white);
font-size: 24px;
line-height: 1;
cursor: pointer;
transition: background 0.2s ease;
}
.lightbox__close:hover {
background: rgba(255, 255, 255, 0.24);
}
.lightbox__figure {
margin: 0;
width: min(460px, 90vw);
background: var(--white);
border-radius: var(--r-lg);
overflow: hidden;
box-shadow: var(--shadow-lg);
transform: scale(0.94);
animation: pop 0.26s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
@keyframes pop {
to {
transform: scale(1);
}
}
.lightbox__art {
width: 100%;
aspect-ratio: 4 / 3;
}
.lightbox__cap {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
padding: 16px 20px;
}
.lightbox__cap strong {
font-family: var(--serif);
font-size: 22px;
font-weight: 600;
color: var(--ink);
}
.lightbox__cap span {
font-size: 13px;
color: var(--muted);
}
/* ---------- Toast ---------- */
.toast-wrap {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%);
z-index: 60;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
pointer-events: none;
}
.toast {
display: inline-flex;
align-items: center;
gap: 9px;
padding: 12px 18px;
border-radius: 999px;
font-size: 13.5px;
font-weight: 500;
color: var(--cream);
background: var(--ink);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: var(--shadow-lg);
opacity: 0;
transform: translateY(14px) scale(0.96);
transition: opacity 0.28s ease, transform 0.28s ease;
}
.toast::before {
content: "✦";
color: var(--gold);
}
.toast.show {
opacity: 1;
transform: translateY(0) scale(1);
}
/* ---------- Responsive ---------- */
@media (max-width: 520px) {
.stage {
padding: 16px 12px;
}
.identity {
gap: 16px;
}
.avatar {
width: 84px;
height: 84px;
}
.avatar__initials {
font-size: 32px;
}
.stats {
grid-template-columns: 1fr;
}
.stat + .stat {
border-left: none;
border-top: 1px solid var(--line);
}
.gallery {
grid-template-columns: repeat(2, 1fr);
}
.tile__cap {
opacity: 1;
transform: none;
}
.actions {
flex-direction: column;
}
.btn--primary {
flex: 1;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.001ms !important;
transition-duration: 0.001ms !important;
}
}(function () {
"use strict";
var STYLIST = "Aria";
/* ---------- Toast helper ---------- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg) {
if (!toastWrap) return;
var el = document.createElement("div");
el.className = "toast";
el.textContent = msg;
toastWrap.appendChild(el);
// force reflow so the transition runs
void el.offsetWidth;
el.classList.add("show");
setTimeout(function () {
el.classList.remove("show");
setTimeout(function () {
el.remove();
}, 300);
}, 2400);
}
/* ---------- Animated stat counters ---------- */
function animateCount(node) {
var target = parseInt(node.getAttribute("data-count"), 10) || 0;
var suffix = node.getAttribute("data-suffix") || "";
var dur = 1100;
var start = performance.now();
function frame(now) {
var p = Math.min((now - start) / dur, 1);
// easeOutCubic
var eased = 1 - Math.pow(1 - p, 3);
var value = Math.round(target * eased);
node.textContent = value.toLocaleString("en-US") + suffix;
if (p < 1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
var counters = Array.prototype.slice.call(
document.querySelectorAll(".num[data-count]")
);
if ("IntersectionObserver" in window && counters.length) {
var io = new IntersectionObserver(
function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
animateCount(entry.target);
io.unobserve(entry.target);
}
});
},
{ threshold: 0.4 }
);
counters.forEach(function (c) {
io.observe(c);
});
} else {
counters.forEach(animateCount);
}
/* ---------- Lightbox ---------- */
var lightbox = document.getElementById("lightbox");
var lightboxArt = document.getElementById("lightboxArt");
var lightboxTitle = document.getElementById("lightboxTitle");
var lightboxMeta = document.getElementById("lightboxMeta");
var lightboxClose = document.getElementById("lightboxClose");
var lastFocused = null;
function openLightbox(tile) {
lastFocused = tile;
var styles = window.getComputedStyle(tile);
lightboxArt.style.background = styles.backgroundImage;
lightboxTitle.textContent = tile.getAttribute("data-title") || "Look";
lightboxMeta.textContent = tile.getAttribute("data-meta") || "";
lightbox.hidden = false;
lightboxClose.focus();
document.addEventListener("keydown", onKeydown);
}
function closeLightbox() {
lightbox.hidden = true;
document.removeEventListener("keydown", onKeydown);
if (lastFocused) lastFocused.focus();
}
function onKeydown(e) {
if (e.key === "Escape") closeLightbox();
}
var gallery = document.getElementById("gallery");
if (gallery) {
gallery.addEventListener("click", function (e) {
var tile = e.target.closest(".tile");
if (tile) openLightbox(tile);
});
}
lightboxClose.addEventListener("click", closeLightbox);
lightbox.addEventListener("click", function (e) {
if (e.target === lightbox) closeLightbox();
});
/* ---------- Book CTA ---------- */
var bookBtn = document.getElementById("bookBtn");
if (bookBtn) {
bookBtn.addEventListener("click", function () {
toast("Opening " + STYLIST + "'s availability…");
});
}
/* ---------- Follow toggle ---------- */
var followBtn = document.getElementById("followBtn");
if (followBtn) {
var followLabel = followBtn.querySelector(".btn__label");
followBtn.addEventListener("click", function () {
var following = followBtn.getAttribute("aria-pressed") === "true";
following = !following;
followBtn.setAttribute("aria-pressed", String(following));
if (followLabel) followLabel.textContent = following ? "Following" : "Follow";
toast(
following
? "You're following " + STYLIST + ". We'll share new looks."
: "Unfollowed " + STYLIST + "."
);
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Stylist Profile · Maison Lumière Salon</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="stage">
<article class="profile" aria-labelledby="stylist-name">
<!-- Header band -->
<header class="profile__head">
<div class="brand">
<span class="brand__mark" aria-hidden="true">ML</span>
<span class="brand__name">Maison Lumière</span>
</div>
<span class="badge-status">
<span class="dot" aria-hidden="true"></span>
Booking open
</span>
</header>
<!-- Identity -->
<section class="identity">
<div class="avatar" role="img" aria-label="Portrait of Aria Vance">
<span class="avatar__initials" aria-hidden="true">AV</span>
<span class="avatar__ring" aria-hidden="true"></span>
</div>
<div class="identity__body">
<p class="eyebrow">Senior Colorist · Lvl V Artist</p>
<h1 class="display" id="stylist-name">Aria Vance</h1>
<div class="rating" aria-label="Rated 4.9 out of 5 from 312 reviews">
<span class="stars" aria-hidden="true">
<span class="stars__fill" style="--pct: 98%"></span>
</span>
<strong>4.9</strong>
<span class="rating__count">312 reviews</span>
</div>
<ul class="chips" aria-label="Specialties">
<li class="chip">Balayage</li>
<li class="chip">Lived-in Color</li>
<li class="chip">Blonde Correction</li>
<li class="chip">Gloss & Tone</li>
<li class="chip">Bridal</li>
</ul>
</div>
</section>
<!-- Bio -->
<p class="bio">
A decade behind the chair has made Aria a quiet authority on
dimensional blondes and soft, sun-grown brunettes. She designs color
that grows out gracefully — so the second appointment is a luxury,
never a rescue.
</p>
<!-- Stats -->
<dl class="stats">
<div class="stat">
<dt>Years</dt>
<dd><span class="num" data-count="11">0</span></dd>
</div>
<div class="stat">
<dt>Clients</dt>
<dd><span class="num" data-count="1840" data-suffix="+">0</span></dd>
</div>
<div class="stat">
<dt>Rebook</dt>
<dd><span class="num" data-count="94" data-suffix="%">0</span></dd>
</div>
</dl>
<!-- Portfolio -->
<section class="portfolio" aria-labelledby="portfolio-title">
<div class="portfolio__head">
<h2 class="section-label" id="portfolio-title">Portfolio</h2>
<span class="hint">Tap a look to enlarge</span>
</div>
<ul class="gallery" id="gallery">
<li>
<button class="tile g1" type="button" data-title="Honey Balayage" data-meta="Lived-in · 3.5 hrs">
<span class="tile__cap">Honey Balayage</span>
</button>
</li>
<li>
<button class="tile g2" type="button" data-title="Smoke Root Melt" data-meta="Brunette · 2 hrs">
<span class="tile__cap">Smoke Root Melt</span>
</button>
</li>
<li>
<button class="tile g3" type="button" data-title="Champagne Blonde" data-meta="Correction · 4 hrs">
<span class="tile__cap">Champagne Blonde</span>
</button>
</li>
<li>
<button class="tile g4" type="button" data-title="Copper Gloss" data-meta="Tone · 1.5 hrs">
<span class="tile__cap">Copper Gloss</span>
</button>
</li>
<li>
<button class="tile g5" type="button" data-title="Bridal Pearl" data-meta="Bridal · 3 hrs">
<span class="tile__cap">Bridal Pearl</span>
</button>
</li>
<li>
<button class="tile g6" type="button" data-title="Soft Money Piece" data-meta="Face frame · 2 hrs">
<span class="tile__cap">Soft Money Piece</span>
</button>
</li>
</ul>
</section>
<!-- Actions -->
<footer class="actions">
<button class="btn btn--primary" id="bookBtn" type="button">
Book with Aria
</button>
<button
class="btn btn--ghost"
id="followBtn"
type="button"
aria-pressed="false"
>
<span class="btn__label">Follow</span>
</button>
</footer>
</article>
</main>
<!-- Lightbox -->
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Portfolio preview" hidden>
<button class="lightbox__close" id="lightboxClose" type="button" aria-label="Close preview">×</button>
<figure class="lightbox__figure">
<div class="lightbox__art" id="lightboxArt"></div>
<figcaption class="lightbox__cap">
<strong id="lightboxTitle">Look</strong>
<span id="lightboxMeta"></span>
</figcaption>
</figure>
</div>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Stylist Profile
A luxe, editorial profile panel for the artists behind the chair at Maison Lumière Salon. A gradient initials avatar anchors a confident serif display name, a Senior Colorist title, and a precise star rating with review count. Specialty chips, a hand-written bio, and a hairline-bordered stats row establish trust at a glance — the kind of screen a boutique salon would actually ship.
The stats for years, clients, and rebook rate count up smoothly the first time they scroll into view. The portfolio reads as a tidy grid of gradient tiles standing in for real photography; tap or key into any look and it blooms into a soft-blurred lightbox with title and service metadata, dismissable with Escape, a backdrop click, or the close button.
Two clear actions close the card: a gold gradient Book with Aria primary button and a Follow toggle that flips its pressed state and label, each surfacing a quiet pill toast. Everything is plain HTML, CSS, and vanilla JavaScript — no frameworks, no build step — with AA-contrast color, visible focus rings, reduced-motion support, and a layout that holds together down to roughly 360px.