/* ── Neobrutalism UI kit tokens (uiCookies) — identical in every set ─────────────
Thick ink borders, hard offset shadows and flat saturated fills on cream paper.
Change a token and the whole kit follows; the border and shadow recipes below are built from them. */
:root{
--nb-paper:#fff6e0; /* page background, cream */
--nb-surface:#ffffff; /* card, field and button faces */
--nb-ink:#111111; /* text, 17.5:1 on paper */
--nb-muted:#454036; /* secondary text, 9.6:1 on paper */
--nb-line:#111111; /* border colour */
--nb-bw:3px; /* border width */
--nb-shadow-x:4px; /* hard shadow offset; pressed controls move this far into it */
--nb-shadow-y:4px;
--nb-shadow-color:#111111;
--nb-radius:12px; /* cards and panels */
--nb-radius-sm:8px; /* buttons and fields */
--nb-focus:#1d4ed8; /* dashed focus outline, 6.2:1 on paper, never the shadow */
--nb-on-fill:#111111; /* text on every palette fill below, 6.8:1 or more */
--nb-on-fill-muted:#2b2822;/* secondary text on fills, 5.2:1 or more */
--nb-lemon:#ffe14d;--nb-pink:#ff90d0;--nb-cyan:#62e2e8;--nb-lime:#b6f36a;
--nb-orange:#ffa94d;--nb-violet:#b9a3ff;--nb-red:#ff6b6b;
--nb-blue:#2448e0;--nb-on-blue:#ffffff; /* the only fill that takes white text, 6.8:1 */
--nb-danger:#b42318; /* destructive text, 6.1:1 on paper */
--nb-font:"Space Grotesk","Archivo","Helvetica Neue",Arial,system-ui,sans-serif;
--nb-font-display:"Archivo Black","Arial Black","Helvetica Neue",Arial,sans-serif;
}
/* Dark brutalism: data-nb="dark" on any wrapper, or a checked .nb-theme-input inside a [data-nb] element.
:is() is a forgiving list, so browsers without :has() still apply data-nb="dark". Fills keep dark text. */
[data-nb]:is([data-nb="dark"],:has(.nb-theme-input:checked)){
--nb-paper:#17150f;--nb-surface:#25221b;--nb-ink:#fbf3dc;--nb-muted:#d6ccb2;--nb-line:#fbf3dc;
--nb-shadow-color:#fbf3dc;--nb-focus:#8db4ff;--nb-danger:#ff9b8f;color-scheme:dark}
/* More contrast requested: secondary text turns full ink */
@media (prefers-contrast:more){:root,[data-nb]:is([data-nb],:has(.nb-theme-input:checked)){--nb-muted:var(--nb-ink);--nb-on-fill-muted:var(--nb-on-fill)}}
:root,[data-nb]{
--nb-border:var(--nb-bw) solid var(--nb-line);
--nb-shadow:var(--nb-shadow-x) var(--nb-shadow-y) 0 0 var(--nb-shadow-color);
--nb-shadow-sm:calc(var(--nb-shadow-x)*.5) calc(var(--nb-shadow-y)*.5) 0 0 var(--nb-shadow-color);
--nb-shadow-lg:calc(var(--nb-shadow-x)*1.75) calc(var(--nb-shadow-y)*1.75) 0 0 var(--nb-shadow-color);
--nb-shadow-hover:calc(var(--nb-shadow-x) + 2px) calc(var(--nb-shadow-y) + 2px) 0 0 var(--nb-shadow-color);
--nb-press:var(--nb-shadow-x) var(--nb-shadow-y); /* translate for a pressed control */
--nb-press-sm:calc(var(--nb-shadow-x)*.5) calc(var(--nb-shadow-y)*.5); /* …for one with the small shadow */
}
[data-nb]{background:var(--nb-paper);color:var(--nb-ink);font-family:var(--nb-font)}
/* Components */
:where([class*="nb-"]),:where([class*="nb-"])::before,:where([class*="nb-"])::after{box-sizing:border-box}
.nb-sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.nb-ico{width:20px;height:20px;flex:none}
.nb-display{font-family:var(--nb-font-display);font-weight:900;letter-spacing:-.01em;line-height:1.05}
.nb-link{color:inherit;font-weight:700;text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:3px}
.nb-link:hover{text-decoration-thickness:4px}
.nb-link:focus-visible{outline:3px dashed var(--nb-focus);outline-offset:3px;border-radius:2px}
/* Buttons: a face, a thick border and a hard shadow. Hover lifts it, pressing moves it into its shadow */
.nb-btn{--nb-fill:var(--nb-surface);--nb-text:var(--nb-ink);
display:inline-flex;align-items:center;justify-content:center;gap:.5rem;min-height:46px;padding:.6rem 1.2rem;
border:var(--nb-border);border-radius:var(--nb-radius-sm);background:var(--nb-fill);color:var(--nb-text);
font:700 1rem/1.2 var(--nb-font);text-decoration:none;cursor:pointer;box-shadow:var(--nb-shadow);
transition:translate .12s,box-shadow .12s}
.nb-btn:hover{translate:-2px -2px;box-shadow:var(--nb-shadow-hover)}
.nb-btn:active{translate:var(--nb-press);box-shadow:none}
.nb-btn:focus-visible{outline:3px dashed var(--nb-focus);outline-offset:5px}
.nb-btn--lemon{--nb-fill:var(--nb-lemon);--nb-text:var(--nb-on-fill)}
.nb-btn--pink{--nb-fill:var(--nb-pink);--nb-text:var(--nb-on-fill)}
.nb-btn--cyan{--nb-fill:var(--nb-cyan);--nb-text:var(--nb-on-fill)}
.nb-btn--lime{--nb-fill:var(--nb-lime);--nb-text:var(--nb-on-fill)}
.nb-btn--red{--nb-fill:var(--nb-red);--nb-text:var(--nb-on-fill)}
.nb-btn--blue{--nb-fill:var(--nb-blue);--nb-text:var(--nb-on-blue)}
.nb-btn--ink{--nb-fill:var(--nb-ink);--nb-text:var(--nb-paper)}
.nb-btn--sm{min-height:38px;padding:.4rem .85rem;font-size:.875rem}
.nb-btn--lg{min-height:56px;padding:.8rem 1.6rem;font-size:1.15rem}
.nb-btn--block{width:100%}
.nb-btn--icon{width:48px;height:48px;min-height:0;padding:0}
.nb-btn--icon-sm{width:38px;height:38px;box-shadow:var(--nb-shadow-sm)}
.nb-btn--icon-sm:hover{box-shadow:var(--nb-shadow)}
.nb-btn--icon-sm:active{translate:var(--nb-press-sm)}
.nb-btn--round{border-radius:50%}
.nb-btn:disabled{--nb-fill:var(--nb-paper);--nb-text:var(--nb-muted);border-style:dashed;box-shadow:none;translate:none;cursor:not-allowed}
/* Icon toggle: a checkbox behind a round face. On = pressed into its shadow, pink, and the outline icon turns solid */
.nb-itoggle{position:relative;display:inline-flex}
.nb-itoggle__face{display:grid;place-items:center;width:48px;height:48px;border:var(--nb-border);border-radius:50%;background:var(--nb-surface);color:var(--nb-ink);
box-shadow:var(--nb-shadow);cursor:pointer;transition:translate .12s,box-shadow .12s}
.nb-itoggle__face .nb-ico{width:22px;height:22px}
.nb-itoggle__face .nb-ico+.nb-ico,.nb-itoggle input:checked+.nb-itoggle__face .nb-ico:first-child{display:none}
.nb-itoggle input:checked+.nb-itoggle__face{translate:var(--nb-press);box-shadow:none;background:var(--nb-pink);color:var(--nb-on-fill)}
.nb-itoggle input:checked+.nb-itoggle__face .nb-ico+.nb-ico{display:block}
.nb-itoggle input:focus-visible+.nb-itoggle__face{outline:3px dashed var(--nb-focus);outline-offset:5px}
/* Toggle tiles: a visually hidden checkbox and a face. On = pushed into the shadow, lime fill, a check and the word On */
.nb-toggle{position:relative;display:flex}
.nb-toggle__face{display:flex;flex-direction:column;align-items:flex-start;gap:.45rem;width:100%;min-width:140px;padding:1rem;
border:var(--nb-border);border-radius:var(--nb-radius);background:var(--nb-surface);color:var(--nb-ink);box-shadow:var(--nb-shadow);
cursor:pointer;transition:translate .12s,box-shadow .12s}
.nb-toggle__icon{display:grid;place-items:center;width:42px;height:42px;margin-bottom:.2rem;border:var(--nb-border);border-radius:50%;background:var(--nb-paper)}
.nb-toggle__name{font-weight:800;line-height:1.25}
.nb-toggle__state{display:inline-flex;align-items:center;gap:.3rem;padding:.12rem .5rem;border:2px solid currentColor;border-radius:999px;
font-size:.72rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}
.nb-toggle__state::before{content:"";width:8px;height:8px;border:2px solid currentColor;border-radius:50%}
.nb-toggle__state::after{content:"Off"}
.nb-toggle input:checked+.nb-toggle__face{translate:var(--nb-press);box-shadow:none;background:var(--nb-lime);color:var(--nb-on-fill)}
.nb-toggle input:checked+.nb-toggle__face .nb-toggle__icon{border-color:var(--nb-on-fill);background:var(--nb-on-fill);color:var(--nb-lime)}
.nb-toggle input:checked+.nb-toggle__face .nb-toggle__state::before{width:10px;height:6px;border-width:0 0 2px 2px;border-radius:0;rotate:-45deg;translate:0 -1px}
.nb-toggle input:checked+.nb-toggle__face .nb-toggle__state::after{content:"On"}
.nb-toggle input:focus-visible+.nb-toggle__face{outline:3px dashed var(--nb-focus);outline-offset:5px}
/* Switches: a checkbox with role="switch". On = the square knob slides right, the track fills lime and the label says On */
.nb-switch{position:relative;display:flex;align-items:center;gap:.8rem;cursor:pointer}
.nb-switch__text{flex:1;min-width:0;font-weight:800;line-height:1.3}
.nb-switch__text small{display:block;margin-top:.1rem;font-size:.85rem;font-weight:500;color:var(--nb-muted)}
.nb-switch__state{min-width:2.2em;font-size:.75rem;font-weight:800;letter-spacing:.08em;text-align:right;text-transform:uppercase}
.nb-switch__state::after{content:"Off"}
.nb-switch__track{position:relative;flex:none;width:70px;height:36px;border:var(--nb-border);border-radius:var(--nb-radius-sm);background:var(--nb-paper);
box-shadow:var(--nb-shadow-sm);transition:background-color .12s}
.nb-switch__track::before{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border:var(--nb-border);border-radius:4px;background:var(--nb-surface);transition:translate .15s}
.nb-switch input:checked~.nb-switch__track{background:var(--nb-lime)}
.nb-switch input:checked~.nb-switch__track::before{translate:34px 0;border-color:var(--nb-on-fill);background:var(--nb-on-fill)}
.nb-switch input:checked~.nb-switch__state::after{content:"On"}
.nb-switch input:focus-visible~.nb-switch__track{outline:3px dashed var(--nb-focus);outline-offset:4px}
/* Segmented control: radios in a fieldset. The chosen option fills lemon and gains a check */
.nb-seg{min-width:0;margin:0;padding:0;border:0}
.nb-seg__legend{margin-bottom:.55rem;padding:0;font-weight:800}
.nb-seg__opts{display:flex;flex-wrap:wrap;width:fit-content;max-width:100%;border:var(--nb-border);border-radius:var(--nb-radius-sm);background:var(--nb-surface);box-shadow:var(--nb-shadow);overflow:hidden}
.nb-seg__opt{position:relative;flex:1 1 auto}
.nb-seg__opt+.nb-seg__opt{border-left:var(--nb-border)}
.nb-seg__opt span{display:flex;align-items:center;justify-content:center;gap:.4rem;min-height:44px;padding:.45rem 1rem;color:var(--nb-ink);font-weight:700;cursor:pointer}
.nb-seg__opt input:checked+span{background:var(--nb-lemon);color:var(--nb-on-fill)}
.nb-seg__opt input:checked+span::before{content:"";width:12px;height:7px;border:solid currentColor;border-width:0 0 3px 3px;rotate:-45deg;translate:0 -2px}
.nb-seg__opt input:focus-visible+span{outline:3px dashed var(--nb-focus);outline-offset:-7px}
/* Card: a white panel, the thick border and the large hard shadow. Fill variants keep dark text */
.nb-card{position:relative;padding:1.4rem;border:var(--nb-border);border-radius:var(--nb-radius);background:var(--nb-surface);color:var(--nb-ink);box-shadow:var(--nb-shadow-lg);font-family:var(--nb-font)}
.nb-card h2,.nb-card h3,.nb-card p{margin:0}
.nb-card--lemon,.nb-card--pink,.nb-card--cyan,.nb-card--lime,.nb-card--orange,.nb-card--violet{--nb-muted:var(--nb-on-fill-muted);color:var(--nb-on-fill)}
.nb-card--lemon{background:var(--nb-lemon)}.nb-card--pink{background:var(--nb-pink)}.nb-card--cyan{background:var(--nb-cyan)}
.nb-card--lime{background:var(--nb-lime)}.nb-card--orange{background:var(--nb-orange)}.nb-card--violet{background:var(--nb-violet)}
/* Stickers: tilted labels that sit on or over a card */
.nb-sticker{display:inline-flex;align-items:center;gap:.35rem;width:max-content;padding:.3rem .7rem;border:var(--nb-border);border-radius:999px;
background:var(--nb-lemon);color:var(--nb-on-fill);font:800 .75rem/1.1 var(--nb-font);letter-spacing:.06em;text-transform:uppercase;box-shadow:var(--nb-shadow-sm);rotate:-4deg}
.nb-sticker .nb-ico{width:14px;height:14px}
.nb-sticker--pink{background:var(--nb-pink)}.nb-sticker--cyan{background:var(--nb-cyan)}.nb-sticker--lime{background:var(--nb-lime)}
.nb-sticker--orange{background:var(--nb-orange)}.nb-sticker--violet{background:var(--nb-violet)}
.nb-sticker--right{rotate:5deg}
.nb-sticker--corner{position:absolute;top:-16px;right:14px;z-index:2}
.nb-sticker--square{border-radius:4px}
/* Layout helpers */
.nb-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:18px}
.nb-tiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:20px}
.nb-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));gap:28px;align-items:start}
.nb-stack{display:grid;gap:1.25rem}
/* Fields: a white well with the thick border and a small shadow; focus adds a dashed outline */
.nb-field{display:grid;gap:.4rem;min-width:0}
.nb-label{font-weight:800;font-size:.95rem}
.nb-input,.nb-select,.nb-textarea{width:100%;min-height:48px;padding:.65rem .9rem;border:var(--nb-border);border-radius:var(--nb-radius-sm);background:var(--nb-surface);color:var(--nb-ink);
font:500 1rem/1.4 var(--nb-font);box-shadow:var(--nb-shadow-sm);transition:box-shadow .12s}
.nb-textarea{min-height:110px;resize:vertical}
.nb-input::placeholder,.nb-textarea::placeholder{color:var(--nb-muted);opacity:1}
.nb-input:focus,.nb-select:focus,.nb-textarea:focus{outline:3px dashed var(--nb-focus);outline-offset:4px;box-shadow:var(--nb-shadow)}
.nb-hint{font-size:.85rem;color:var(--nb-muted)}
.nb-row{display:flex;justify-content:space-between;align-items:baseline;gap:1rem}
.nb-control{position:relative}
.nb-control>.nb-ico{position:absolute;top:50%;left:14px;translate:0 -50%;pointer-events:none}
.nb-control>.nb-ico~.nb-input{padding-left:2.75rem}
.nb-select{appearance:none;padding-right:2.75rem;cursor:pointer}
.nb-control--select::after{content:"";position:absolute;top:50%;right:18px;width:10px;height:10px;border:solid var(--nb-ink);border-width:0 3px 3px 0;translate:0 -70%;rotate:45deg;pointer-events:none}
.nb-input[aria-invalid="true"]{box-shadow:var(--nb-shadow-x) var(--nb-shadow-y) 0 0 var(--nb-red)}
.nb-error{display:flex;align-items:center;gap:.45rem;width:fit-content;max-width:100%;padding:.3rem .65rem;border:2px solid var(--nb-line);border-radius:6px;background:var(--nb-red);color:var(--nb-on-fill);font-size:.85rem;font-weight:700}
.nb-error .nb-ico{width:16px;height:16px}
/* Checkboxes and radios: a hidden native input and a box that is pushed into its shadow and filled when chosen */
.nb-fieldset{min-width:0;margin:0;padding:0;border:0}
.nb-fieldset legend{margin-bottom:.6rem;padding:0;font-weight:800}
.nb-choices{display:grid;gap:.8rem}
.nb-check{position:relative;display:flex;align-items:center;gap:.75rem;font-weight:600;cursor:pointer}
.nb-check__box{display:grid;place-items:center;flex:none;width:28px;height:28px;border:var(--nb-border);border-radius:6px;background:var(--nb-surface);color:transparent;
box-shadow:var(--nb-shadow-sm);transition:translate .12s,box-shadow .12s}
.nb-check__box .nb-ico{width:18px;height:18px}
.nb-check--radio .nb-check__box{border-radius:50%}
.nb-check--radio .nb-check__box::after{content:"";width:12px;height:12px;border-radius:50%;background:transparent}
.nb-check input:checked+.nb-check__box{translate:var(--nb-press-sm);box-shadow:none;background:var(--nb-lemon);color:var(--nb-on-fill)}
.nb-check--radio input:checked+.nb-check__box::after{background:var(--nb-on-fill)}
.nb-check input:focus-visible+.nb-check__box{outline:3px dashed var(--nb-focus);outline-offset:4px}
/* Stat cards: a label, a big figure, and the change written in words beside its arrow */
.nb-stat{display:grid;gap:.35rem}
.nb-stat__top{display:flex;align-items:center;justify-content:space-between;gap:.75rem}
.nb-stat__label{font-size:.8rem;font-weight:800;letter-spacing:.08em;text-transform:uppercase}
.nb-stat__icon{display:grid;place-items:center;width:40px;height:40px;border:var(--nb-bw) solid currentColor;border-radius:50%}
.nb-stat__value{font-size:2.3rem}
.nb-stat__delta{display:flex;align-items:center;gap:.35rem;font-size:.88rem;font-weight:700;color:var(--nb-muted)}
.nb-stat__delta .nb-ico{width:16px;height:16px}
/* Alerts: the type is named by an icon, a bold title and hidden text, never by colour alone */
.nb-alert{display:flex;align-items:flex-start;gap:.85rem;padding:1rem 1.1rem;border:var(--nb-border);border-radius:var(--nb-radius);background:var(--nb-tone,var(--nb-cyan));color:var(--nb-on-fill);box-shadow:var(--nb-shadow);font-family:var(--nb-font)}
.nb-alert--success{--nb-tone:var(--nb-lime)}.nb-alert--warning{--nb-tone:var(--nb-lemon)}.nb-alert--error{--nb-tone:var(--nb-red)}
.nb-alert__icon{display:grid;place-items:center;flex:none;width:38px;height:38px;border-radius:50%;background:var(--nb-on-fill);color:var(--nb-tone,var(--nb-cyan))}
.nb-alert__body{flex:1;min-width:0}
.nb-alert__title{margin:0;font-weight:800}
.nb-alert__body p+p{margin:.15rem 0 0}
.nb-alert a{color:inherit;font-weight:800;text-decoration:underline;text-decoration-thickness:2px;text-underline-offset:3px}
.nb-alert a:focus-visible{outline:3px dashed var(--nb-on-fill);outline-offset:3px}
/* Progress: labelled bars with a hatched fill and the value written out */
.nb-progress{display:grid;gap:.45rem}
.nb-progress__top{display:flex;justify-content:space-between;gap:1rem;font-weight:800}
.nb-progress__top span{font-variant-numeric:tabular-nums}
.nb-bar{height:24px;border:var(--nb-border);border-radius:999px;background:var(--nb-surface);overflow:hidden}
.nb-bar__fill{display:block;width:var(--p);height:100%;border-right:var(--nb-border);background:repeating-linear-gradient(-45deg,transparent 0 8px,rgba(17,17,17,.22) 8px 11px),var(--nb-tone,var(--nb-lime))}
.nb-bar--pink{--nb-tone:var(--nb-pink)}.nb-bar--cyan{--nb-tone:var(--nb-cyan)}.nb-bar--lemon{--nb-tone:var(--nb-lemon)}
.nb-steps{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin:0;padding:0;list-style:none;counter-reset:nb-step}
.nb-steps li{display:grid;gap:.35rem;font-size:.8rem;font-weight:800}
.nb-steps li::before{content:"";height:14px;border:var(--nb-border);border-radius:4px;background:var(--nb-surface)}
.nb-steps li.is-done::before{background:var(--nb-lime)}
.nb-steps li[aria-current="step"]::before{background:repeating-linear-gradient(-45deg,var(--nb-lemon) 0 6px,var(--nb-surface) 6px 10px)}
/* Music player: a range seek bar, a checkbox play/pause, and shuffle and repeat toggles that press in and gain a dot */
.nb-player{max-width:380px}
.nb-player__art{position:relative;height:190px;margin-bottom:1.1rem;border:var(--nb-border);border-radius:var(--nb-radius-sm);overflow:hidden;background:var(--nb-violet)}
.nb-player__art svg{display:block;width:100%;height:100%}
.nb-player__art .nb-sticker{position:absolute;right:12px;bottom:12px}
.nb-player__title{font-size:1.4rem}
.nb-player__artist{margin-top:.2rem!important;font-weight:600;color:var(--nb-muted)}
.nb-times{display:flex;justify-content:space-between;margin-top:.15rem;font-size:.85rem;font-weight:700;font-variant-numeric:tabular-nums}
.nb-seek{margin-top:1rem}
.nb-controls{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-top:1rem}
.nb-play .nb-itoggle__face{width:62px;height:62px;background:var(--nb-lemon);color:var(--nb-on-fill)}
.nb-play .nb-itoggle__face .nb-ico{width:26px;height:26px}
.nb-play input:checked+.nb-itoggle__face{background:var(--nb-lemon)}
.nb-mini{position:relative;display:inline-flex}
.nb-mini__face{position:relative;display:grid;place-items:center;width:42px;height:42px;border:var(--nb-bw) solid transparent;border-radius:var(--nb-radius-sm);color:var(--nb-ink);cursor:pointer}
.nb-mini__face:hover{border-color:var(--nb-line)}
.nb-mini input:checked+.nb-mini__face{border-color:var(--nb-line);background:var(--nb-cyan);color:var(--nb-on-fill)}
.nb-mini input:checked+.nb-mini__face::after{content:"";position:absolute;bottom:3px;width:5px;height:5px;border-radius:50%;background:currentColor}
.nb-mini input:focus-visible+.nb-mini__face{outline:3px dashed var(--nb-focus);outline-offset:3px}
/* Now playing bar: small art, the track, a thin progress bar and the play toggle */
.nb-np{display:grid;grid-template-columns:72px minmax(0,1fr) auto;align-items:center;gap:1rem}
.nb-np__art{width:72px;height:72px;border:var(--nb-border);border-radius:var(--nb-radius-sm);overflow:hidden;background:var(--nb-violet)}
.nb-np__art svg{display:block;width:100%;height:100%}
.nb-np__title{font-weight:800;line-height:1.25}
.nb-np__artist{font-size:.88rem;color:var(--nb-muted)}
.nb-np .nb-bar{height:14px;margin-top:.5rem}
.nb-np .nb-play .nb-itoggle__face{width:54px;height:54px}
@media (prefers-reduced-motion:reduce){[class*="nb-"],[class*="nb-"]::before,[class*="nb-"]::after{transition:none!important;animation:none!important}}
/* ── Neobrutalism UI kit tokens (uiCookies) — identical in every set ─────────────
Thick ink borders, hard offset shadows and flat saturated fills on cream paper.
Change a token and the whole kit follows; the border and shadow recipes below are built from them. */
:root{
--nb-paper:#fff6e0; /* page background, cream */
--nb-surface:#ffffff; /* card, field and button faces */
--nb-ink:#111111; /* text, 17.5:1 on paper */
--nb-muted:#454036; /* secondary text, 9.6:1 on paper */
--nb-line:#111111; /* border colour */
--nb-bw:3px; /* border width */
--nb-shadow-x:4px; /* hard shadow offset; pressed controls move this far into it */
--nb-shadow-y:4px;
--nb-shadow-color:#111111;
--nb-radius:12px; /* cards and panels */
--nb-radius-sm:8px; /* buttons and fields */
--nb-focus:#1d4ed8; /* dashed focus outline, 6.2:1 on paper, never the shadow */
--nb-on-fill:#111111; /* text on every palette fill below, 6.8:1 or more */
--nb-on-fill-muted:#2b2822;/* secondary text on fills, 5.2:1 or more */
--nb-lemon:#ffe14d;--nb-pink:#ff90d0;--nb-cyan:#62e2e8;--nb-lime:#b6f36a;
--nb-orange:#ffa94d;--nb-violet:#b9a3ff;--nb-red:#ff6b6b;
--nb-blue:#2448e0;--nb-on-blue:#ffffff; /* the only fill that takes white text, 6.8:1 */
--nb-danger:#b42318; /* destructive text, 6.1:1 on paper */
--nb-font:"Space Grotesk","Archivo","Helvetica Neue",Arial,system-ui,sans-serif;
--nb-font-display:"Archivo Black","Arial Black","Helvetica Neue",Arial,sans-serif;
}
/* Dark brutalism: data-nb="dark" on any wrapper, or a checked .nb-theme-input inside a [data-nb] element.
:is() is a forgiving list, so browsers without :has() still apply data-nb="dark". Fills keep dark text. */
[data-nb]:is([data-nb="dark"],:has(.nb-theme-input:checked)){
--nb-paper:#17150f;--nb-surface:#25221b;--nb-ink:#fbf3dc;--nb-muted:#d6ccb2;--nb-line:#fbf3dc;
--nb-shadow-color:#fbf3dc;--nb-focus:#8db4ff;--nb-danger:#ff9b8f;color-scheme:dark}
/* More contrast requested: secondary text turns full ink */
@media (prefers-contrast:more){:root,[data-nb]:is([data-nb],:has(.nb-theme-input:checked)){--nb-muted:var(--nb-ink);--nb-on-fill-muted:var(--nb-on-fill)}}
:root,[data-nb]{
--nb-border:var(--nb-bw) solid var(--nb-line);
--nb-shadow:var(--nb-shadow-x) var(--nb-shadow-y) 0 0 var(--nb-shadow-color);
--nb-shadow-sm:calc(var(--nb-shadow-x)*.5) calc(var(--nb-shadow-y)*.5) 0 0 var(--nb-shadow-color);
--nb-shadow-lg:calc(var(--nb-shadow-x)*1.75) calc(var(--nb-shadow-y)*1.75) 0 0 var(--nb-shadow-color);
--nb-shadow-hover:calc(var(--nb-shadow-x) + 2px) calc(var(--nb-shadow-y) + 2px) 0 0 var(--nb-shadow-color);
--nb-press:var(--nb-shadow-x) var(--nb-shadow-y); /* translate for a pressed control */
--nb-press-sm:calc(var(--nb-shadow-x)*.5) calc(var(--nb-shadow-y)*.5); /* …for one with the small shadow */
}
[data-nb]{background:var(--nb-paper);color:var(--nb-ink);font-family:var(--nb-font)}
/* The tokens above, exposed as Tailwind utilities: bg-nb-paper, bg-nb-lemon, text-nb-ink, border-nb-line,
shadow-nb, shadow-nb-sm, shadow-nb-lg, rounded-nb, rounded-nb-sm, font-nb, font-nb-display, outline-nb-focus… */
@theme inline {
--color-nb-paper: var(--nb-paper); --color-nb-surface: var(--nb-surface); --color-nb-ink: var(--nb-ink);
--color-nb-muted: var(--nb-muted); --color-nb-line: var(--nb-line); --color-nb-focus: var(--nb-focus);
--color-nb-on-fill: var(--nb-on-fill); --color-nb-on-fill-muted: var(--nb-on-fill-muted);
--color-nb-lemon: var(--nb-lemon); --color-nb-pink: var(--nb-pink); --color-nb-cyan: var(--nb-cyan); --color-nb-lime: var(--nb-lime);
--color-nb-orange: var(--nb-orange); --color-nb-violet: var(--nb-violet); --color-nb-red: var(--nb-red);
--color-nb-blue: var(--nb-blue); --color-nb-on-blue: var(--nb-on-blue); --color-nb-danger: var(--nb-danger);
--shadow-nb: var(--nb-shadow); --shadow-nb-sm: var(--nb-shadow-sm); --shadow-nb-lg: var(--nb-shadow-lg); --shadow-nb-hover: var(--nb-shadow-hover);
--radius-nb: var(--nb-radius); --radius-nb-sm: var(--nb-radius-sm);
--font-nb: var(--nb-font); --font-nb-display: var(--nb-font-display);
}
/* Starburst sticker outline (Tailwind has no clip-path utility) */
@utility clip-nb-burst {
clip-path: polygon(50% 0%,59.1% 16.1%,75% 6.7%,74.8% 25.2%,93.3% 25%,83.9% 40.9%,100% 50%,83.9% 59.1%,93.3% 75%,74.8% 74.8%,75% 93.3%,59.1% 83.9%,50% 100%,40.9% 83.9%,25% 93.3%,25.2% 74.8%,6.7% 75%,16.1% 59.1%,0% 50%,16.1% 40.9%,6.7% 25%,25.2% 25.2%,25% 6.7%,40.9% 16.1%);
}
About this set
The same kit on a dark background, switched by one attribute. Put data-nb=”dark” on any wrapper and the shared tokens change to near-black paper, a dark surface, cream text, cream borders and cream offset shadows, with a lighter blue for the dashed focus outline, so every component follows without its own dark rules. The saturated fills stay the same and keep dark text, which is what gives dark brutalism its punch. This set shows the buttons, an icon button and favourite toggle, toggle tiles, switch rows, a search field, a checkbox, a segmented control, three stat cards, a warning alert and a now playing bar with a progress bar and a play toggle. Text keeps at least 4.5:1 on the dark paper and surface, borders and shadows are cream against near-black, and the focus outline uses its own light blue so it never blends with the shadow. Use it for night modes, media apps and dashboards. Bootstrap adds data-bs-theme=”dark” next to data-nb so its own components match, and Tailwind needs no dark: classes because the utilities read the tokens.
What’s included
One attribute, data-nb="dark", re-themes every component
Cream borders and shadows on near-black paper
Fills keep dark text for contrast
Buttons, toggles, switches, field, segmented control, stat cards, alert and player
Works with Bootstrap's data-bs-theme="dark"
Accessibility
Every control keeps its native semantics and name from the light kit, and on states are still shown by position, a check or a word as well as colour. The focus outline switches to a lighter blue that reaches more than 7:1 on the dark surfaces, and the play control is a named checkbox beside a labelled progressbar. Transitions stop under prefers-reduced-motion.
Customise it
Edit the [data-nb] dark token rule in the block at the top; the light values are on :root. Bootstrap reads the same tokens through the --bs-* mapping, and Tailwind's bg-nb-paper, text-nb-ink and shadow-nb utilities change with them.