/* ── Claymorphism UI kit tokens (uiCookies) — identical in every set ──────────────
Every component is a puffy pastel object floating above --clay-bg: one big soft drop shadow
below it, an inset highlight top left and an inset shade bottom right. Change a token and
the whole kit follows; the shadow recipes below are built from these values. */
:root{
--clay-bg:#f3effa; /* page, a pale lilac white */
--clay-surface:#fffcf8; /* cards, fields and plain objects */
--clay-peach:#ffd2bd; /* object fills: ink text on every fill reaches 11:1 */
--clay-lilac:#dccdff;
--clay-mint:#bdeed6;
--clay-sky:#c2e0ff;
--clay-lemon:#ffe9a3;
--clay-ink:#241a3d; /* text, 14.4:1 on --clay-bg */
--clay-muted:#51476a; /* secondary text, 5.7:1 or more on every fill */
--clay-edge:#7a6e96; /* control boundary, 4.1:1 on --clay-bg, so shadows are never the only edge */
--clay-line:rgba(36,26,61,.12); /* decorative edge on cards and panels */
--clay-accent:#6236e8; /* focus ring and on / selected / current states, 5.8:1 on --clay-bg */
--clay-on-accent:#ffffff; /* text and marks on accent fills, 6.5:1 */
--clay-tint:#e9e1ff; /* light accent wash behind current items */
--clay-drop:rgba(86,56,168,.30); /* colour of the outer drop shadow */
--clay-hi:rgba(255,255,255,.88); /* inset highlight, top left */
--clay-lo:rgba(52,30,112,.20); /* inset shade, bottom right */
--clay-lift:14px; /* how high objects float; every shadow scales from it */
--clay-radius:30px; /* cards; buttons are pills, fields use .55 of it */
--clay-bw:1.5px; /* control border width */
--clay-success:#17663d;--clay-warning:#8a4b00;--clay-danger:#b3261e;
--clay-font:ui-rounded,"SF Pro Rounded","Nunito","Quicksand","Varela Round","Arial Rounded MT Bold",system-ui,sans-serif;
}
/* --clay-fill is set by .clay-peach, .clay-mint… on one object and must not leak into its children */
@property --clay-fill{syntax:"*";inherits:false}
/* Dark clay: data-clay="dark" on any wrapper, or a checked .clay-theme-input inside a [data-clay] element.
:is() is a forgiving list, so browsers without :has() still apply data-clay="dark" */
[data-clay]:is([data-clay="dark"],:has(.clay-theme-input:checked)){
--clay-bg:#1a1530;--clay-surface:#2a2347;
--clay-peach:#6e3f33;--clay-lilac:#4a3a8a;--clay-mint:#215a4a;--clay-sky:#26497c;--clay-lemon:#5f5220;
--clay-ink:#f7f3ff;--clay-muted:#d2c9ea;--clay-edge:#8f83b8;--clay-line:rgba(255,255,255,.1);
--clay-accent:#bba6ff;--clay-on-accent:#1a1530;--clay-tint:#3a2f66;
--clay-drop:rgba(4,2,14,.62);--clay-hi:rgba(255,255,255,.2);--clay-lo:rgba(0,0,0,.34);
--clay-success:#86e3b0;--clay-warning:#ffc76e;--clay-danger:#ffa196;color-scheme:dark}
/* More contrast requested: soft shading stays, but every edge and secondary text turns full ink */
@media (prefers-contrast:more){:root,[data-clay]:is([data-clay],:has(.clay-theme-input:checked)){
--clay-edge:var(--clay-ink);--clay-line:var(--clay-ink);--clay-muted:var(--clay-ink);--clay-bw:2px}}
:root,[data-clay]{
--clay-shadow:0 var(--clay-lift) calc(var(--clay-lift)*2.2) calc(var(--clay-lift)*-.3) var(--clay-drop),
inset calc(var(--clay-lift)*.45) calc(var(--clay-lift)*.5) calc(var(--clay-lift)*.9) var(--clay-hi),
inset calc(var(--clay-lift)*-.45) calc(var(--clay-lift)*-.6) calc(var(--clay-lift)*.9) var(--clay-lo);
--clay-shadow-sm:0 calc(var(--clay-lift)*.55) calc(var(--clay-lift)*1.2) calc(var(--clay-lift)*-.15) var(--clay-drop),
inset calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.3) calc(var(--clay-lift)*.5) var(--clay-hi),
inset calc(var(--clay-lift)*-.25) calc(var(--clay-lift)*-.35) calc(var(--clay-lift)*.5) var(--clay-lo);
--clay-press:0 calc(var(--clay-lift)*.12) calc(var(--clay-lift)*.25) 0 var(--clay-drop),
inset calc(var(--clay-lift)*.2) calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
--clay-well:inset calc(var(--clay-lift)*.15) calc(var(--clay-lift)*.22) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
}
[data-clay]{background:var(--clay-bg);color:var(--clay-ink)}
/* Components */
:where([class*="clay-"]),:where([class*="clay-"])::before,:where([class*="clay-"])::after{box-sizing:border-box}
.clay-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.clay-ico{width:22px;height:22px;flex:none}
.clay-ico--lg{width:28px;height:28px}
/* Fills: put one on any object; components read --clay-fill */
.clay-peach{--clay-fill:var(--clay-peach)}.clay-lilac{--clay-fill:var(--clay-lilac)}.clay-mint{--clay-fill:var(--clay-mint)}
.clay-sky{--clay-fill:var(--clay-sky)}.clay-lemon{--clay-fill:var(--clay-lemon)}
/* Card: a puffy panel that floats on the big drop shadow */
.clay-card{margin:0;padding:1.6rem;border:var(--clay-bw) solid var(--clay-line);border-radius:var(--clay-radius);
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);box-shadow:var(--clay-shadow)}
/* Layout helpers */
.clay-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:18px}
.clay-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:22px}
.clay-stack{display:grid;gap:1.4rem}
/* Buttons: pastel pills that float; hover lifts them, pressing squashes the shadow flat */
.clay-btn{display:inline-flex;align-items:center;justify-content:center;gap:.55rem;min-height:50px;margin:0;padding:.7rem 1.5rem;
border:var(--clay-bw) solid var(--clay-edge);border-radius:999px;background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);
font-family:inherit;font-size:1rem;font-weight:800;line-height:1.2;text-decoration:none;cursor:pointer;
box-shadow:var(--clay-shadow-sm);transition:transform .18s,box-shadow .18s}
.clay-btn:hover{transform:translateY(-2px);box-shadow:var(--clay-shadow)}
.clay-btn:active{transform:translateY(2px);box-shadow:var(--clay-press)}
.clay-btn:focus-visible{outline:3px solid var(--clay-accent);outline-offset:4px}
.clay-btn--accent{--clay-fill:var(--clay-accent);border-color:var(--clay-accent);color:var(--clay-on-accent)}
.clay-btn--sm{min-height:40px;padding:.45rem 1.05rem;font-size:.88rem}
.clay-btn--block{width:100%}
/* Icon buttons: round (or squircle) clay pebbles with a chunky icon and an aria-label */
.clay-iconbtn{display:inline-grid;place-items:center;width:58px;height:58px;margin:0;padding:0;border:var(--clay-bw) solid var(--clay-edge);border-radius:50%;
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);cursor:pointer;box-shadow:var(--clay-shadow-sm);transition:transform .18s,box-shadow .18s}
.clay-iconbtn:hover{transform:translateY(-2px);box-shadow:var(--clay-shadow)}
.clay-iconbtn:active{transform:translateY(2px);box-shadow:var(--clay-press)}
.clay-iconbtn:focus-visible{outline:3px solid var(--clay-accent);outline-offset:4px}
.clay-iconbtn--sq{border-radius:20px}
.clay-iconbtn--sm{width:44px;height:44px}
.clay-iconbtn--lg{width:72px;height:72px}
.clay-iconbtn--accent{--clay-fill:var(--clay-accent);border-color:var(--clay-accent);color:var(--clay-on-accent)}
@media (prefers-reduced-motion:reduce){.clay-btn,.clay-iconbtn{transition:none}.clay-btn:hover,.clay-btn:active,.clay-iconbtn:hover,.clay-iconbtn:active{transform:none}}
/* Blob: a small puffy icon tile that sits on any card */
.clay-blob{display:grid;place-items:center;flex:none;width:52px;height:52px;border:var(--clay-bw) solid var(--clay-line);border-radius:18px;
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);box-shadow:var(--clay-shadow-sm)}
.clay-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.3rem .75rem;border:var(--clay-bw) solid var(--clay-line);border-radius:999px;
background:var(--clay-surface);color:var(--clay-ink);font-size:.8rem;font-weight:800;line-height:1.3}
.clay-chip .clay-ico{width:16px;height:16px}
/* Stat cards: coloured clay slabs with a blob icon, a big number and a sentence that explains the change */
.clay-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:24px}
.clay-stat{display:grid;gap:.25rem;align-content:start}
.clay-stat__label{margin:.85rem 0 0;font-size:.9rem;font-weight:800;color:var(--clay-muted)}
.clay-stat__value{margin:0;font-size:2.2rem;font-weight:800;line-height:1.1;letter-spacing:-.01em}
.clay-stat__unit{font-size:1rem;font-weight:700}
.clay-stat__note{margin:0;font-size:.86rem;font-weight:600;color:var(--clay-muted)}
/* Content card: an illustration panel made of clay shapes, a chip, a title and a progress line */
.clay-lesson{display:flex;flex-direction:column;gap:1rem;padding:1rem 1rem 1.4rem}
.clay-art{position:relative;height:170px;overflow:hidden;border-radius:calc(var(--clay-radius)*.75);background:var(--clay-sky);box-shadow:var(--clay-well)}
.clay-art__shape{position:absolute;border-radius:50%;box-shadow:var(--clay-shadow-sm)}
.clay-art__shape--sun{top:22px;right:34px;width:58px;height:58px;background:var(--clay-lemon)}
.clay-art__shape--cloud{top:52px;left:40px;width:140px;height:54px;border-radius:999px;background:var(--clay-surface)}
.clay-art__shape--cloud2{top:30px;left:78px;width:70px;height:60px;background:var(--clay-surface)}
.clay-art__shape--hill{bottom:-70px;left:-20px;width:240px;height:140px;background:var(--clay-mint)}
.clay-art__shape--hill2{bottom:-90px;right:-40px;width:260px;height:150px;background:var(--clay-peach)}
.clay-art__shape--drop{top:116px;width:14px;height:20px;border-radius:50% 50% 50% 50%/60% 60% 40% 40%;background:var(--clay-accent);box-shadow:none}
.clay-lesson__body{display:grid;gap:.55rem;padding:0 .5rem}
.clay-lesson__tag{margin:0}
.clay-lesson__title{margin:0;font-size:1.3rem;font-weight:800;line-height:1.25}
.clay-lesson__text{margin:0;color:var(--clay-muted)}
.clay-lesson__foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:1rem;padding:0 .5rem}
.clay-meter{display:grid;gap:.35rem;flex:1;min-width:140px;font-size:.84rem;font-weight:700;color:var(--clay-muted)}
.clay-meter__bar{height:14px;border:var(--clay-bw) solid var(--clay-edge);border-radius:999px;background:var(--clay-bg);box-shadow:var(--clay-well);overflow:hidden}
.clay-meter__fill{display:block;height:100%;width:var(--clay-pct,50%);border-radius:999px;background:var(--clay-accent)}
/* Profile card */
.clay-profile{display:grid;justify-items:center;gap:.4rem;text-align:center}
.clay-avatar{display:grid;place-items:center;width:96px;height:96px;margin-bottom:.6rem;border:var(--clay-bw) solid var(--clay-line);border-radius:50%;
background:var(--clay-fill,var(--clay-lilac));font-size:2rem;font-weight:800;box-shadow:var(--clay-shadow)}
.clay-profile__name{margin:0;font-size:1.3rem;font-weight:800;line-height:1.25}
.clay-profile__role{margin:0;color:var(--clay-muted)}
.clay-profile__stats{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;width:100%;margin:.9rem 0 1.1rem;padding:0;list-style:none}
.clay-profile__stat{display:grid;padding:.6rem .3rem;border-radius:18px;background:var(--clay-fill,var(--clay-bg));box-shadow:var(--clay-well)}
.clay-profile__num{font-size:1.25rem;font-weight:800;line-height:1.2}
.clay-profile__key{font-size:.78rem;font-weight:700;color:var(--clay-muted)}
.clay-profile__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:12px}
/* Pricing pair */
.clay-plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:28px;align-items:start}
.clay-plan{position:relative;display:grid;gap:.9rem}
.clay-plan__badge{position:absolute;top:-14px;right:24px;padding:.3rem .85rem;border:var(--clay-bw) solid var(--clay-accent);border-radius:999px;
background:var(--clay-accent);color:var(--clay-on-accent);font-size:.78rem;font-weight:800;box-shadow:var(--clay-shadow-sm)}
.clay-plan__name{margin:0;font-size:1.15rem;font-weight:800}
.clay-plan__price{margin:0;font-size:2.6rem;font-weight:800;line-height:1}
.clay-plan__per{font-size:1rem;font-weight:700;color:var(--clay-muted)}
.clay-plan__desc{margin:0;color:var(--clay-muted)}
.clay-plan__list{display:grid;gap:.6rem;margin:.2rem 0 .4rem;padding:0;list-style:none}
.clay-plan__item{display:flex;align-items:center;gap:.6rem;font-weight:600}
.clay-plan__tick{display:grid;place-items:center;flex:none;width:26px;height:26px;border-radius:50%;background:var(--clay-fill,var(--clay-surface));box-shadow:var(--clay-shadow-sm)}
.clay-plan__tick .clay-ico{width:16px;height:16px}
.clay-two{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:28px;align-items:start}
@media (max-width:767px){.clay-two{grid-template-columns:1fr}}
.clay-stats--two{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
/* Alerts: coloured clay slabs that name their type in a word, an icon and hidden text, not only in colour */
.clay-alerts{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,380px),1fr));gap:22px}
.clay-alert{display:flex;align-items:flex-start;gap:1rem;padding:1.1rem 1.2rem;border:var(--clay-bw) solid var(--clay-line);border-radius:calc(var(--clay-radius)*.8);
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);box-shadow:var(--clay-shadow)}
.clay-alert__icon{display:grid;place-items:center;flex:none;width:44px;height:44px;border-radius:15px;background:var(--clay-surface);box-shadow:var(--clay-shadow-sm)}
.clay-alert__title{margin:0;font-weight:800;line-height:1.3}
.clay-alert__text{margin:.15rem 0 0;font-size:.92rem;color:var(--clay-muted)}
/* Toasts: a polite live region; each toast is a floating pebble with a close button */
.clay-toasts{display:grid;gap:16px;max-width:420px}
.clay-toast{display:flex;align-items:center;gap:.9rem;padding:.8rem .8rem .8rem 1rem;border:var(--clay-bw) solid var(--clay-line);border-radius:24px;
background:var(--clay-surface);color:var(--clay-ink);box-shadow:var(--clay-shadow)}
.clay-toast[hidden]{display:none}
.clay-toast__body{flex:1;min-width:0}
.clay-toast__title{margin:0;font-weight:800;line-height:1.3}
.clay-toast__text{margin:0;font-size:.88rem;color:var(--clay-muted)}
/* Tooltips: shown on hover and keyboard focus, dismissed with Escape, tied to the button with aria-describedby */
.clay-tip{position:relative;display:inline-flex}
.clay-tip__bubble{position:absolute;bottom:calc(100% + 12px);left:50%;z-index:5;width:max-content;max-width:220px;padding:.5rem .8rem;border:var(--clay-bw) solid var(--clay-ink);
border-radius:14px;background:var(--clay-ink);color:var(--clay-bg);font-size:.84rem;font-weight:700;line-height:1.35;text-align:center;
box-shadow:var(--clay-shadow-sm);opacity:0;visibility:hidden;transform:translate(-50%,6px);transition:opacity .18s,transform .18s,visibility .18s;pointer-events:none}
.clay-tip__bubble::after{content:"";position:absolute;top:100%;left:50%;margin-left:-7px;border:7px solid transparent;border-top-color:var(--clay-ink)}
.clay-tip:hover .clay-tip__bubble,.clay-tip:focus-within .clay-tip__bubble{opacity:1;visibility:visible;transform:translate(-50%,0)}
.clay-tip.is-dismissed .clay-tip__bubble{opacity:0;visibility:hidden}
/* Dropdown: a button with aria-expanded and a floating clay menu */
.clay-dd{position:relative;display:inline-block}
.clay-dd__chev{transition:transform .18s}
.clay-dd [aria-expanded="true"] .clay-dd__chev{transform:rotate(180deg)}
.clay-dd__menu{position:absolute;top:calc(100% + 12px);left:0;z-index:10;min-width:250px;margin:0;padding:.5rem;list-style:none;border:var(--clay-bw) solid var(--clay-line);
border-radius:22px;background:var(--clay-surface);box-shadow:var(--clay-shadow)}
.clay-dd__menu[hidden]{display:none}
.clay-dd__item{display:flex;align-items:center;gap:.7rem;width:100%;min-height:46px;padding:.45rem .7rem;border:0;border-radius:15px;background:transparent;
color:var(--clay-ink);font-family:inherit;font-size:.95rem;font-weight:700;text-align:left;text-decoration:none;cursor:pointer}
.clay-dd__item:hover{background:var(--clay-bg)}
.clay-dd__item:focus-visible{outline:3px solid var(--clay-accent);outline-offset:-3px}
.clay-dd__item--danger{color:var(--clay-danger)}
.clay-dd__sep{height:0;margin:.35rem .5rem;border-top:var(--clay-bw) solid var(--clay-line)}
/* Dialog: native <dialog> opened with showModal(), so focus is trapped and Escape closes it */
.clay-dialog{width:min(440px,calc(100% - 32px));margin:auto;padding:1.8rem;border:var(--clay-bw) solid var(--clay-line);border-radius:var(--clay-radius);
background:var(--clay-surface);color:var(--clay-ink);box-shadow:var(--clay-shadow);text-align:center}
.clay-dialog::backdrop{background:rgba(36,26,61,.45)}
.clay-dialog[open]{animation:clay-pop .25s ease-out}
@keyframes clay-pop{from{transform:scale(.92);opacity:0}}
.clay-dialog__icon{display:grid;place-items:center;width:70px;height:70px;margin:0 auto 1rem;border-radius:24px;background:var(--clay-peach);box-shadow:var(--clay-shadow-sm)}
.clay-dialog__title{margin:0;font-size:1.35rem;font-weight:800;line-height:1.25}
.clay-dialog__text{margin:.5rem 0 1.4rem;color:var(--clay-muted)}
.clay-dialog__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin:0}
.clay-row{display:flex;flex-wrap:wrap;align-items:flex-start;gap:56px}
.clay-row>section{flex:1 1 280px;min-width:0}
@media (prefers-reduced-motion:reduce){.clay-tip__bubble,.clay-dd__chev{transition:none}.clay-dialog[open]{animation:none}}
JavaScript
// Dialog: any button with data-clay-open="dialog-id" opens that <dialog>; focus returns to the button on close
document.querySelectorAll('[data-clay-open]').forEach(function (btn) {
var dialog = document.getElementById(btn.getAttribute('data-clay-open'));
if (!dialog) return;
btn.addEventListener('click', function () {
dialog.showModal();
dialog.addEventListener('close', function () { btn.focus(); }, { once: true });
});
dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); }); // backdrop click
});
// Tooltips: Escape hides the visible tooltip until the pointer or focus leaves it
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
document.querySelectorAll('[data-clay-tip]').forEach(function (t) { if (t.matches(':hover, :focus-within')) t.classList.add('is-dismissed'); });
});
document.querySelectorAll('[data-clay-tip]').forEach(function (t) {
['mouseleave', 'focusout'].forEach(function (ev) { t.addEventListener(ev, function () { t.classList.remove('is-dismissed'); }); });
});
// Toasts: a close button hides its toast
document.querySelectorAll('[data-clay-dismiss]').forEach(function (b) {
b.addEventListener('click', function () { b.closest('[data-clay-toast]').hidden = true; });
});
// Dropdown: toggle on click; Escape (focus back to the button) or a click outside closes it
document.querySelectorAll('[data-clay-dropdown]').forEach(function (btn) {
var menu = document.getElementById(btn.getAttribute('aria-controls'));
function set(open) { btn.setAttribute('aria-expanded', open); menu.hidden = !open; }
btn.addEventListener('click', function () { set(btn.getAttribute('aria-expanded') !== 'true'); });
document.addEventListener('click', function (e) { if (!btn.parentNode.contains(e.target)) set(false); });
btn.parentNode.addEventListener('keydown', function (e) { if (e.key === 'Escape' && btn.getAttribute('aria-expanded') === 'true') { set(false); btn.focus(); } });
});
/* ── Claymorphism UI kit tokens (uiCookies) — identical in every set ──────────────
Every component is a puffy pastel object floating above --clay-bg: one big soft drop shadow
below it, an inset highlight top left and an inset shade bottom right. Change a token and
the whole kit follows; the shadow recipes below are built from these values. */
:root{
--clay-bg:#f3effa; /* page, a pale lilac white */
--clay-surface:#fffcf8; /* cards, fields and plain objects */
--clay-peach:#ffd2bd; /* object fills: ink text on every fill reaches 11:1 */
--clay-lilac:#dccdff;
--clay-mint:#bdeed6;
--clay-sky:#c2e0ff;
--clay-lemon:#ffe9a3;
--clay-ink:#241a3d; /* text, 14.4:1 on --clay-bg */
--clay-muted:#51476a; /* secondary text, 5.7:1 or more on every fill */
--clay-edge:#7a6e96; /* control boundary, 4.1:1 on --clay-bg, so shadows are never the only edge */
--clay-line:rgba(36,26,61,.12); /* decorative edge on cards and panels */
--clay-accent:#6236e8; /* focus ring and on / selected / current states, 5.8:1 on --clay-bg */
--clay-on-accent:#ffffff; /* text and marks on accent fills, 6.5:1 */
--clay-tint:#e9e1ff; /* light accent wash behind current items */
--clay-drop:rgba(86,56,168,.30); /* colour of the outer drop shadow */
--clay-hi:rgba(255,255,255,.88); /* inset highlight, top left */
--clay-lo:rgba(52,30,112,.20); /* inset shade, bottom right */
--clay-lift:14px; /* how high objects float; every shadow scales from it */
--clay-radius:30px; /* cards; buttons are pills, fields use .55 of it */
--clay-bw:1.5px; /* control border width */
--clay-success:#17663d;--clay-warning:#8a4b00;--clay-danger:#b3261e;
--clay-font:ui-rounded,"SF Pro Rounded","Nunito","Quicksand","Varela Round","Arial Rounded MT Bold",system-ui,sans-serif;
}
/* --clay-fill is set by .clay-peach, .clay-mint… on one object and must not leak into its children */
@property --clay-fill{syntax:"*";inherits:false}
/* Dark clay: data-clay="dark" on any wrapper, or a checked .clay-theme-input inside a [data-clay] element.
:is() is a forgiving list, so browsers without :has() still apply data-clay="dark" */
[data-clay]:is([data-clay="dark"],:has(.clay-theme-input:checked)){
--clay-bg:#1a1530;--clay-surface:#2a2347;
--clay-peach:#6e3f33;--clay-lilac:#4a3a8a;--clay-mint:#215a4a;--clay-sky:#26497c;--clay-lemon:#5f5220;
--clay-ink:#f7f3ff;--clay-muted:#d2c9ea;--clay-edge:#8f83b8;--clay-line:rgba(255,255,255,.1);
--clay-accent:#bba6ff;--clay-on-accent:#1a1530;--clay-tint:#3a2f66;
--clay-drop:rgba(4,2,14,.62);--clay-hi:rgba(255,255,255,.2);--clay-lo:rgba(0,0,0,.34);
--clay-success:#86e3b0;--clay-warning:#ffc76e;--clay-danger:#ffa196;color-scheme:dark}
/* More contrast requested: soft shading stays, but every edge and secondary text turns full ink */
@media (prefers-contrast:more){:root,[data-clay]:is([data-clay],:has(.clay-theme-input:checked)){
--clay-edge:var(--clay-ink);--clay-line:var(--clay-ink);--clay-muted:var(--clay-ink);--clay-bw:2px}}
:root,[data-clay]{
--clay-shadow:0 var(--clay-lift) calc(var(--clay-lift)*2.2) calc(var(--clay-lift)*-.3) var(--clay-drop),
inset calc(var(--clay-lift)*.45) calc(var(--clay-lift)*.5) calc(var(--clay-lift)*.9) var(--clay-hi),
inset calc(var(--clay-lift)*-.45) calc(var(--clay-lift)*-.6) calc(var(--clay-lift)*.9) var(--clay-lo);
--clay-shadow-sm:0 calc(var(--clay-lift)*.55) calc(var(--clay-lift)*1.2) calc(var(--clay-lift)*-.15) var(--clay-drop),
inset calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.3) calc(var(--clay-lift)*.5) var(--clay-hi),
inset calc(var(--clay-lift)*-.25) calc(var(--clay-lift)*-.35) calc(var(--clay-lift)*.5) var(--clay-lo);
--clay-press:0 calc(var(--clay-lift)*.12) calc(var(--clay-lift)*.25) 0 var(--clay-drop),
inset calc(var(--clay-lift)*.2) calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
--clay-well:inset calc(var(--clay-lift)*.15) calc(var(--clay-lift)*.22) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
}
[data-clay]{background:var(--clay-bg);color:var(--clay-ink)}
/* Bootstrap's own variables mapped onto the kit tokens */
:root,[data-clay]{--bs-body-bg:var(--clay-bg);--bs-body-color:var(--clay-ink);--bs-body-font-family:var(--clay-font);
--bs-emphasis-color:var(--clay-ink);--bs-heading-color:var(--clay-ink);--bs-secondary-color:var(--clay-muted);--bs-tertiary-color:var(--clay-muted);
--bs-border-color:var(--clay-edge);--bs-border-width:var(--clay-bw);--bs-border-radius:calc(var(--clay-radius)*.55);
--bs-border-radius-lg:var(--clay-radius);--bs-border-radius-pill:999px;--bs-focus-ring-color:var(--clay-accent);
--bs-link-color:var(--clay-accent);--bs-link-color-rgb:98,54,232;--bs-link-hover-color:var(--clay-ink);--bs-link-hover-color-rgb:36,26,61}
/* Components */
:where([class*="clay-"]),:where([class*="clay-"])::before,:where([class*="clay-"])::after{box-sizing:border-box}
.clay-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.clay-ico{width:22px;height:22px;flex:none}
.clay-ico--lg{width:28px;height:28px}
/* Fills: put one on any object; components read --clay-fill */
.clay-peach{--clay-fill:var(--clay-peach)}.clay-lilac{--clay-fill:var(--clay-lilac)}.clay-mint{--clay-fill:var(--clay-mint)}
.clay-sky{--clay-fill:var(--clay-sky)}.clay-lemon{--clay-fill:var(--clay-lemon)}
/* Card: a puffy panel that floats on the big drop shadow */
.clay-card{margin:0;padding:1.6rem;border:var(--clay-bw) solid var(--clay-line);border-radius:var(--clay-radius);
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);box-shadow:var(--clay-shadow)}
/* Layout helpers */
.clay-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:18px}
.clay-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:22px}
.clay-stack{display:grid;gap:1.4rem}
/* Cards: Bootstrap .card through its --bs-card-* variables */
.card.clay-card{--bs-card-bg:var(--clay-fill,var(--clay-surface));--bs-card-color:var(--clay-ink);--bs-card-border-color:var(--clay-line);
--bs-card-border-width:var(--clay-bw);--bs-card-border-radius:var(--clay-radius);--bs-card-inner-border-radius:var(--clay-radius);
--bs-card-spacer-x:1.6rem;--bs-card-spacer-y:1.6rem;padding:0}
/* Buttons: Bootstrap .btn with a .btn-clay variant set through --bs-btn-* variables.
Hover lifts the pill, pressing squashes the shadow flat */
.btn-clay{--bs-btn-padding-x:1.5rem;--bs-btn-padding-y:.7rem;--bs-btn-font-family:inherit;--bs-btn-font-size:1rem;--bs-btn-font-weight:800;--bs-btn-line-height:1.2;
--bs-btn-border-width:var(--clay-bw);--bs-btn-border-radius:999px;
--bs-btn-bg:var(--clay-fill,var(--clay-surface));--bs-btn-color:var(--clay-ink);--bs-btn-border-color:var(--clay-edge);
--bs-btn-hover-bg:var(--bs-btn-bg);--bs-btn-hover-color:var(--bs-btn-color);--bs-btn-hover-border-color:var(--bs-btn-border-color);
--bs-btn-active-bg:var(--bs-btn-bg);--bs-btn-active-color:var(--bs-btn-color);--bs-btn-active-border-color:var(--bs-btn-border-color);
--clay-btn-shadow:var(--clay-shadow-sm);
display:inline-flex;align-items:center;justify-content:center;gap:.55rem;min-height:50px;
box-shadow:var(--clay-btn-shadow);transition:transform .18s,box-shadow .18s}
.btn-clay:hover{--clay-btn-shadow:var(--clay-shadow);transform:translateY(-2px)}
.btn-clay:active,.btn-clay:first-child:active{--clay-btn-shadow:var(--clay-press);transform:translateY(2px)}
.btn-clay:focus-visible,.btn-check:focus-visible+.btn-clay{box-shadow:var(--clay-btn-shadow);outline:3px solid var(--clay-accent);outline-offset:4px}
.btn-clay-accent{--clay-fill:var(--clay-accent);--bs-btn-color:var(--clay-on-accent);--bs-btn-border-color:var(--clay-accent)}
.btn-clay.btn-sm{--bs-btn-padding-x:1.05rem;--bs-btn-padding-y:.45rem;--bs-btn-font-size:.88rem;min-height:40px}
/* Icon buttons: a round or squircle .btn-clay with an aria-label */
.btn-clay-icon{--bs-btn-padding-x:0;--bs-btn-padding-y:0;width:58px;height:58px;min-height:0;border-radius:50%}
.btn-clay-icon.btn-clay-sq{border-radius:20px}
.btn-clay-icon.btn-clay-sm{width:44px;height:44px}
.btn-clay-icon.btn-clay-lg{width:72px;height:72px}
@media (prefers-reduced-motion:reduce){.btn-clay{transition:none}.btn-clay:hover,.btn-clay:active,.btn-clay:first-child:active{transform:none}}
/* Blob: a small puffy icon tile that sits on any card */
.clay-blob{display:grid;place-items:center;flex:none;width:52px;height:52px;border:var(--clay-bw) solid var(--clay-line);border-radius:18px;
background:var(--clay-fill,var(--clay-surface));color:var(--clay-ink);box-shadow:var(--clay-shadow-sm)}
.clay-chip{display:inline-flex;align-items:center;gap:.35rem;padding:.3rem .75rem;border:var(--clay-bw) solid var(--clay-line);border-radius:999px;
background:var(--clay-surface);color:var(--clay-ink);font-size:.8rem;font-weight:800;line-height:1.3}
.clay-chip .clay-ico{width:16px;height:16px}
/* Stat cards: coloured clay slabs with a blob icon, a big number and a sentence that explains the change */
.clay-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:24px}
.clay-stat{display:grid;gap:.25rem;align-content:start}
.clay-stat__label{margin:.85rem 0 0;font-size:.9rem;font-weight:800;color:var(--clay-muted)}
.clay-stat__value{margin:0;font-size:2.2rem;font-weight:800;line-height:1.1;letter-spacing:-.01em}
.clay-stat__unit{font-size:1rem;font-weight:700}
.clay-stat__note{margin:0;font-size:.86rem;font-weight:600;color:var(--clay-muted)}
/* Content card: an illustration panel made of clay shapes, a chip, a title and a progress line */
.clay-lesson{display:flex;flex-direction:column;gap:1rem;padding:1rem 1rem 1.4rem}
.clay-art{position:relative;height:170px;overflow:hidden;border-radius:calc(var(--clay-radius)*.75);background:var(--clay-sky);box-shadow:var(--clay-well)}
.clay-art__shape{position:absolute;border-radius:50%;box-shadow:var(--clay-shadow-sm)}
.clay-art__shape--sun{top:22px;right:34px;width:58px;height:58px;background:var(--clay-lemon)}
.clay-art__shape--cloud{top:52px;left:40px;width:140px;height:54px;border-radius:999px;background:var(--clay-surface)}
.clay-art__shape--cloud2{top:30px;left:78px;width:70px;height:60px;background:var(--clay-surface)}
.clay-art__shape--hill{bottom:-70px;left:-20px;width:240px;height:140px;background:var(--clay-mint)}
.clay-art__shape--hill2{bottom:-90px;right:-40px;width:260px;height:150px;background:var(--clay-peach)}
.clay-art__shape--drop{top:116px;width:14px;height:20px;border-radius:50% 50% 50% 50%/60% 60% 40% 40%;background:var(--clay-accent);box-shadow:none}
.clay-lesson__body{display:grid;gap:.55rem;padding:0 .5rem}
.clay-lesson__tag{margin:0}
.clay-lesson__title{margin:0;font-size:1.3rem;font-weight:800;line-height:1.25}
.clay-lesson__text{margin:0;color:var(--clay-muted)}
.clay-lesson__foot{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:1rem;padding:0 .5rem}
.clay-meter{display:grid;gap:.35rem;flex:1;min-width:140px;font-size:.84rem;font-weight:700;color:var(--clay-muted)}
.clay-meter__bar{height:14px;border:var(--clay-bw) solid var(--clay-edge);border-radius:999px;background:var(--clay-bg);box-shadow:var(--clay-well);overflow:hidden}
.clay-meter__fill{display:block;height:100%;width:var(--clay-pct,50%);border-radius:999px;background:var(--clay-accent)}
/* Profile card */
.clay-profile{display:grid;justify-items:center;gap:.4rem;text-align:center}
.clay-avatar{display:grid;place-items:center;width:96px;height:96px;margin-bottom:.6rem;border:var(--clay-bw) solid var(--clay-line);border-radius:50%;
background:var(--clay-fill,var(--clay-lilac));font-size:2rem;font-weight:800;box-shadow:var(--clay-shadow)}
.clay-profile__name{margin:0;font-size:1.3rem;font-weight:800;line-height:1.25}
.clay-profile__role{margin:0;color:var(--clay-muted)}
.clay-profile__stats{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;width:100%;margin:.9rem 0 1.1rem;padding:0;list-style:none}
.clay-profile__stat{display:grid;padding:.6rem .3rem;border-radius:18px;background:var(--clay-fill,var(--clay-bg));box-shadow:var(--clay-well)}
.clay-profile__num{font-size:1.25rem;font-weight:800;line-height:1.2}
.clay-profile__key{font-size:.78rem;font-weight:700;color:var(--clay-muted)}
.clay-profile__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:12px}
/* Pricing pair */
.clay-plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:28px;align-items:start}
.clay-plan{position:relative;display:grid;gap:.9rem}
.clay-plan__badge{position:absolute;top:-14px;right:24px;padding:.3rem .85rem;border:var(--clay-bw) solid var(--clay-accent);border-radius:999px;
background:var(--clay-accent);color:var(--clay-on-accent);font-size:.78rem;font-weight:800;box-shadow:var(--clay-shadow-sm)}
.clay-plan__name{margin:0;font-size:1.15rem;font-weight:800}
.clay-plan__price{margin:0;font-size:2.6rem;font-weight:800;line-height:1}
.clay-plan__per{font-size:1rem;font-weight:700;color:var(--clay-muted)}
.clay-plan__desc{margin:0;color:var(--clay-muted)}
.clay-plan__list{display:grid;gap:.6rem;margin:.2rem 0 .4rem;padding:0;list-style:none}
.clay-plan__item{display:flex;align-items:center;gap:.6rem;font-weight:600}
.clay-plan__tick{display:grid;place-items:center;flex:none;width:26px;height:26px;border-radius:50%;background:var(--clay-fill,var(--clay-surface));box-shadow:var(--clay-shadow-sm)}
.clay-plan__tick .clay-ico{width:16px;height:16px}
.clay-two{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);gap:28px;align-items:start}
@media (max-width:767px){.clay-two{grid-template-columns:1fr}}
.clay-stats--two{grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}
/* Alerts: Bootstrap .alert through --bs-alert-* variables */
.clay-alerts{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,380px),1fr));gap:22px}
.alert.clay-balert{--bs-alert-bg:var(--clay-fill,var(--clay-surface));--bs-alert-color:var(--clay-ink);--bs-alert-border-color:var(--clay-line);--bs-alert-border-radius:calc(var(--clay-radius)*.8);
--bs-alert-padding-x:1.2rem;--bs-alert-padding-y:1.1rem;--bs-alert-margin-bottom:0;display:flex;align-items:flex-start;gap:1rem;border-width:var(--clay-bw);box-shadow:var(--clay-shadow)}
.clay-alert__icon{display:grid;place-items:center;flex:none;width:44px;height:44px;border-radius:15px;background:var(--clay-surface);box-shadow:var(--clay-shadow-sm)}
.clay-alert__title{margin:0;font-weight:800;line-height:1.3}
.clay-alert__text{margin:.15rem 0 0;font-size:.92rem;color:var(--clay-muted)}
/* Toasts: Bootstrap .toast inside a polite live region; the close button uses data-bs-dismiss="toast" */
.clay-btoasts{display:grid;gap:16px;max-width:420px}
.toast.clay-btoast{--bs-toast-max-width:100%;--bs-toast-bg:var(--clay-surface);--bs-toast-color:var(--clay-ink);--bs-toast-border-color:var(--clay-line);--bs-toast-border-width:var(--clay-bw);
--bs-toast-border-radius:24px;--bs-toast-box-shadow:var(--clay-shadow);--bs-toast-font-size:1rem;width:100%}
.clay-btoast .toast-body{display:flex;align-items:center;gap:.9rem;padding:.8rem .8rem .8rem 1rem}
.clay-toast__body{flex:1;min-width:0}
.clay-toast__title{margin:0;font-weight:800;line-height:1.3}
.clay-toast__text{margin:0;font-size:.88rem;color:var(--clay-muted)}
/* Tooltips: Bootstrap's tooltip plugin, restyled through --bs-tooltip-* */
.tooltip{--bs-tooltip-bg:var(--clay-ink);--bs-tooltip-color:var(--clay-bg);--bs-tooltip-border-radius:14px;--bs-tooltip-padding-x:.8rem;--bs-tooltip-padding-y:.5rem;
--bs-tooltip-font-size:.84rem;--bs-tooltip-opacity:1;--bs-tooltip-max-width:220px;--bs-tooltip-arrow-width:14px;--bs-tooltip-arrow-height:7px;font-family:var(--clay-font);font-weight:700}
/* Dropdown: Bootstrap .dropdown-menu as a floating clay menu */
.clay-bdd .dropdown-toggle::after{display:none}
.clay-bdd .clay-dd__chev{transition:transform .18s}
.clay-bdd [aria-expanded="true"] .clay-dd__chev{transform:rotate(180deg)}
.clay-bdd .dropdown-menu{--bs-dropdown-min-width:250px;--bs-dropdown-padding-x:.5rem;--bs-dropdown-padding-y:.5rem;--bs-dropdown-bg:var(--clay-surface);--bs-dropdown-color:var(--clay-ink);
--bs-dropdown-border-color:var(--clay-line);--bs-dropdown-border-width:var(--clay-bw);--bs-dropdown-border-radius:22px;--bs-dropdown-box-shadow:var(--clay-shadow);--bs-dropdown-spacer:12px;
--bs-dropdown-link-color:var(--clay-ink);--bs-dropdown-link-hover-color:var(--clay-ink);--bs-dropdown-link-hover-bg:var(--clay-bg);--bs-dropdown-link-active-color:var(--clay-ink);--bs-dropdown-link-active-bg:var(--clay-bg);
--bs-dropdown-item-padding-x:.7rem;--bs-dropdown-item-padding-y:.45rem;--bs-dropdown-item-border-radius:15px;--bs-dropdown-divider-bg:var(--clay-line);--bs-dropdown-divider-margin-y:.35rem;box-shadow:var(--bs-dropdown-box-shadow)}
.clay-bdd .dropdown-item{display:flex;align-items:center;gap:.7rem;min-height:46px;font-size:.95rem;font-weight:700}
.clay-bdd .dropdown-item:focus-visible{outline:3px solid var(--clay-accent);outline-offset:-3px}
.clay-bdd .dropdown-item.clay-dd__item--danger{color:var(--clay-danger)}
.clay-bdd .dropdown-divider{margin-left:.5rem;margin-right:.5rem;border-top-width:var(--clay-bw);opacity:1}
/* Modal: Bootstrap .modal; focus is kept inside and Escape closes it */
.modal.clay-bmodal{--bs-modal-width:440px;--bs-modal-padding:1.8rem;--bs-modal-bg:var(--clay-surface);--bs-modal-color:var(--clay-ink);--bs-modal-border-color:var(--clay-line);
--bs-modal-border-width:var(--clay-bw);--bs-modal-border-radius:var(--clay-radius);--bs-modal-box-shadow:var(--clay-shadow);--bs-backdrop-bg:rgb(36,26,61);--bs-backdrop-opacity:.45}
.clay-bmodal .modal-content{box-shadow:var(--bs-modal-box-shadow);text-align:center}
.clay-dialog__icon{display:grid;place-items:center;width:70px;height:70px;margin:0 auto 1rem;border-radius:24px;background:var(--clay-peach);box-shadow:var(--clay-shadow-sm)}
.clay-dialog__title{margin:0;font-size:1.35rem;font-weight:800;line-height:1.25}
.clay-dialog__text{margin:.5rem 0 1.4rem;color:var(--clay-muted)}
.clay-dialog__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin:0}
.clay-row{display:flex;flex-wrap:wrap;align-items:flex-start;gap:56px}
.clay-row>section{flex:1 1 280px;min-width:0}
@media (prefers-reduced-motion:reduce){.clay-bdd .clay-dd__chev{transition:none}}
JavaScript
// Tooltips: Bootstrap's plugin is opt-in, so start it on every element that asks for one
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(function (el) { new bootstrap.Tooltip(el); });
/* ── Claymorphism UI kit tokens (uiCookies) — identical in every set ──────────────
Every component is a puffy pastel object floating above --clay-bg: one big soft drop shadow
below it, an inset highlight top left and an inset shade bottom right. Change a token and
the whole kit follows; the shadow recipes below are built from these values. */
:root{
--clay-bg:#f3effa; /* page, a pale lilac white */
--clay-surface:#fffcf8; /* cards, fields and plain objects */
--clay-peach:#ffd2bd; /* object fills: ink text on every fill reaches 11:1 */
--clay-lilac:#dccdff;
--clay-mint:#bdeed6;
--clay-sky:#c2e0ff;
--clay-lemon:#ffe9a3;
--clay-ink:#241a3d; /* text, 14.4:1 on --clay-bg */
--clay-muted:#51476a; /* secondary text, 5.7:1 or more on every fill */
--clay-edge:#7a6e96; /* control boundary, 4.1:1 on --clay-bg, so shadows are never the only edge */
--clay-line:rgba(36,26,61,.12); /* decorative edge on cards and panels */
--clay-accent:#6236e8; /* focus ring and on / selected / current states, 5.8:1 on --clay-bg */
--clay-on-accent:#ffffff; /* text and marks on accent fills, 6.5:1 */
--clay-tint:#e9e1ff; /* light accent wash behind current items */
--clay-drop:rgba(86,56,168,.30); /* colour of the outer drop shadow */
--clay-hi:rgba(255,255,255,.88); /* inset highlight, top left */
--clay-lo:rgba(52,30,112,.20); /* inset shade, bottom right */
--clay-lift:14px; /* how high objects float; every shadow scales from it */
--clay-radius:30px; /* cards; buttons are pills, fields use .55 of it */
--clay-bw:1.5px; /* control border width */
--clay-success:#17663d;--clay-warning:#8a4b00;--clay-danger:#b3261e;
--clay-font:ui-rounded,"SF Pro Rounded","Nunito","Quicksand","Varela Round","Arial Rounded MT Bold",system-ui,sans-serif;
}
/* --clay-fill is set by .clay-peach, .clay-mint… on one object and must not leak into its children */
@property --clay-fill{syntax:"*";inherits:false}
/* Dark clay: data-clay="dark" on any wrapper, or a checked .clay-theme-input inside a [data-clay] element.
:is() is a forgiving list, so browsers without :has() still apply data-clay="dark" */
[data-clay]:is([data-clay="dark"],:has(.clay-theme-input:checked)){
--clay-bg:#1a1530;--clay-surface:#2a2347;
--clay-peach:#6e3f33;--clay-lilac:#4a3a8a;--clay-mint:#215a4a;--clay-sky:#26497c;--clay-lemon:#5f5220;
--clay-ink:#f7f3ff;--clay-muted:#d2c9ea;--clay-edge:#8f83b8;--clay-line:rgba(255,255,255,.1);
--clay-accent:#bba6ff;--clay-on-accent:#1a1530;--clay-tint:#3a2f66;
--clay-drop:rgba(4,2,14,.62);--clay-hi:rgba(255,255,255,.2);--clay-lo:rgba(0,0,0,.34);
--clay-success:#86e3b0;--clay-warning:#ffc76e;--clay-danger:#ffa196;color-scheme:dark}
/* More contrast requested: soft shading stays, but every edge and secondary text turns full ink */
@media (prefers-contrast:more){:root,[data-clay]:is([data-clay],:has(.clay-theme-input:checked)){
--clay-edge:var(--clay-ink);--clay-line:var(--clay-ink);--clay-muted:var(--clay-ink);--clay-bw:2px}}
:root,[data-clay]{
--clay-shadow:0 var(--clay-lift) calc(var(--clay-lift)*2.2) calc(var(--clay-lift)*-.3) var(--clay-drop),
inset calc(var(--clay-lift)*.45) calc(var(--clay-lift)*.5) calc(var(--clay-lift)*.9) var(--clay-hi),
inset calc(var(--clay-lift)*-.45) calc(var(--clay-lift)*-.6) calc(var(--clay-lift)*.9) var(--clay-lo);
--clay-shadow-sm:0 calc(var(--clay-lift)*.55) calc(var(--clay-lift)*1.2) calc(var(--clay-lift)*-.15) var(--clay-drop),
inset calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.3) calc(var(--clay-lift)*.5) var(--clay-hi),
inset calc(var(--clay-lift)*-.25) calc(var(--clay-lift)*-.35) calc(var(--clay-lift)*.5) var(--clay-lo);
--clay-press:0 calc(var(--clay-lift)*.12) calc(var(--clay-lift)*.25) 0 var(--clay-drop),
inset calc(var(--clay-lift)*.2) calc(var(--clay-lift)*.25) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
--clay-well:inset calc(var(--clay-lift)*.15) calc(var(--clay-lift)*.22) calc(var(--clay-lift)*.45) var(--clay-lo),
inset calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*-.12) calc(var(--clay-lift)*.3) var(--clay-hi);
}
[data-clay]{background:var(--clay-bg);color:var(--clay-ink)}
/* The tokens above, exposed as Tailwind utilities: bg-clay-peach, text-clay-muted, border-clay-edge,
shadow-clay, shadow-clay-sm, shadow-clay-press, shadow-clay-well, rounded-clay, rounded-clay-field, font-clay… */
@theme inline {
--color-clay-bg: var(--clay-bg); --color-clay-surface: var(--clay-surface);
--color-clay-peach: var(--clay-peach); --color-clay-lilac: var(--clay-lilac); --color-clay-mint: var(--clay-mint);
--color-clay-sky: var(--clay-sky); --color-clay-lemon: var(--clay-lemon);
--color-clay-ink: var(--clay-ink); --color-clay-muted: var(--clay-muted); --color-clay-edge: var(--clay-edge); --color-clay-line: var(--clay-line);
--color-clay-accent: var(--clay-accent); --color-clay-on-accent: var(--clay-on-accent); --color-clay-tint: var(--clay-tint);
--color-clay-success: var(--clay-success); --color-clay-warning: var(--clay-warning); --color-clay-danger: var(--clay-danger);
--shadow-clay: var(--clay-shadow); --shadow-clay-sm: var(--clay-shadow-sm); --shadow-clay-press: var(--clay-press); --shadow-clay-well: var(--clay-well);
--radius-clay: var(--clay-radius); --radius-clay-field: calc(var(--clay-radius) * .55); --radius-clay-sm: calc(var(--clay-radius) * .4);
--font-clay: var(--clay-font);
}
@keyframes clay-pop{from{transform:scale(.92);opacity:0}}
JavaScript
// Dialog: any button with data-clay-open="dialog-id" opens that <dialog>; focus returns to the button on close
document.querySelectorAll('[data-clay-open]').forEach(function (btn) {
var dialog = document.getElementById(btn.getAttribute('data-clay-open'));
if (!dialog) return;
btn.addEventListener('click', function () {
dialog.showModal();
dialog.addEventListener('close', function () { btn.focus(); }, { once: true });
});
dialog.addEventListener('click', function (e) { if (e.target === dialog) dialog.close(); }); // backdrop click
});
// Tooltips: Escape hides the visible tooltip until the pointer or focus leaves it
document.addEventListener('keydown', function (e) {
if (e.key !== 'Escape') return;
document.querySelectorAll('[data-clay-tip]').forEach(function (t) { if (t.matches(':hover, :focus-within')) t.classList.add('is-dismissed'); });
});
document.querySelectorAll('[data-clay-tip]').forEach(function (t) {
['mouseleave', 'focusout'].forEach(function (ev) { t.addEventListener(ev, function () { t.classList.remove('is-dismissed'); }); });
});
// Toasts: a close button hides its toast
document.querySelectorAll('[data-clay-dismiss]').forEach(function (b) {
b.addEventListener('click', function () { b.closest('[data-clay-toast]').hidden = true; });
});
// Dropdown: toggle on click; Escape (focus back to the button) or a click outside closes it
document.querySelectorAll('[data-clay-dropdown]').forEach(function (btn) {
var menu = document.getElementById(btn.getAttribute('aria-controls'));
function set(open) { btn.setAttribute('aria-expanded', open); menu.hidden = !open; }
btn.addEventListener('click', function () { set(btn.getAttribute('aria-expanded') !== 'true'); });
document.addEventListener('click', function (e) { if (!btn.parentNode.contains(e.target)) set(false); });
btn.parentNode.addEventListener('keydown', function (e) { if (e.key === 'Escape' && btn.getAttribute('aria-expanded') === 'true') { set(false); btn.focus(); } });
});
About this set
Feedback and overlay patterns in soft clay. Four alerts in sky, mint, lemon and peach cover a tip, a saved message, a screen-time warning and a failed upload; each names its type in a title, an icon and hidden text such as Warning, so colour is never the only signal, and the error uses role=”alert”. Two toasts, a badge unlocked and a lesson saved for offline, sit in a polite live region and each has a round close button. Three round icon buttons show tooltips on hover and keyboard focus that Escape dismisses. A Lesson options button opens a floating clay menu with save, share, copy and report actions, and closes on Escape or a click outside. A Leave lesson button opens a confirmation dialog with a flag badge, a short explanation and Leave for now and Keep learning buttons. In the plain CSS and Tailwind versions the dialog is the native dialog element opened with showModal(), so focus is trapped, Escape closes it and focus returns to the button; the Bootstrap version uses its modal, toast, tooltip and dropdown plugins.
What’s included
Alerts that name their type in words, an icon and hidden text
Toasts in a polite live region with close buttons
Tooltips on hover and focus, dismissed with Escape
Dropdown menu with aria-expanded, Escape and outside-click closing
Confirmation dialog on the native dialog element
Accessibility
The dialog is labelled and described by its heading and text, traps focus and returns it on close. Tooltips are tied to their buttons with aria-describedby and can be dismissed with Escape, the dropdown button reports aria-expanded, and toasts are announced through a polite live region. The dialog pop-in animation and every transition stop under prefers-reduced-motion.
Customise it
Alert colours come from the fill classes and the tooltip uses --clay-ink on --clay-bg, so it inverts in dark mode. Bootstrap maps the tokens onto --bs-alert-*, --bs-toast-*, --bs-tooltip-*, --bs-dropdown-* and --bs-modal-* variables; Tailwind uses bg-clay-*, shadow-clay and backdrop: utilities.