colorsbymax 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -934,7 +934,7 @@ var PRESETS = RAW_PRESETS.map((t) => ({
934
934
  var DARK_SUFFIX = "~dark";
935
935
  /** True when a theme's page background is dark. */
936
936
  var isDarkTheme = (tokens) => luminance(tokens.background) < .2;
937
- var hslOf$2 = (hex) => rgbToHsl(hexToRgb(hex));
937
+ var hslOf$4 = (hex) => rgbToHsl(hexToRgb(hex));
938
938
  var hsl$1 = (h, s, l) => rgbToHex(hslToRgb([
939
939
  h,
940
940
  Math.min(1, Math.max(0, s)),
@@ -942,12 +942,12 @@ var hsl$1 = (h, s, l) => rgbToHex(hslToRgb([
942
942
  ]));
943
943
  /** Same hue, saturation capped at `maxS`, lightness set to `l`. */
944
944
  var tone = (hex, l, maxS = 1) => {
945
- const [h, s] = hslOf$2(hex);
945
+ const [h, s] = hslOf$4(hex);
946
946
  return hsl$1(h, Math.min(s, maxS), l);
947
947
  };
948
948
  /** Keeps hue and saturation, lifting lightness into [min, max] so it reads on a dark page. */
949
949
  var lift = (hex, min, max = .78) => {
950
- const [h, s, l] = hslOf$2(hex);
950
+ const [h, s, l] = hslOf$4(hex);
951
951
  return hsl$1(h, s, Math.min(max, Math.max(min, l)));
952
952
  };
953
953
  /**
@@ -955,7 +955,7 @@ var lift = (hex, min, max = .78) => {
955
955
  * Lightness alone doesn't guarantee that: a vivid blue or violet at 60% lightness still reads dark.
956
956
  */
957
957
  var readOn = (hex, bg, ratio) => {
958
- const [h, s, l] = hslOf$2(hex);
958
+ const [h, s, l] = hslOf$4(hex);
959
959
  let out = hex;
960
960
  for (let x = l; contrastRatio(out, bg) < ratio && x < .96; x += .01) out = hsl$1(h, s, x);
961
961
  return out;
@@ -1033,6 +1033,139 @@ function toDark(theme) {
1033
1033
  * @param {'light' | 'dark'} mode
1034
1034
  */
1035
1035
  var inMode = (theme, mode) => mode === "dark" && !theme.custom ? toDark(theme) : theme;
1036
+ var QUIET = [
1037
+ "background",
1038
+ "surface",
1039
+ "secondary",
1040
+ "accent",
1041
+ "border",
1042
+ "app-background",
1043
+ "app-input",
1044
+ "app-border"
1045
+ ];
1046
+ var ON_QUIET = {
1047
+ secondary: "on-secondary",
1048
+ accent: "on-accent"
1049
+ };
1050
+ /**
1051
+ * The Subtle style on a site painted with the colour tokens: the page's backgrounds, cards,
1052
+ * borders and tints go nearly neutral (same lightness, a hint of the hue), so the theme's colour
1053
+ * stays on what matters: buttons, links, headings, highlights and charts.
1054
+ */
1055
+ function subtleTokens(t) {
1056
+ const out = { ...t };
1057
+ for (const key of QUIET) {
1058
+ if (!t[key]) continue;
1059
+ const [h, s, l] = hslOf$4(t[key]);
1060
+ out[key] = hsl$1(h, s * .18, l);
1061
+ }
1062
+ for (const [bg, fg] of Object.entries(ON_QUIET)) {
1063
+ if (!out[bg] || !out[fg] || contrastRatio(out[fg], out[bg]) >= 4.5) continue;
1064
+ out[fg] = contrastRatio(t.ink, out[bg]) >= 4.5 ? t.ink : luminance(out[bg]) > .4 ? "#000000" : "#ffffff";
1065
+ }
1066
+ return out;
1067
+ }
1068
+ /** The ten, in the order a card shows them. */
1069
+ var SWATCH_KEYS = [
1070
+ "primary",
1071
+ "primary-alt",
1072
+ "primary-dark",
1073
+ "secondary",
1074
+ "background",
1075
+ "ink",
1076
+ "data-1",
1077
+ "data-2",
1078
+ "data-3",
1079
+ "data-4"
1080
+ ];
1081
+ var CHART = [
1082
+ "data-1",
1083
+ "data-2",
1084
+ "data-3",
1085
+ "data-4"
1086
+ ];
1087
+ var HIDDEN_CHART = [
1088
+ "data-5",
1089
+ "data-6",
1090
+ "data-7",
1091
+ "data-8"
1092
+ ];
1093
+ var hslOf$3 = (hex) => rgbToHsl(hexToRgb(hex));
1094
+ var hueGap$1 = (a, b) => {
1095
+ const d = Math.abs(a - b);
1096
+ return Math.min(d, 360 - d);
1097
+ };
1098
+ var clampColours = (n) => Math.min(10, Math.max(5, Math.round(Number(n) || 10)));
1099
+ /** How far a colour's hue is from the theme's brand colours (0 = one of them); greys fit anything. */
1100
+ function misfit(tokens, key) {
1101
+ const [h, s] = hslOf$3(tokens[key]);
1102
+ if (s < .15) return 0;
1103
+ const brand = ["primary", "primary-alt"].map((k) => hslOf$3(tokens[k])).filter(([, bs]) => bs >= .15);
1104
+ if (!brand.length) return 90;
1105
+ return Math.min(...brand.map(([bh]) => hueGap$1(h, bh)));
1106
+ }
1107
+ /** The swatch keys a theme keeps at `count` colours, in card order. */
1108
+ function paletteKeys(tokens, count = 10) {
1109
+ const n = clampColours(count);
1110
+ if (n >= 10) return SWATCH_KEYS;
1111
+ const extras = ["secondary", ...[...CHART].sort((a, b) => misfit(tokens, a) - misfit(tokens, b))];
1112
+ const kept = /* @__PURE__ */ new Set([
1113
+ "primary",
1114
+ "primary-alt",
1115
+ "primary-dark",
1116
+ "background",
1117
+ "ink",
1118
+ ...extras.slice(0, n - 5)
1119
+ ]);
1120
+ return SWATCH_KEYS.filter((k) => kept.has(k));
1121
+ }
1122
+ /** Same hue and saturation as `hex`, at lightness `l`. */
1123
+ var atLightness = (hex, l) => {
1124
+ const [h, s] = hslOf$3(hex);
1125
+ return rgbToHex(hslToRgb([
1126
+ h,
1127
+ s,
1128
+ l
1129
+ ]));
1130
+ };
1131
+ /**
1132
+ * A theme's tokens using only `count` of its colours. Chart colours that go are painted with
1133
+ * the ones that stay (brand colours first), at their own lightness so charts keep their
1134
+ * contrast; a soft tint that goes becomes a tint of the brand colour. The colours it replaces are
1135
+ * then run through the contrast fixes (lightness only), so a smaller palette still reads.
1136
+ */
1137
+ function reducePalette(tokens, count = 10) {
1138
+ const n = clampColours(count);
1139
+ if (n >= 10) return tokens;
1140
+ const kept = new Set(paletteKeys(tokens, n));
1141
+ const out = { ...tokens };
1142
+ const replaced = /* @__PURE__ */ new Set();
1143
+ const pool = [
1144
+ "primary",
1145
+ "primary-alt",
1146
+ ...CHART.filter((k) => kept.has(k))
1147
+ ].map((k) => tokens[k]);
1148
+ [...CHART.filter((k) => !kept.has(k)), ...HIDDEN_CHART].forEach((key, i) => {
1149
+ if (!tokens[key]) return;
1150
+ out[key] = atLightness(pool[i % pool.length], hslOf$3(tokens[key])[2]);
1151
+ replaced.add(key);
1152
+ });
1153
+ if (!kept.has("secondary")) {
1154
+ out.secondary = mix(tokens.primary, tokens.background, .14);
1155
+ out["on-secondary"] = [
1156
+ tokens["on-secondary"],
1157
+ tokens["primary-dark"],
1158
+ tokens.ink
1159
+ ].find((c) => c && contrastRatio(c, out.secondary) >= 4.5) ?? tokens.ink;
1160
+ replaced.add("secondary").add("on-secondary");
1161
+ }
1162
+ for (const [key, value] of Object.entries(fixAll(out))) if (replaced.has(key)) out[key] = value;
1163
+ return out;
1164
+ }
1165
+ /** A theme's saved colour count: its own, or the visitor's default for every theme. */
1166
+ var coloursFor = (sizes, themeId, fallback = 10) => clampColours(sizes?.[baseId(themeId)] ?? fallback);
1167
+ /** Light and dark versions of a theme share one count. */
1168
+ var baseId = (id) => String(id).replace(/~dark$/, "");
1036
1169
  //#endregion
1037
1170
  //#region src/scan.js
1038
1171
  /** Elements beyond this count are ignored so scanning stays fast on huge pages. */
@@ -1083,8 +1216,8 @@ function withoutAppliedTheme(fn) {
1083
1216
  const freeze = document.createElement("style");
1084
1217
  freeze.textContent = "*,*::before,*::after{transition:none!important}";
1085
1218
  document.head.appendChild(freeze);
1086
- const recolour = document.querySelector("style[data-colorsbymax=\"recolour\"]");
1087
- if (recolour) recolour.disabled = true;
1219
+ const applied = [...document.querySelectorAll("style[data-colorsbymax=\"recolour\"], style[data-colorsbymax=\"vivid\"]")];
1220
+ for (const sheet of applied) sheet.disabled = true;
1088
1221
  const style = document.documentElement.style;
1089
1222
  const saved = [];
1090
1223
  for (let i = style.length - 1; i >= 0; i--) {
@@ -1098,7 +1231,7 @@ function withoutAppliedTheme(fn) {
1098
1231
  return fn();
1099
1232
  } finally {
1100
1233
  for (const [prop, value] of saved) style.setProperty(prop, value);
1101
- if (recolour) recolour.disabled = false;
1234
+ for (const sheet of applied) sheet.disabled = false;
1102
1235
  getComputedStyle(document.documentElement).color;
1103
1236
  document.body.offsetHeight;
1104
1237
  freeze.remove();
@@ -1305,6 +1438,337 @@ function suggestThemes(scan, siteName, library = []) {
1305
1438
  return [...themes, ...matches];
1306
1439
  }
1307
1440
  //#endregion
1441
+ //#region src/vivid.js
1442
+ var ZONE = "data-cbm-z";
1443
+ var ROLE = "data-cbm-v";
1444
+ var MIN_TEXT = 4.5;
1445
+ var MIN_LARGE = 3;
1446
+ var IGNORE = /* @__PURE__ */ new Set([
1447
+ "script",
1448
+ "style",
1449
+ "noscript",
1450
+ "template",
1451
+ "link",
1452
+ "meta",
1453
+ "colorsbymax-root"
1454
+ ]);
1455
+ var BUTTON_INPUTS = /* @__PURE__ */ new Set([
1456
+ "button",
1457
+ "submit",
1458
+ "reset"
1459
+ ]);
1460
+ var FIELD_SKIP = /* @__PURE__ */ new Set([
1461
+ "checkbox",
1462
+ "radio",
1463
+ "range",
1464
+ "color",
1465
+ "file",
1466
+ "hidden",
1467
+ "image",
1468
+ ...BUTTON_INPUTS
1469
+ ]);
1470
+ var WRAPPERS = /* @__PURE__ */ new Set([
1471
+ "div",
1472
+ "main",
1473
+ "section",
1474
+ "article"
1475
+ ]);
1476
+ var visibleBlocks = (el) => [...el.children].filter((c) => {
1477
+ if (IGNORE.has(c.localName) || c.dataset?.colorsbymax !== void 0) return false;
1478
+ return c.getBoundingClientRect().height > 8 && getComputedStyle(c).display !== "none";
1479
+ });
1480
+ var pageHeight = () => Math.max(document.documentElement.scrollHeight, 1);
1481
+ var isTag = (el, ...tags) => tags.includes(el.localName);
1482
+ /**
1483
+ * The page's top-level parts, top to bottom: past any single wrapper (an app's root div), with a
1484
+ * <main>, or a wrapper holding most of the page, opened up into its sections.
1485
+ */
1486
+ function pageParts() {
1487
+ let container = document.body;
1488
+ for (let i = 0; i < 6; i++) {
1489
+ const kids = visibleBlocks(container);
1490
+ if (kids.length !== 1) break;
1491
+ container = kids[0];
1492
+ }
1493
+ const parts = [];
1494
+ for (const kid of visibleBlocks(container)) {
1495
+ const r = kid.getBoundingClientRect();
1496
+ if (kid.localName === "main" || kid.getAttribute("role") === "main" || WRAPPERS.has(kid.localName) && r.height > pageHeight() * .5 && visibleBlocks(kid).length > 1) parts.push(...visibleBlocks(kid));
1497
+ else parts.push(kid);
1498
+ }
1499
+ return parts.filter((p) => p.getBoundingClientRect().height > 30);
1500
+ }
1501
+ var hasButton = (el) => el.querySelector("button, [role=\"button\"], input[type=\"submit\"], a[class*=\"btn\" i], a[class*=\"button\" i]");
1502
+ /** Marks the page's header, hero, sections, closing call to action and footer. */
1503
+ function markZones() {
1504
+ for (const el of document.querySelectorAll(`[${ZONE}]`)) el.removeAttribute(ZONE);
1505
+ const parts = pageParts();
1506
+ if (parts.length < 2) return;
1507
+ let header = parts.find((p) => isTag(p, "header") || p.getAttribute("role") === "banner");
1508
+ if (!header && parts[0].getBoundingClientRect().height < 200 && parts[0].querySelector("nav, a")) header = parts[0];
1509
+ const last = parts[parts.length - 1];
1510
+ const footer = parts.find((p) => isTag(p, "footer") || p.getAttribute("role") === "contentinfo") ?? (isTag(last, "footer") ? last : null);
1511
+ const body = parts.filter((p) => p !== header && p !== footer);
1512
+ const hero = body.find((p) => p.querySelector("h1")) ?? body[0];
1513
+ const end = body[body.length - 1];
1514
+ const cta = end && end !== hero && end.querySelector("h1, h2, h3") && hasButton(end) && end.textContent.trim().length < 400 ? end : null;
1515
+ if (header) header.setAttribute(ZONE, "header");
1516
+ if (footer) footer.setAttribute(ZONE, "footer");
1517
+ if (hero) hero.setAttribute(ZONE, "hero");
1518
+ if (cta) cta.setAttribute(ZONE, "cta");
1519
+ body.filter((p) => p !== hero && p !== cta).forEach((p, i) => i % 2 === 0 && p.setAttribute("data-cbm-z", "tint"));
1520
+ }
1521
+ /** What an element paints over: 'primary' (a brand-coloured band), 'secondary' (the footer), or 'page'. */
1522
+ var zoneOf = (el) => {
1523
+ const z = el.closest(`[${ZONE}]`)?.getAttribute(ZONE);
1524
+ return z === "cta" ? "primary" : z === "footer" ? "secondary" : "page";
1525
+ };
1526
+ var px = (v) => parseFloat(v) || 0;
1527
+ var allBorders = (cs) => [
1528
+ "top",
1529
+ "right",
1530
+ "bottom",
1531
+ "left"
1532
+ ].every((s) => px(cs.getPropertyValue(`border-${s}-width`)) > 0 && cs.getPropertyValue(`border-${s}-style`) !== "none");
1533
+ /**
1534
+ * The roles an element plays, from its original look. `ownFill` is its own background (a hex, or
1535
+ * null if it has none worth counting) and `under` the hex of what it sits on.
1536
+ */
1537
+ function rolesOf(el, cs, ownFill, under) {
1538
+ const roles = [];
1539
+ const name = el.localName;
1540
+ const zoneRole = el.getAttribute(ZONE);
1541
+ const zone = zoneOf(el);
1542
+ if (zoneRole) roles.push(`z-${zoneRole}`);
1543
+ const inButton = el.parentElement?.closest(`[${ROLE}~="btn"], [${ROLE}~="btn2"], [${ROLE}~="btn-inv"]`);
1544
+ if (inButton) {
1545
+ const kind = inButton.getAttribute(ROLE).match(/\b(btn-inv|btn2|btn)\b/)[1];
1546
+ roles.push(`${kind}-in`);
1547
+ return roles;
1548
+ }
1549
+ if (zone === "primary") roles.push("on1");
1550
+ else if (zone === "secondary") roles.push("on2");
1551
+ const type = (el.getAttribute("type") || "").toLowerCase();
1552
+ const radius = px(cs.borderTopLeftRadius);
1553
+ const filled = Boolean(ownFill) && deltaE(ownFill, under) > 12;
1554
+ if (name === "button" || el.getAttribute("role") === "button" || name === "input" && BUTTON_INPUTS.has(type) || (name === "a" || name === "label") && (filled || allBorders(cs)) && px(cs.paddingLeft) >= 6 && cs.display !== "inline") {
1555
+ roles.push(zone === "primary" ? "btn-inv" : filled ? "btn" : "btn2");
1556
+ return roles;
1557
+ }
1558
+ if (name === "a") {
1559
+ roles.push(el.closest(`nav, [data-cbm-z="header"]`) ? "navlink" : zone === "page" ? "link" : "link-on");
1560
+ return roles;
1561
+ }
1562
+ if (/^h[1-3]$/.test(name) && zone === "page") roles.push("head");
1563
+ if (zone === "page" && [
1564
+ "em",
1565
+ "strong",
1566
+ "b",
1567
+ "i",
1568
+ "span",
1569
+ "mark"
1570
+ ].includes(name) && el.parentElement?.localName === "h1") roles.push("hl");
1571
+ if (name === "input" && !FIELD_SKIP.has(type)) roles.push("field");
1572
+ else if (name === "textarea" || name === "select") roles.push("field");
1573
+ if (zone !== "page") return roles;
1574
+ if (name === "th") roles.push("th");
1575
+ else if (name === "blockquote") roles.push("quote");
1576
+ else if (name === "pre" || name === "code" && el.parentElement?.localName !== "pre") roles.push("code");
1577
+ else if (name === "hr") roles.push("rule");
1578
+ else if (name === "mark") roles.push("mark");
1579
+ const blockish = [
1580
+ "block",
1581
+ "flex",
1582
+ "grid",
1583
+ "flow-root",
1584
+ "list-item"
1585
+ ].includes(cs.display);
1586
+ if (blockish && !zoneRole && radius >= 6 && (allBorders(cs) || cs.boxShadow !== "none" || filled) && ![
1587
+ "input",
1588
+ "textarea",
1589
+ "select",
1590
+ "img"
1591
+ ].includes(name)) {
1592
+ const r = el.getBoundingClientRect();
1593
+ const parent = el.parentElement?.getBoundingClientRect();
1594
+ if (r.height >= 56 && parent && r.width <= parent.width * .96 && el.textContent.trim()) roles.push("card");
1595
+ }
1596
+ const tinted = Boolean(ownFill) && deltaE(ownFill, under) > 2;
1597
+ if (!blockish && tinted && radius >= 6 && el.textContent.trim().length <= 30 && !el.querySelector("div, p")) roles.push("badge");
1598
+ return roles;
1599
+ }
1600
+ var readsOn = (fg, bgs, min) => bgs.every((bg) => contrastRatio(fg, bg) >= min);
1601
+ /** The first colour that reads on every background, else the theme's plain text colour. */
1602
+ var firstReadable = (candidates, bgs, min) => candidates.find((c) => c && readsOn(c, bgs, min)) ?? candidates[candidates.length - 1];
1603
+ /** The Colourful stylesheet for a theme's tokens. */
1604
+ function vividCss(t) {
1605
+ const bg = t.background;
1606
+ const tint = mix(t.secondary, bg, .55);
1607
+ const glow = mix(t.primary, bg, .16);
1608
+ const glowAlt = mix(t["primary-alt"], bg, .12);
1609
+ const pageBgs = [
1610
+ bg,
1611
+ t.surface,
1612
+ tint,
1613
+ glow
1614
+ ];
1615
+ const ink = firstReadable([t.ink], pageBgs, MIN_TEXT);
1616
+ const head = firstReadable([t["primary-dark"], ink], pageBgs, MIN_LARGE);
1617
+ const link = firstReadable([
1618
+ t.primary,
1619
+ t["primary-dark"],
1620
+ ink
1621
+ ], pageBgs, MIN_TEXT);
1622
+ const linkHover = firstReadable([t["primary-dark"], ink], pageBgs, MIN_TEXT);
1623
+ const navHover = firstReadable([
1624
+ t.primary,
1625
+ t["primary-dark"],
1626
+ ink
1627
+ ], [t.surface, bg], MIN_TEXT);
1628
+ const bandEnd = mix(t["primary-alt"], t.primary, .45);
1629
+ const onPrimary = firstReadable([
1630
+ t["on-primary"],
1631
+ "#ffffff",
1632
+ "#000000"
1633
+ ], [t.primary], MIN_TEXT);
1634
+ const band = readsOn(onPrimary, [t.primary, bandEnd], MIN_TEXT) ? `linear-gradient(135deg,${t.primary},${bandEnd})` : t.primary;
1635
+ const footerBg = t.secondary;
1636
+ const onSecondary = firstReadable([t["on-secondary"], ink], [footerBg], MIN_TEXT);
1637
+ const btnHover = mix(t["primary-dark"], t.primary, .22);
1638
+ const onBtnHover = firstReadable([
1639
+ onPrimary,
1640
+ "#ffffff",
1641
+ "#000000"
1642
+ ], [btnHover], MIN_TEXT);
1643
+ const quiet = firstReadable([t["on-secondary"], ink], [t.secondary], MIN_TEXT);
1644
+ const badgeBg = mix(t.primary, t.surface, .2);
1645
+ const badgeInk = firstReadable([
1646
+ t["primary-dark"],
1647
+ t["on-secondary"],
1648
+ ink
1649
+ ], [badgeBg], MIN_TEXT);
1650
+ const hlGradient = readsOn(t.primary, [bg, glow], MIN_LARGE) && readsOn(t["primary-alt"], [bg, glow], MIN_LARGE);
1651
+ const shadow = `0 18px 40px -22px ${t.shadow}`;
1652
+ const move = "transition:background-color .25s,color .25s,border-color .25s,box-shadow .25s,transform .25s!important";
1653
+ const r = (role, css) => `[${ROLE}~="${role}"]{${css}}`;
1654
+ const imp = (decls) => decls.map((d) => `${d}!important`).join(";");
1655
+ return [
1656
+ `html{accent-color:${t.primary}}`,
1657
+ `::selection{background:${mix(t.primary, bg, .28)}}`,
1658
+ `[${ROLE}] li::marker,li[${ROLE}]::marker{color:${t.primary}}`,
1659
+ r("z-header", imp([`background-color:${t.surface}`, `border-bottom-color:${t.border}`])),
1660
+ r("z-hero", imp([`background-color:${bg}`, `background-image:radial-gradient(120% 85% at 50% -10%,${glow} 0%,transparent 62%),radial-gradient(60% 60% at 100% 100%,${glowAlt} 0%,transparent 70%)`])),
1661
+ r("z-tint", imp([`background-color:${tint}`])),
1662
+ r("z-cta", imp([
1663
+ `background-color:${t.primary}`,
1664
+ `background-image:${band}`,
1665
+ `color:${onPrimary}`
1666
+ ])),
1667
+ r("z-footer", imp([
1668
+ `background-color:${footerBg}`,
1669
+ `border-top-color:transparent`,
1670
+ `color:${onSecondary}`
1671
+ ])),
1672
+ r("on1", imp([`color:${onPrimary}`])),
1673
+ r("on2", imp([`color:${onSecondary}`])),
1674
+ r("head", imp([`color:${head}`])),
1675
+ r("hl", hlGradient ? imp([
1676
+ `background-image:linear-gradient(100deg,${t.primary},${t["primary-alt"]})`,
1677
+ "background-color:transparent",
1678
+ "-webkit-background-clip:text",
1679
+ "background-clip:text",
1680
+ "color:transparent",
1681
+ "-webkit-text-fill-color:transparent"
1682
+ ]) : imp([`color:${t.primary}`])),
1683
+ r("link", imp([
1684
+ `color:${link}`,
1685
+ `text-decoration-color:${mix(link, bg, .45)}`,
1686
+ move
1687
+ ])),
1688
+ `[${ROLE}~="link"]:hover{${imp([`color:${linkHover}`, `text-decoration-color:${linkHover}`])}}`,
1689
+ r("link-on", imp(["color:inherit", "text-decoration-color:currentColor"])),
1690
+ r("navlink", imp([`color:${firstReadable([t["ink-secondary"], ink], [t.surface, bg], MIN_TEXT)}`, move])),
1691
+ `[${ROLE}~="navlink"]:hover{${imp([`color:${navHover}`])}}`,
1692
+ r("card", imp([
1693
+ `background-color:${t.surface}`,
1694
+ `border-color:${t.border}`,
1695
+ move
1696
+ ])),
1697
+ `[${ROLE}~="card"]:hover{${imp([
1698
+ `border-color:${t.primary}`,
1699
+ `box-shadow:${shadow}`,
1700
+ "transform:translateY(-2px)"
1701
+ ])}}`,
1702
+ r("badge", imp([
1703
+ `background-color:${badgeBg}`,
1704
+ `color:${badgeInk}`,
1705
+ `border-color:transparent`
1706
+ ])),
1707
+ r("field", imp([
1708
+ `background-color:${t.surface}`,
1709
+ `border-color:${t.border}`,
1710
+ `color:${ink}`,
1711
+ move
1712
+ ])),
1713
+ `[${ROLE}~="field"]:focus{${imp([
1714
+ `border-color:${t.primary}`,
1715
+ `outline:2px solid ${mix(t.primary, bg, .35)}`,
1716
+ "outline-offset:1px"
1717
+ ])}}`,
1718
+ r("th", imp([`background-color:${tint}`, `color:${head}`])),
1719
+ r("quote", imp([
1720
+ `border-left-color:${t.primary}`,
1721
+ `background-color:${t.surface}`,
1722
+ "border-radius:0 12px 12px 0"
1723
+ ])),
1724
+ r("code", imp([`background-color:${t.secondary}`, `color:${quiet}`])),
1725
+ r("rule", imp([`border-color:${t.border}`])),
1726
+ r("mark", imp([`background-color:${mix(t["primary-alt"], bg, .3)}`, `color:${ink}`])),
1727
+ r("btn", imp([
1728
+ `background-color:${t.primary}`,
1729
+ "background-image:none",
1730
+ `color:${onPrimary}`,
1731
+ `border-color:${t.primary}`,
1732
+ `box-shadow:0 8px 20px -10px ${t.primary}`,
1733
+ move
1734
+ ])),
1735
+ `[${ROLE}~="btn"]:hover{${imp([
1736
+ `background-color:${btnHover}`,
1737
+ `border-color:${btnHover}`,
1738
+ `color:${onBtnHover}`,
1739
+ "transform:translateY(-1px)"
1740
+ ])}}`,
1741
+ r("btn-in", imp([`color:${onPrimary}`])),
1742
+ `[${ROLE}~="btn"]:hover [${ROLE}~="btn-in"]{${imp([`color:${onBtnHover}`])}}`,
1743
+ r("btn2", imp([
1744
+ `background-color:${t.secondary}`,
1745
+ "background-image:none",
1746
+ `color:${quiet}`,
1747
+ `border-color:${t.border}`,
1748
+ move
1749
+ ])),
1750
+ `[${ROLE}~="btn2"]:hover{${imp([`border-color:${t.primary}`, "transform:translateY(-1px)"])}}`,
1751
+ r("btn2-in", imp([`color:${quiet}`])),
1752
+ r("btn-inv", imp([
1753
+ `background-color:${onPrimary}`,
1754
+ "background-image:none",
1755
+ `color:${firstReadable([
1756
+ t.primary,
1757
+ t["primary-dark"],
1758
+ ink
1759
+ ], [onPrimary], MIN_TEXT)}`,
1760
+ `border-color:${onPrimary}`,
1761
+ move
1762
+ ])),
1763
+ `[${ROLE}~="btn-inv"]:hover{${imp(["transform:translateY(-1px)", `box-shadow:0 10px 24px -12px ${t.shadow}`])}}`,
1764
+ r("btn-inv-in", imp([`color:${firstReadable([
1765
+ t.primary,
1766
+ t["primary-dark"],
1767
+ ink
1768
+ ], [onPrimary], MIN_TEXT)}`]))
1769
+ ].join("\n");
1770
+ }
1771
+ //#endregion
1308
1772
  //#region src/recolour.js
1309
1773
  var ATTR = "data-cbm";
1310
1774
  /** Elements the engine never touches: the switcher itself. */
@@ -1382,7 +1846,7 @@ var fillsElement = (cs) => Boolean(cs.backgroundImage && cs.backgroundImage !==
1382
1846
  var hueDist = (a, b) => Math.min(Math.abs(a - b), 360 - Math.abs(a - b));
1383
1847
  /** How far hue `a` is from `b`, signed, in -180..180. */
1384
1848
  var signedHue = (a, b) => (a - b + 540) % 360 - 180;
1385
- var hslOf$1 = (hex) => rgbToHsl(hexToRgb(hex));
1849
+ var hslOf$2 = (hex) => rgbToHsl(hexToRgb(hex));
1386
1850
  /** True when the site defines colorsbymax's variables itself, so no re-colouring is needed. */
1387
1851
  function usesColourTokens() {
1388
1852
  return withoutAppliedTheme(() => getComputedStyle(document.documentElement).getPropertyValue("--color-primary").trim() !== "");
@@ -1417,16 +1881,20 @@ function createParser() {
1417
1881
  * Starts re-colouring the page. Returns the site's own colours as tokens (`site`), `apply(tokens)`
1418
1882
  * to show a theme (null restores the original look) and `stop()`.
1419
1883
  */
1420
- function createRecolourer() {
1884
+ function createRecolourer({ colourful = false } = {}) {
1421
1885
  const parse = createParser();
1422
1886
  const sheet = document.createElement("style");
1423
1887
  sheet.dataset.colorsbymax = "recolour";
1424
1888
  document.head.append(sheet);
1889
+ const vividSheet = document.createElement("style");
1890
+ vividSheet.dataset.colorsbymax = "vivid";
1891
+ document.head.append(vividSheet);
1892
+ let vivid = colourful;
1425
1893
  const { roles } = inferRoles(collectColors());
1426
1894
  const site = themeFromRoles(roles);
1427
1895
  const plain = !roles.primary;
1428
- const siteBgL = hslOf$1(site.background)[2];
1429
- const siteInkL = hslOf$1(site.ink)[2];
1896
+ const siteBgL = hslOf$2(site.background)[2];
1897
+ const siteInkL = hslOf$2(site.ink)[2];
1430
1898
  /** Each distinct painted value, keyed "prop|value", plus the background under text and icons. */
1431
1899
  const entries = /* @__PURE__ */ new Map();
1432
1900
  let theme = null;
@@ -1497,11 +1965,18 @@ function createRecolourer() {
1497
1965
  if (el.localName === SKIP$1) continue;
1498
1966
  if (el.closest(skip)) {
1499
1967
  el.removeAttribute(ATTR);
1968
+ el.removeAttribute(ROLE);
1500
1969
  continue;
1501
1970
  }
1502
1971
  const ids = read(el);
1503
1972
  if (ids.length) el.setAttribute(ATTR, ids.join(" "));
1504
1973
  else el.removeAttribute(ATTR);
1974
+ const cs = getComputedStyle(el);
1975
+ const own = parse(cs.backgroundColor);
1976
+ const under = parse(backdropOf(el.parentElement))?.hex ?? "#ffffff";
1977
+ const roles = rolesOf(el, cs, own && own.alpha >= .5 ? own.hex : null, under);
1978
+ if (roles.length) el.setAttribute(ROLE, roles.join(" "));
1979
+ else el.removeAttribute(ROLE);
1505
1980
  }
1506
1981
  }
1507
1982
  });
@@ -1513,17 +1988,17 @@ function createRecolourer() {
1513
1988
  const mapHex = (css, kind) => {
1514
1989
  const c = parse(css);
1515
1990
  if (!c) return null;
1516
- const [, s, l] = hslOf$1(c.hex);
1991
+ const [, s, l] = hslOf$2(c.hex);
1517
1992
  let hex;
1518
1993
  if (s < NEUTRAL_SATURATION) {
1519
1994
  const span = siteInkL - siteBgL;
1520
1995
  const t = Math.abs(span) < .05 ? l > .5 ? 0 : 1 : clamp$1((l - siteBgL) / span);
1521
1996
  hex = mix(theme.ink, theme.background, t);
1522
1997
  } else {
1523
- const hue = hslOf$1(c.hex)[0];
1524
- const near = (token, within) => hueDist(hue, hslOf$1(site[token])[0]) <= within;
1998
+ const hue = hslOf$2(c.hex)[0];
1999
+ const near = (token, within) => hueDist(hue, hslOf$2(site[token])[0]) <= within;
1525
2000
  let anchor = near("primary", FAMILY_HUE) ? "primary" : null;
1526
- if (!anchor && s >= .25) anchor = STATUS.filter((token) => near(token, STATUS_HUE)).sort((a, b) => hueDist(hue, hslOf$1(site[a])[0]) - hueDist(hue, hslOf$1(site[b])[0]))[0] ?? null;
2001
+ if (!anchor && s >= .25) anchor = STATUS.filter((token) => near(token, STATUS_HUE)).sort((a, b) => hueDist(hue, hslOf$2(site[a])[0]) - hueDist(hue, hslOf$2(site[b])[0]))[0] ?? null;
1527
2002
  if (!anchor && near("primary-alt", FAMILY_HUE)) anchor = "primary-alt";
1528
2003
  if (!anchor) {
1529
2004
  let bestD = Infinity;
@@ -1532,10 +2007,10 @@ function createRecolourer() {
1532
2007
  if (d < bestD) [anchor, bestD] = [token, d];
1533
2008
  }
1534
2009
  }
1535
- const [sh, ss, sl] = hslOf$1(site[anchor]);
1536
- const [th, ts, tl] = hslOf$1(theme[anchor]);
1537
- const themeBgL = hslOf$1(theme.background)[2];
1538
- const themeInkL = hslOf$1(theme.ink)[2];
2010
+ const [sh, ss, sl] = hslOf$2(site[anchor]);
2011
+ const [th, ts, tl] = hslOf$2(theme[anchor]);
2012
+ const themeBgL = hslOf$2(theme.background)[2];
2013
+ const themeInkL = hslOf$2(theme.ink)[2];
1539
2014
  const toBg = sl - siteBgL;
1540
2015
  const toInk = siteInkL - sl;
1541
2016
  const u = Math.abs(toBg) < .05 ? null : (l - siteBgL) / toBg;
@@ -1591,7 +2066,7 @@ function createRecolourer() {
1591
2066
  if (entry.prop === "background-image") value = entry.value.replace(COLOR_IN_GRADIENT, (stop) => mapColour(stop, entry.kind));
1592
2067
  else if (entry.backdrop) {
1593
2068
  const own = parse(entry.value);
1594
- const brand = entry.accent && own && hslOf$1(own.hex)[1] < NEUTRAL_SATURATION;
2069
+ const brand = entry.accent && own && hslOf$2(own.hex)[1] < NEUTRAL_SATURATION;
1595
2070
  const fg = brand ? {
1596
2071
  hex: theme.primary,
1597
2072
  alpha: own.alpha
@@ -1614,7 +2089,22 @@ function createRecolourer() {
1614
2089
  return `[${ATTR}~="${entry.id}"]{${entry.prop}:${value}!important}`;
1615
2090
  };
1616
2091
  let skip = `${SKIP$1}, ${LOGO_SELECTOR}`;
2092
+ withoutAppliedTheme(markZones);
1617
2093
  tag([document.body], true);
2094
+ const LOGO_ATTR = "data-cbm-logo";
2095
+ const logos = withoutAppliedTheme(() => [...document.querySelectorAll(LOGO_SELECTOR)].filter((el) => !el.parentElement?.closest(LOGO_SELECTOR) && !el.closest(SKIP$1)).map((el) => ({
2096
+ el,
2097
+ color: getComputedStyle(el).color
2098
+ })));
2099
+ const logoRules = () => {
2100
+ if (!theme || !skip.includes(LOGO_SELECTOR)) return "";
2101
+ return logos.map(({ el, color }, i) => {
2102
+ const own = parse(color);
2103
+ if (!own || hslOf$2(own.hex)[1] >= NEUTRAL_SATURATION || !el.isConnected) return "";
2104
+ el.setAttribute(LOGO_ATTR, i);
2105
+ return `[${LOGO_ATTR}="${i}"]{color:${mapColour(color, "text")}!important}`;
2106
+ }).join("");
2107
+ };
1618
2108
  const observer = new MutationObserver((records) => {
1619
2109
  const added = [];
1620
2110
  const changed = /* @__PURE__ */ new Set();
@@ -1638,7 +2128,13 @@ function createRecolourer() {
1638
2128
  /** Shows `tokens` in place of the site's colours; null brings back the original look. */
1639
2129
  apply(tokens) {
1640
2130
  theme = tokens;
1641
- sheet.textContent = tokens ? [...entries.values()].map(rule).join("") : "";
2131
+ sheet.textContent = tokens ? [...entries.values()].map(rule).join("") + logoRules() : "";
2132
+ vividSheet.textContent = tokens && vivid ? vividCss(tokens) : "";
2133
+ },
2134
+ /** Colourful (true) paints the page's parts by role; Subtle (false) only swaps its colours. */
2135
+ setColourful(on) {
2136
+ vivid = on;
2137
+ vividSheet.textContent = theme && vivid ? vividCss(theme) : "";
1642
2138
  },
1643
2139
  /** Whether the logo is re-coloured with the rest (off keeps it in its own colours). */
1644
2140
  setLogoColouring(on) {
@@ -1646,11 +2142,18 @@ function createRecolourer() {
1646
2142
  if (next === skip) return;
1647
2143
  skip = next;
1648
2144
  tag([document.body], true);
2145
+ if (theme) sheet.textContent = [...entries.values()].map(rule).join("") + logoRules();
1649
2146
  },
1650
2147
  stop() {
1651
2148
  observer.disconnect();
1652
2149
  sheet.remove();
1653
- for (const el of document.querySelectorAll(`[${ATTR}]`)) el.removeAttribute(ATTR);
2150
+ vividSheet.remove();
2151
+ for (const el of document.querySelectorAll(`[${ATTR}], [${ROLE}], [${ZONE}], [${LOGO_ATTR}]`)) {
2152
+ el.removeAttribute(LOGO_ATTR);
2153
+ el.removeAttribute(ATTR);
2154
+ el.removeAttribute(ROLE);
2155
+ el.removeAttribute(ZONE);
2156
+ }
1654
2157
  }
1655
2158
  };
1656
2159
  }
@@ -1670,6 +2173,9 @@ function createRecolourer() {
1670
2173
  * @property {number | 'full' | null} panelHeight Pixels, 'full' for all the room there is, or null to fit the content
1671
2174
  * @property {boolean} libraryCollapsed Whether the library's category chips are folded away
1672
2175
  * @property {boolean} colourLogo Whether themes re-colour the site's logo too (off keeps its own colours)
2176
+ * @property {number} paletteSize How many of a theme's ten colours every theme uses (5 to 10; 5 to start, the core ones)
2177
+ * @property {'colourful' | 'subtle'} colourStyle On a re-coloured site: 'colourful' paints its parts by role
2178
+ * (header, hero, sections, cards, buttons, footer), 'subtle' only swaps the colours it already has
1673
2179
  * @property {boolean} hideButton Hidden on this device from the finish screen (Alt+Shift+C brings it back)
1674
2180
  */
1675
2181
  /** @type {PanelSettings} */
@@ -1686,6 +2192,8 @@ var DEFAULT_SETTINGS = {
1686
2192
  panelHeight: null,
1687
2193
  libraryCollapsed: false,
1688
2194
  colourLogo: false,
2195
+ colourStyle: "colourful",
2196
+ paletteSize: 5,
1689
2197
  hideButton: false
1690
2198
  };
1691
2199
  /** Size presets offered in settings; dragging an edge gives a custom size instead. */
@@ -1725,6 +2233,8 @@ function loadSettings(storageKey, defaults = DEFAULT_SETTINGS) {
1725
2233
  "dark",
1726
2234
  "system"
1727
2235
  ].includes(out.mode)) out.mode = defaults.mode;
2236
+ if (!["colourful", "subtle"].includes(out.colourStyle)) out.colourStyle = defaults.colourStyle;
2237
+ if (!Number.isInteger(out.paletteSize) || out.paletteSize < 5 || out.paletteSize > 10) out.paletteSize = defaults.paletteSize;
1728
2238
  const size = (v) => typeof v === "number" && v > 0 && v < 1e4;
1729
2239
  out.panelWidth = size(saved.panelWidth) ? saved.panelWidth : null;
1730
2240
  out.panelHeight = size(saved.panelHeight) || saved.panelHeight === "full" ? saved.panelHeight : null;
@@ -1785,7 +2295,8 @@ var initialState = (defaultId) => ({
1785
2295
  overrides: {},
1786
2296
  customs: [],
1787
2297
  snapshot: null,
1788
- scanned: null
2298
+ scanned: null,
2299
+ paletteSizes: {}
1789
2300
  });
1790
2301
  /** Keeps only known token keys with valid hex values. */
1791
2302
  function sanitizeTokens(input) {
@@ -1840,7 +2351,8 @@ function loadState(storageKey, defaultTheme) {
1840
2351
  overrides: sanitizeTokens(data.overrides),
1841
2352
  customs,
1842
2353
  snapshot,
1843
- scanned
2354
+ scanned,
2355
+ paletteSizes: Object.fromEntries(Object.entries(data.paletteSizes && typeof data.paletteSizes === "object" ? data.paletteSizes : {}).filter(([id, n]) => typeof id === "string" && Number.isInteger(n) && n >= 5 && n <= 10))
1844
2356
  };
1845
2357
  } catch {
1846
2358
  return fresh;
@@ -1922,6 +2434,13 @@ var newId = () => `custom-${Date.now().toString(36)}-${Math.random().toString(36
1922
2434
  * `hidden: import.meta.env.PROD` to keep it out of production once the colours are chosen.
1923
2435
  * @property {'bottom-right' | 'bottom-left' | 'top-left' | 'top-right'} [position] Where the colour
1924
2436
  * button starts (default 'bottom-right'). 'top-right' sits under a floating nav bar.
2437
+ * @property {'colourful' | 'subtle'} [colourStyle] How boldly a re-coloured site takes a theme, for a
2438
+ * first-time visitor. 'colourful' (default) paints the page's parts by role, as a designer would:
2439
+ * header, hero, alternating sections, cards, buttons, links, headings and footer. 'subtle' only
2440
+ * swaps the colours the site already has. Visitors can switch in the panel.
2441
+ * @property {Partial<Record<keyof typeof FEATURES, boolean>>} [features] Parts of the panel to switch off
2442
+ * for everyone, e.g. { scan: false, audit: false } (all on by default). Unlike the rest of the
2443
+ * config it's read live, so a site can change it after loading (from its own settings).
1925
2444
  * @property {boolean | 'auto'} [recolour] Re-colour a site that doesn't paint with the --color-*
1926
2445
  * variables, by swapping the colours actually on the page. 'auto' (default) does it only when
1927
2446
  * the site doesn't define --color-primary itself.
@@ -1972,9 +2491,28 @@ function resolveSite(config, pageColours) {
1972
2491
  };
1973
2492
  }
1974
2493
  /** @param {{ config?: ColorsByMaxConfig, children: import('react').ReactNode }} props */
2494
+ /** Parts of the panel a site can switch off for everyone (config `features`). All on by default. */
2495
+ var FEATURES = {
2496
+ picks: true,
2497
+ library: true,
2498
+ search: true,
2499
+ surprise: true,
2500
+ scan: true,
2501
+ custom: true,
2502
+ overrides: true,
2503
+ importExport: true,
2504
+ audit: true,
2505
+ addToSite: true,
2506
+ colourStyle: true,
2507
+ colourCount: true
2508
+ };
1975
2509
  function ThemeProvider({ config = {}, children }) {
1976
2510
  const storageKey = config.storageKey || "colorsbymax";
1977
2511
  const [initialConfig] = useState(config);
2512
+ const features = useMemo(() => ({
2513
+ ...FEATURES,
2514
+ ...Object.fromEntries(Object.entries(config.features ?? {}).filter(([k, v]) => k in FEATURES && typeof v === "boolean"))
2515
+ }), [config.features]);
1978
2516
  const [pageColours, setPageColours] = useState(null);
1979
2517
  const site = useMemo(() => resolveSite(initialConfig, pageColours), [initialConfig, pageColours]);
1980
2518
  const { siteName, defaultTheme } = site;
@@ -1988,10 +2526,17 @@ function ThemeProvider({ config = {}, children }) {
1988
2526
  const find = (id) => allThemes.find((t) => t.id === id);
1989
2527
  const lightOfTwin = state.activeId.endsWith("~dark") ? find(state.activeId.slice(0, -DARK_SUFFIX.length)) : null;
1990
2528
  const base = find(state.activeId) ?? (lightOfTwin ? toDark(lightOfTwin) : null) ?? (state.snapshot?.id === state.activeId ? state.snapshot : null) ?? defaultTheme;
2529
+ const [paletteDefault, setPaletteDefault] = useState(() => loadSettings(storageKey, DEFAULT_SETTINGS).paletteSize);
2530
+ const coloursOf = useCallback((id) => coloursFor(state.paletteSizes, id, paletteDefault), [state.paletteSizes, paletteDefault]);
2531
+ const colourCount = coloursOf(base.id);
1991
2532
  const tokens = useMemo(() => ({
1992
- ...base.tokens,
2533
+ ...reducePalette(base.tokens, colourCount),
1993
2534
  ...state.overrides
1994
- }), [base, state.overrides]);
2535
+ }), [
2536
+ base,
2537
+ colourCount,
2538
+ state.overrides
2539
+ ]);
1995
2540
  const issues = useMemo(() => checkTheme(tokens), [tokens]);
1996
2541
  useLayoutEffect(() => {
1997
2542
  applyTokens(tokens);
@@ -2020,6 +2565,7 @@ function ThemeProvider({ config = {}, children }) {
2020
2565
  const settingDefaults = useMemo(() => ({
2021
2566
  ...DEFAULT_SETTINGS,
2022
2567
  colourLogo: Boolean(initialConfig.colourLogo),
2568
+ colourStyle: initialConfig.colourStyle === "subtle" ? "subtle" : "colourful",
2023
2569
  mode: [
2024
2570
  "light",
2025
2571
  "dark",
@@ -2030,6 +2576,18 @@ function ThemeProvider({ config = {}, children }) {
2030
2576
  useLayoutEffect(() => {
2031
2577
  recolourer.current?.setLogoColouring(logoColouring);
2032
2578
  }, [logoColouring, pageColours]);
2579
+ const [colourStyle, setColourStyle] = useState(() => loadSettings(storageKey, settingDefaults).colourStyle);
2580
+ useLayoutEffect(() => {
2581
+ recolourer.current?.setColourful(colourStyle === "colourful");
2582
+ }, [colourStyle, pageColours]);
2583
+ useLayoutEffect(() => {
2584
+ if (!pageColours) applyTokens(colourStyle === "subtle" ? subtleTokens(tokens) : tokens);
2585
+ }, [
2586
+ tokens,
2587
+ state,
2588
+ colourStyle,
2589
+ pageColours
2590
+ ]);
2033
2591
  useLayoutEffect(() => {
2034
2592
  if (logoColouring || pageColours) return;
2035
2593
  const style = document.createElement("style");
@@ -2171,6 +2729,22 @@ function ThemeProvider({ config = {}, children }) {
2171
2729
  recolouring: Boolean(pageColours),
2172
2730
  /** Turns re-colouring of the site's logo on or off (the switcher's "Colour the logo" setting). */
2173
2731
  setLogoColouring,
2732
+ /** Sets how boldly a re-coloured site takes the theme: 'colourful' or 'subtle'. */
2733
+ setColourStyle,
2734
+ /** Which parts of the panel are on (config `features`). */
2735
+ features,
2736
+ /** How many colours a theme uses (5 to 10): its own count, or the visitor's default. */
2737
+ coloursOf,
2738
+ /** Sets one theme's colour count (light and dark share it). */
2739
+ setThemeColours: (id, n) => setState((s) => ({
2740
+ ...s,
2741
+ paletteSizes: {
2742
+ ...s.paletteSizes,
2743
+ [baseId(id)]: clampColours(n)
2744
+ }
2745
+ })),
2746
+ /** The default count for every theme (the panel's settings); themes given their own count keep it. */
2747
+ setPaletteDefault,
2174
2748
  siteThemes,
2175
2749
  defaultTheme,
2176
2750
  presets: PRESETS,
@@ -2544,6 +3118,95 @@ var ToggleRight = icon("toggle-right", [["circle", {
2544
3118
  "y": "5",
2545
3119
  "rx": "7"
2546
3120
  }]]);
3121
+ var LibraryBig = icon("library-big", [
3122
+ ["rect", {
3123
+ "width": "8",
3124
+ "height": "18",
3125
+ "x": "3",
3126
+ "y": "3",
3127
+ "rx": "1"
3128
+ }],
3129
+ ["path", { "d": "M7 3v18" }],
3130
+ ["path", { "d": "M20.4 18.9c.2.5-.1 1.1-.6 1.3l-1.9.7c-.5.2-1.1-.1-1.3-.6L11.1 5.1c-.2-.5.1-1.1.6-1.3l1.9-.7c.5-.2 1.1.1 1.3.6Z" }]
3131
+ ]);
3132
+ var Minus = icon("minus", [["path", { "d": "M5 12h14" }]]);
3133
+ var Plus = icon("plus", [["path", { "d": "M5 12h14" }], ["path", { "d": "M12 5v14" }]]);
3134
+ //#endregion
3135
+ //#region src/burst.jsx
3136
+ var BURST_TIME = 1100;
3137
+ var BURST_KEYS = [
3138
+ "primary",
3139
+ "primary-alt",
3140
+ "data-1",
3141
+ "data-2",
3142
+ "data-3",
3143
+ "data-4",
3144
+ "warning",
3145
+ "data-5"
3146
+ ];
3147
+ var BURST_PIECES = Array.from({ length: 12 }, (_, i) => {
3148
+ const angle = (i * 30 + (i % 2 ? 9 : -6)) * (Math.PI / 180);
3149
+ const distance = 30 + i % 3 * 9;
3150
+ return {
3151
+ x: Math.round(Math.cos(angle) * distance),
3152
+ y: Math.round(Math.sin(angle) * distance),
3153
+ r: Math.round(angle * 180 / Math.PI),
3154
+ shape: [
3155
+ "squiggle",
3156
+ "dot",
3157
+ "dash"
3158
+ ][i % 3],
3159
+ delay: i % 4 * 25
3160
+ };
3161
+ });
3162
+ /** `spread` scales how far the pieces fly, for something wider than the colour button. */
3163
+ function Burst({ tokens, spread = 1 }) {
3164
+ return /* @__PURE__ */ jsx("span", {
3165
+ "aria-hidden": "true",
3166
+ className: "theme-burst pointer-events-none absolute inset-0",
3167
+ children: BURST_PIECES.map((p, i) => /* @__PURE__ */ jsx("span", {
3168
+ className: `theme-burst-piece ${p.shape === "squiggle" ? "" : `theme-piece-${p.shape}`}`,
3169
+ style: {
3170
+ "--x": `${Math.round(p.x * spread)}px`,
3171
+ "--y": `${Math.round(p.y * spread)}px`,
3172
+ "--r": `${p.r}deg`,
3173
+ color: tokens[BURST_KEYS[i % BURST_KEYS.length]],
3174
+ animationDelay: `${p.delay}ms`
3175
+ },
3176
+ children: p.shape === "squiggle" && /* @__PURE__ */ jsx("svg", {
3177
+ width: "16",
3178
+ height: "10",
3179
+ viewBox: "0 0 16 10",
3180
+ fill: "none",
3181
+ children: /* @__PURE__ */ jsx("path", {
3182
+ d: "M1 5c1.6-3.6 3.4-3.6 4.6 0s3 3.6 4.6 0 3-3.6 4.4 0",
3183
+ stroke: "currentColor",
3184
+ strokeWidth: "2.2",
3185
+ strokeLinecap: "round"
3186
+ })
3187
+ })
3188
+ }, i))
3189
+ });
3190
+ }
3191
+ /**
3192
+ * A burst to show on demand: `celebrate()` starts one (restarting it if one is running), and
3193
+ * `burst` is the key to render `<Burst key={burst} />` with while `bursting`.
3194
+ */
3195
+ function useBurst() {
3196
+ const [burst, setBurst] = useState(0);
3197
+ const [bursting, setBursting] = useState(false);
3198
+ useEffect(() => {
3199
+ if (!burst) return;
3200
+ setBursting(true);
3201
+ const id = setTimeout(() => setBursting(false), BURST_TIME);
3202
+ return () => clearTimeout(id);
3203
+ }, [burst]);
3204
+ return {
3205
+ burst,
3206
+ bursting,
3207
+ celebrate: () => setBurst((n) => n + 1)
3208
+ };
3209
+ }
2547
3210
  /** Images are scaled down to at most this many pixels on their longest side before sampling. */
2548
3211
  var SAMPLE_SIZE = 200;
2549
3212
  /** PDF pages read, from the first. */
@@ -2555,7 +3218,7 @@ var MIN_SHARE = .004;
2555
3218
  /** How close (CIE76 ΔE) a colour must be to a mix of two others to count as their edge blend. */
2556
3219
  var BLEND_DELTA_E = 6;
2557
3220
  var HEX_IN_TEXT = /#([0-9a-f]{6}|[0-9a-f]{3})\b/gi;
2558
- var hslOf = (hex) => rgbToHsl(hexToRgb(hex));
3221
+ var hslOf$1 = (hex) => rgbToHsl(hexToRgb(hex));
2559
3222
  /**
2560
3223
  * The main colours in an image or PDF, most important first.
2561
3224
  * @param {File} file
@@ -2709,7 +3372,7 @@ var expandHex = (h) => `#${(h.length === 3 ? h.replace(/./g, (c) => c + c) : h).
2709
3372
  */
2710
3373
  function rolesFromPalette(colours) {
2711
3374
  const info = colours.map((hex, i) => {
2712
- const [h, s, l] = hslOf(hex);
3375
+ const [h, s, l] = hslOf$1(hex);
2713
3376
  return {
2714
3377
  hex,
2715
3378
  h,
@@ -2878,19 +3541,214 @@ function removePreview() {
2878
3541
  for (const el of document.querySelectorAll(`[${PREVIEW}]`)) el.remove();
2879
3542
  }
2880
3543
  //#endregion
2881
- //#region src/ThemePanel.jsx
2882
- var SWATCH_KEYS = [
2883
- "primary",
2884
- "primary-alt",
2885
- "primary-dark",
2886
- "secondary",
2887
- "background",
2888
- "ink",
2889
- "data-1",
2890
- "data-2",
2891
- "data-3",
2892
- "data-4"
3544
+ //#region src/logoMark.jsx
3545
+ var BARS = [
3546
+ "M57.13,163.05H0C28.34,108.7,56.67,54.35,85.01,0h55.44c-27.77,54.35-55.55,108.7-83.32,163.05Z",
3547
+ "M134.89,163.05h-57.13C106.1,108.7,134.43,54.35,162.77,0h55.44c-27.77,54.35-55.55,108.7-83.32,163.05Z",
3548
+ "M212.65,163.05h-57.13C183.86,108.7,212.19,54.35,240.53,0h55.44c-27.77,54.35-55.55,108.7-83.32,163.05Z"
3549
+ ];
3550
+ var MIN_CONTRAST = 3;
3551
+ var hue = (hex) => rgbToHsl(hexToRgb(hex))[0];
3552
+ var hueGap = (a, b) => {
3553
+ const d = Math.abs(hue(a) - hue(b));
3554
+ return Math.min(d, 360 - d);
3555
+ };
3556
+ /** The three bars' colours for a theme. */
3557
+ function logoColours(tokens) {
3558
+ const [a, b] = [tokens["primary-alt"], tokens.primary];
3559
+ const apart = (x) => Math.min(hueGap(x, a), hueGap(x, b));
3560
+ let c = tokens["data-3"];
3561
+ for (let n = 1; n <= 8; n++) if (apart(tokens[`data-${n}`]) > apart(c)) c = tokens[`data-${n}`];
3562
+ return [
3563
+ a,
3564
+ b,
3565
+ c
3566
+ ];
3567
+ }
3568
+ /** Moves a colour towards black (on light) or white (on dark) until it reads on `background`. */
3569
+ function standOut(hex, background) {
3570
+ const target = contrastRatio(background, "#000000") > contrastRatio(background, "#ffffff") ? "#000000" : "#ffffff";
3571
+ for (let t = 0; t <= 1; t += .05) {
3572
+ const out = mix(target, hex, t);
3573
+ if (contrastRatio(out, background) >= MIN_CONTRAST) return out;
3574
+ }
3575
+ return target;
3576
+ }
3577
+ /** @param {{ tokens: Record<string, string>, background: string, className?: string }} props */
3578
+ function LogoMark({ tokens, background, className = "" }) {
3579
+ const colours = logoColours(tokens).map((c) => standOut(c, background));
3580
+ return /* @__PURE__ */ jsx("svg", {
3581
+ viewBox: "0 0 296 163.05",
3582
+ className,
3583
+ "aria-hidden": "true",
3584
+ children: BARS.map((d, i) => /* @__PURE__ */ jsx("path", {
3585
+ d,
3586
+ fill: colours[i]
3587
+ }, i))
3588
+ });
3589
+ }
3590
+ //#endregion
3591
+ //#region src/colourWords.js
3592
+ var inHue = (h, from, to) => from <= to ? h >= from && h <= to : h >= from || h <= to;
3593
+ /** [word, other words for it, test on [hue 0-360, saturation 0-1, lightness 0-1]] */
3594
+ var COLOURS = [
3595
+ [
3596
+ "red",
3597
+ ["crimson", "scarlet"],
3598
+ ([h, s, l]) => inHue(h, 345, 12) && s >= .35 && l >= .2 && l <= .7
3599
+ ],
3600
+ [
3601
+ "orange",
3602
+ ["tangerine"],
3603
+ ([h, s, l]) => inHue(h, 13, 38) && s >= .45 && l >= .35
3604
+ ],
3605
+ [
3606
+ "coral",
3607
+ ["salmon", "peach"],
3608
+ ([h, s, l]) => inHue(h, 0, 25) && s >= .45 && l >= .55
3609
+ ],
3610
+ [
3611
+ "yellow",
3612
+ ["lemon"],
3613
+ ([h, s, l]) => inHue(h, 45, 64) && s >= .45 && l >= .45
3614
+ ],
3615
+ [
3616
+ "gold",
3617
+ ["mustard", "amber"],
3618
+ ([h, s, l]) => inHue(h, 36, 52) && s >= .4 && l >= .3 && l <= .65
3619
+ ],
3620
+ [
3621
+ "lime",
3622
+ ["chartreuse"],
3623
+ ([h, s, l]) => inHue(h, 65, 95) && s >= .45 && l >= .35
3624
+ ],
3625
+ [
3626
+ "green",
3627
+ [
3628
+ "emerald",
3629
+ "olive",
3630
+ "forest",
3631
+ "sage",
3632
+ "mint"
3633
+ ],
3634
+ ([h, s]) => inHue(h, 75, 160) && s >= .2
3635
+ ],
3636
+ [
3637
+ "teal",
3638
+ ["turquoise"],
3639
+ ([h, s]) => inHue(h, 160, 195) && s >= .25
3640
+ ],
3641
+ [
3642
+ "cyan",
3643
+ ["aqua"],
3644
+ ([h, s, l]) => inHue(h, 180, 200) && s >= .45 && l >= .4
3645
+ ],
3646
+ [
3647
+ "blue",
3648
+ [
3649
+ "sky",
3650
+ "cobalt",
3651
+ "azure"
3652
+ ],
3653
+ ([h, s]) => inHue(h, 196, 250) && s >= .25
3654
+ ],
3655
+ [
3656
+ "navy",
3657
+ ["indigo"],
3658
+ ([h, s, l]) => inHue(h, 205, 255) && s >= .2 && l <= .35
3659
+ ],
3660
+ [
3661
+ "purple",
3662
+ [
3663
+ "violet",
3664
+ "plum",
3665
+ "lavender",
3666
+ "lilac"
3667
+ ],
3668
+ ([h, s]) => inHue(h, 251, 295) && s >= .2
3669
+ ],
3670
+ [
3671
+ "pink",
3672
+ [
3673
+ "rose",
3674
+ "blush",
3675
+ "fuchsia",
3676
+ "magenta",
3677
+ "berry"
3678
+ ],
3679
+ ([h, s, l]) => inHue(h, 296, 344) && s >= .3 && l >= .3
3680
+ ],
3681
+ [
3682
+ "brown",
3683
+ [
3684
+ "chocolate",
3685
+ "coffee",
3686
+ "camel",
3687
+ "rust",
3688
+ "terracotta"
3689
+ ],
3690
+ ([h, s, l]) => inHue(h, 10, 45) && s >= .2 && l <= .45
3691
+ ],
3692
+ [
3693
+ "beige",
3694
+ [
3695
+ "cream",
3696
+ "sand",
3697
+ "tan",
3698
+ "ivory"
3699
+ ],
3700
+ ([h, s, l]) => inHue(h, 25, 60) && s >= .1 && l >= .6
3701
+ ],
3702
+ [
3703
+ "grey",
3704
+ [
3705
+ "gray",
3706
+ "slate",
3707
+ "silver",
3708
+ "charcoal",
3709
+ "monochrome"
3710
+ ],
3711
+ ([, s]) => s < .14
3712
+ ],
3713
+ [
3714
+ "black",
3715
+ ["dark"],
3716
+ ([, , l]) => l <= .18
3717
+ ],
3718
+ [
3719
+ "white",
3720
+ ["light"],
3721
+ ([, , l]) => l >= .9
3722
+ ]
2893
3723
  ];
3724
+ var WORDS = [...COLOURS.map(([word]) => word), ...COLOURS.flatMap(([, aliases]) => aliases)];
3725
+ var hslOf = (hex) => rgbToHsl(hexToRgb(hex));
3726
+ /**
3727
+ * The colour test for a search, or null if it isn't a colour word. "reds" and "Red" count too.
3728
+ * @returns {((tokens: Record<string, string>) => boolean) | null}
3729
+ */
3730
+ function colourMatcher(query) {
3731
+ const q = query.trim().toLowerCase().replace(/s$/, "");
3732
+ const found = COLOURS.find(([word, aliases]) => word === q || aliases.includes(q));
3733
+ if (!found) return null;
3734
+ const [, , test] = found;
3735
+ const keys = ["black", "white"].includes(found[0]) ? [
3736
+ "primary",
3737
+ "primary-dark",
3738
+ "background"
3739
+ ] : ["primary", "primary-alt"];
3740
+ return (tokens) => keys.some((k) => tokens[k] && test(hslOf(tokens[k])));
3741
+ }
3742
+ /** Words that finish what's being typed (at most `max`), colours first, then `extra` (e.g. moods). */
3743
+ function suggestWords(query, extra = [], max = 6) {
3744
+ const q = query.trim().toLowerCase();
3745
+ if (!q) return [];
3746
+ const starts = [...WORDS, ...extra.map((w) => w.toLowerCase())].filter((w) => w.startsWith(q) && w !== q);
3747
+ return [...new Set(starts)].slice(0, max);
3748
+ }
3749
+ //#endregion
3750
+ //#region src/ThemePanel.jsx
3751
+ var PANEL_DARK = "#18181b";
2894
3752
  var btn = "inline-flex items-center justify-center gap-1.5 rounded-lg border border-zinc-300 bg-white px-2.5 py-1.5 text-xs font-medium text-zinc-800 hover:bg-zinc-50 disabled:opacity-50 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1";
2895
3753
  var btnPrimary = "inline-flex items-center justify-center gap-1.5 rounded-lg bg-zinc-900 px-2.5 py-1.5 text-xs font-medium text-white hover:bg-zinc-700 disabled:opacity-50 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-2";
2896
3754
  var field = "w-full rounded-lg border border-zinc-300 bg-white px-2.5 py-1.5 text-sm text-zinc-900 placeholder:text-zinc-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900";
@@ -2924,7 +3782,12 @@ function useSavedToYours() {
2924
3782
  });
2925
3783
  };
2926
3784
  }
2927
- function ThemePanel() {
3785
+ /**
3786
+ * The panel's contents. It stays mounted while the panel is closed (see ThemeSwitcher), so closing
3787
+ * it is like minimising: the chosen group or mood, a search, open sections and the view all stay.
3788
+ * `open` brings the scroll position back, since a hidden panel can't keep its own.
3789
+ */
3790
+ function ThemePanel({ open = true }) {
2928
3791
  const theme = useTheme();
2929
3792
  const { settings, mode, audit, update } = useSettings();
2930
3793
  const overrideCount = Object.keys(theme.state.overrides).length;
@@ -2933,6 +3796,11 @@ function ThemePanel() {
2933
3796
  const rootRef = useRef(null);
2934
3797
  const [toasts, setToasts] = useState([]);
2935
3798
  const [reveal, setReveal] = useState(null);
3799
+ const scrollTop = useRef(0);
3800
+ useLayoutEffect(() => {
3801
+ const el = rootRef.current;
3802
+ if (open && el) el.scrollTop = scrollTop.current;
3803
+ }, [open]);
2936
3804
  const toast = useCallback((text, action) => setToasts((list) => [...list.slice(-2), {
2937
3805
  id: ++toastSeq,
2938
3806
  text,
@@ -2975,22 +3843,32 @@ function ThemePanel() {
2975
3843
  value: panelApi,
2976
3844
  children: [/* @__PURE__ */ jsxs("div", {
2977
3845
  ref: rootRef,
3846
+ onScroll: (e) => {
3847
+ if (open) scrollTop.current = e.currentTarget.scrollTop;
3848
+ },
2978
3849
  className: "theme-scroll @container flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain rounded-[inherit]",
2979
3850
  children: [
2980
3851
  /* @__PURE__ */ jsxs("header", {
2981
- className: "sticky top-0 z-10 flex items-start justify-between gap-3 border-b border-zinc-200 bg-white px-4 py-3",
2982
- children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("h2", {
2983
- id: "theme-panel-title",
2984
- className: "text-base font-semibold tracking-tight",
2985
- children: ["colorsbymax", /* @__PURE__ */ jsx("span", {
2986
- className: "align-super text-[10px] font-medium",
2987
- children: "™"
2988
- })]
2989
- }), /* @__PURE__ */ jsx("p", {
2990
- className: "text-[11px] text-zinc-500",
2991
- children: "by mrmaxdesigns"
2992
- })] }), /* @__PURE__ */ jsxs("div", {
2993
- className: "flex items-center gap-1",
3852
+ className: "sticky top-0 z-10 flex flex-wrap items-center justify-between gap-x-3 gap-y-3.5 border-b border-zinc-200 bg-white px-4 py-3",
3853
+ children: [/* @__PURE__ */ jsxs("div", {
3854
+ className: "flex w-full shrink-0 items-center justify-center gap-2 @min-[520px]:w-auto @min-[520px]:justify-start",
3855
+ children: [/* @__PURE__ */ jsx(LogoMark, {
3856
+ tokens: theme.tokens,
3857
+ background: mode === "dark" ? PANEL_DARK : "#ffffff",
3858
+ className: "h-7 w-auto shrink-0"
3859
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("h2", {
3860
+ id: "theme-panel-title",
3861
+ className: "text-base font-semibold tracking-tight",
3862
+ children: ["colorsbymax", /* @__PURE__ */ jsx("span", {
3863
+ className: "align-super text-[10px] font-medium",
3864
+ children: "™"
3865
+ })]
3866
+ }), /* @__PURE__ */ jsx("p", {
3867
+ className: "text-[11px] text-zinc-500",
3868
+ children: "by mrmaxdesigns"
3869
+ })] })]
3870
+ }), /* @__PURE__ */ jsxs("div", {
3871
+ className: "flex w-full items-center justify-center gap-1 @min-[520px]:ml-auto @min-[520px]:w-auto",
2994
3872
  children: [
2995
3873
  /* @__PURE__ */ jsx("div", {
2996
3874
  role: "radiogroup",
@@ -3013,7 +3891,7 @@ function ThemePanel() {
3013
3891
  }, id);
3014
3892
  })
3015
3893
  }),
3016
- /* @__PURE__ */ jsxs("button", {
3894
+ theme.features.addToSite && /* @__PURE__ */ jsxs("button", {
3017
3895
  type: "button",
3018
3896
  onClick: (e) => view === "mode-toggle" ? setView("main") : showView("mode-toggle", e.currentTarget),
3019
3897
  className: `${btn} shrink-0 whitespace-nowrap px-1.5 @min-[500px]:px-2 ${view === "mode-toggle" ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
@@ -3028,7 +3906,7 @@ function ThemePanel() {
3028
3906
  children: "Add to site"
3029
3907
  })]
3030
3908
  }),
3031
- /* @__PURE__ */ jsxs("button", {
3909
+ theme.features.audit && /* @__PURE__ */ jsxs("button", {
3032
3910
  type: "button",
3033
3911
  onClick: audit.toggle,
3034
3912
  className: `${btn} px-2 ${audit.on ? "border-zinc-900 bg-zinc-100" : "border-transparent"}`,
@@ -3065,7 +3943,6 @@ function ThemePanel() {
3065
3943
  children: [
3066
3944
  /* @__PURE__ */ jsx(Section, {
3067
3945
  title: "Preset themes",
3068
- defaultOpen: true,
3069
3946
  children: /* @__PURE__ */ jsx(PresetGrid, {})
3070
3947
  }),
3071
3948
  settings.showCustom && /* @__PURE__ */ jsx(Section, {
@@ -3365,8 +4242,9 @@ var FINISH_OPTIONS = [
3365
4242
  * here only, or remove colorsbymax, each with code and a prompt for an AI editor. Cancel goes back.
3366
4243
  */
3367
4244
  function FinishView({ onBack }) {
3368
- const { tokens, active, recolouring } = useTheme();
3369
- const { update } = useSettings();
4245
+ const { tokens: themeTokens, active, recolouring } = useTheme();
4246
+ const { settings, update } = useSettings();
4247
+ const tokens = !recolouring && settings.colourStyle === "subtle" ? subtleTokens(themeTokens) : themeTokens;
3370
4248
  const [choice, setChoice] = useState("keep");
3371
4249
  const [kind, setKind] = useState(() => document.querySelector("[data-colorsbymax=\"auto\"]") ? "auto" : "provider");
3372
4250
  const headingRef = useRef(null);
@@ -3613,6 +4491,7 @@ var MODES = [
3613
4491
  /** The visitor's preferences for the panel and colour button. */
3614
4492
  function SettingsView({ onBack }) {
3615
4493
  const { settings, update, reset, resetButton } = useSettings();
4494
+ const { active, features } = useTheme();
3616
4495
  const { toast } = usePanel();
3617
4496
  const headingRef = useRef(null);
3618
4497
  useEffect(() => headingRef.current?.focus(), []);
@@ -3669,6 +4548,40 @@ function SettingsView({ onBack }) {
3669
4548
  })
3670
4549
  ]
3671
4550
  }),
4551
+ features.colourCount && /* @__PURE__ */ jsxs("fieldset", {
4552
+ className: "space-y-1.5",
4553
+ children: [
4554
+ /* @__PURE__ */ jsx("legend", {
4555
+ className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-zinc-600",
4556
+ children: "Colours per theme"
4557
+ }),
4558
+ /* @__PURE__ */ jsxs("div", {
4559
+ className: "flex items-center gap-3 rounded-xl border border-zinc-200 p-2.5",
4560
+ children: [/* @__PURE__ */ jsx("div", {
4561
+ className: "min-w-0 flex-1",
4562
+ children: /* @__PURE__ */ jsx(Swatches, {
4563
+ tokens: active.tokens,
4564
+ count: settings.paletteSize
4565
+ })
4566
+ }), /* @__PURE__ */ jsx(ColourStepper, {
4567
+ value: settings.paletteSize,
4568
+ onChange: (n) => update({ paletteSize: n }),
4569
+ label: "Colours every theme uses",
4570
+ size: "md"
4571
+ })]
4572
+ }),
4573
+ /* @__PURE__ */ jsxs("p", {
4574
+ className: "text-[11px] text-zinc-600",
4575
+ children: [
4576
+ "How many colours every theme uses, from ",
4577
+ 5,
4578
+ " to ",
4579
+ 10,
4580
+ ". Fewer keeps a site calmer, with only a theme’s main colours; the ones that fit it best stay. You can still change it on any theme with − and + on its card."
4581
+ ]
4582
+ })
4583
+ ]
4584
+ }),
3672
4585
  /* @__PURE__ */ jsxs("fieldset", {
3673
4586
  className: "space-y-1.5",
3674
4587
  children: [
@@ -3711,29 +4624,29 @@ function SettingsView({ onBack }) {
3711
4624
  className: "mb-1 text-[11px] font-semibold uppercase tracking-wide text-zinc-600",
3712
4625
  children: "Show"
3713
4626
  }),
3714
- /* @__PURE__ */ jsx(Toggle, {
4627
+ features.picks && /* @__PURE__ */ jsx(Toggle, {
3715
4628
  checked: settings.showPicks,
3716
4629
  onChange: (v) => update({ showPicks: v }),
3717
4630
  label: "Max’s picks",
3718
4631
  note: "Hand-tuned colorsbymax themes"
3719
4632
  }),
3720
- /* @__PURE__ */ jsx(Toggle, {
4633
+ features.library && /* @__PURE__ */ jsx(Toggle, {
3721
4634
  checked: settings.showLibrary,
3722
4635
  onChange: (v) => update({ showLibrary: v }),
3723
4636
  label: "Theme library",
3724
4637
  note: "Hundreds of themes in categories, with search"
3725
4638
  }),
3726
- /* @__PURE__ */ jsx(Toggle, {
4639
+ features.custom && /* @__PURE__ */ jsx(Toggle, {
3727
4640
  checked: settings.showCustom,
3728
4641
  onChange: (v) => update({ showCustom: v }),
3729
4642
  label: "Custom palettes"
3730
4643
  }),
3731
- /* @__PURE__ */ jsx(Toggle, {
4644
+ features.overrides && /* @__PURE__ */ jsx(Toggle, {
3732
4645
  checked: settings.showOverrides,
3733
4646
  onChange: (v) => update({ showOverrides: v }),
3734
4647
  label: "Override a single colour"
3735
4648
  }),
3736
- /* @__PURE__ */ jsx(Toggle, {
4649
+ features.importExport && /* @__PURE__ */ jsx(Toggle, {
3737
4650
  checked: settings.showImportExport,
3738
4651
  onChange: (v) => update({ showImportExport: v }),
3739
4652
  label: "Import / export"
@@ -3951,20 +4864,78 @@ function IssuesLink() {
3951
4864
  ]
3952
4865
  });
3953
4866
  }
3954
- function Swatches({ tokens }) {
4867
+ /** A theme's colours as a strip: the `count` it uses (5 to 10), its most fitting ones. */
4868
+ function Swatches({ tokens, count = 10 }) {
3955
4869
  return /* @__PURE__ */ jsx("div", {
3956
4870
  className: "flex overflow-hidden rounded-md border border-zinc-200",
3957
4871
  "aria-hidden": "true",
3958
- children: SWATCH_KEYS.map((key) => /* @__PURE__ */ jsx("span", {
4872
+ children: paletteKeys(tokens, count).map((key) => /* @__PURE__ */ jsx("span", {
3959
4873
  className: "h-5 flex-1",
3960
4874
  style: { background: tokens[key] }
3961
4875
  }, key))
3962
4876
  });
3963
4877
  }
4878
+ /**
4879
+ * − n + for how many colours a theme uses. Each end dims when it's as far as it goes.
4880
+ * @param {{ value: number, onChange: (n: number) => void, label: string, size?: 'sm' | 'md' }} props
4881
+ */
4882
+ function ColourStepper({ value, onChange, label, size = "sm" }) {
4883
+ const box = size === "sm" ? "h-6 w-6" : "h-8 w-8";
4884
+ const step = (d) => (e) => {
4885
+ e.stopPropagation();
4886
+ onChange(value + d);
4887
+ };
4888
+ return /* @__PURE__ */ jsxs("span", {
4889
+ role: "group",
4890
+ "aria-label": label,
4891
+ className: "inline-flex items-center gap-1",
4892
+ children: [
4893
+ /* @__PURE__ */ jsx("button", {
4894
+ type: "button",
4895
+ onClick: step(-1),
4896
+ disabled: value <= 5,
4897
+ "aria-label": `Fewer colours (${value} now)`,
4898
+ "data-tip": value <= 5 ? `5 is the fewest` : "Use fewer colours",
4899
+ className: `${box} grid place-items-center rounded-full border border-zinc-300 bg-white text-zinc-800 cursor-pointer hover:border-zinc-900 disabled:cursor-default disabled:opacity-35 disabled:hover:border-zinc-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900`,
4900
+ children: /* @__PURE__ */ jsx(Minus, {
4901
+ className: "w-3.5 h-3.5",
4902
+ "aria-hidden": "true"
4903
+ })
4904
+ }),
4905
+ /* @__PURE__ */ jsx("span", {
4906
+ className: "min-w-[1.25rem] text-center text-xs font-semibold tabular-nums text-zinc-900",
4907
+ "aria-live": "polite",
4908
+ children: value
4909
+ }),
4910
+ /* @__PURE__ */ jsx("button", {
4911
+ type: "button",
4912
+ onClick: step(1),
4913
+ disabled: value >= 10,
4914
+ "aria-label": `More colours (${value} now)`,
4915
+ "data-tip": value >= 10 ? `10 is every colour` : "Use more colours",
4916
+ className: `${box} grid place-items-center rounded-full border border-zinc-300 bg-white text-zinc-800 cursor-pointer hover:border-zinc-900 disabled:cursor-default disabled:opacity-35 disabled:hover:border-zinc-300 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900`,
4917
+ children: /* @__PURE__ */ jsx(Plus, {
4918
+ className: "w-3.5 h-3.5",
4919
+ "aria-hidden": "true"
4920
+ })
4921
+ })
4922
+ ]
4923
+ });
4924
+ }
3964
4925
  var PAGE_SIZE = 24;
3965
4926
  var NO_THEMES = [];
4927
+ var COLOUR_STYLES = [{
4928
+ id: "subtle",
4929
+ label: "Subtle",
4930
+ Icon: Contrast
4931
+ }, {
4932
+ id: "colourful",
4933
+ label: "Colourful",
4934
+ Icon: Paintbrush
4935
+ }];
3966
4936
  function PresetGrid() {
3967
- const { siteName, siteThemes, presets: allPresets, customs, active, selectTheme, state, clearOverrides } = useTheme();
4937
+ const { siteName, siteThemes, presets: allPresets, customs, active, selectTheme, state, clearOverrides, tokens, recolouring, features } = useTheme();
4938
+ const surpriseBurst = useBurst();
3968
4939
  const { settings, mode, update } = useSettings();
3969
4940
  const categoriesId = useId();
3970
4941
  const [loaded, setLoaded] = useState(null);
@@ -3993,6 +4964,8 @@ function PresetGrid() {
3993
4964
  if (scrollToThemes.current && themesRef.current) {
3994
4965
  scrollToThemes.current = false;
3995
4966
  const still = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
4967
+ const header = themesRef.current.closest(".theme-scroll")?.querySelector("header");
4968
+ themesRef.current.style.scrollMarginTop = `${(header?.offsetHeight ?? 0) + 12}px`;
3996
4969
  themesRef.current.scrollIntoView({
3997
4970
  block: "start",
3998
4971
  behavior: still ? "auto" : "smooth"
@@ -4067,7 +5040,9 @@ function PresetGrid() {
4067
5040
  else list = (library?.themes ?? []).filter((t) => t.tags.includes(category));
4068
5041
  if (q) {
4069
5042
  const labels = Object.fromEntries((library?.categories ?? []).map((c) => [c.id, c.label.toLowerCase()]));
4070
- list = list.filter((t) => t.name.toLowerCase().includes(q) || t.tags?.some((tag) => labels[tag]?.includes(q)));
5043
+ const byName = (t) => t.name.toLowerCase().includes(q) || t.tags?.some((tag) => labels[tag]?.includes(q));
5044
+ const byColour = colourMatcher(q);
5045
+ list = [...list.filter(byName), ...byColour ? list.filter((t) => !byName(t) && byColour(t.tokens)) : []];
4071
5046
  }
4072
5047
  return list;
4073
5048
  }, [
@@ -4093,14 +5068,20 @@ function PresetGrid() {
4093
5068
  };
4094
5069
  const pick = (t) => selectTheme(t.id, t);
4095
5070
  const surprise = () => {
4096
- const pool = visible.length ? visible : library?.themes ?? [...siteThemes, ...presets];
5071
+ const everything = library?.themes ?? [...siteThemes, ...presets];
5072
+ const pool = (visible.length > 1 ? visible : everything).filter((t) => t.id !== active.id && inMode(t, mode).id !== active.id);
5073
+ if (!pool.length) return;
4097
5074
  pick(inMode(pool[Math.floor(Math.random() * pool.length)], mode));
5075
+ surpriseBurst.celebrate();
4098
5076
  };
5077
+ const suggestions = suggestWords(query, categories.map((c) => c.label));
5078
+ const themeCount = (library?.themes.length ?? 0) + presets.length + siteThemes.length + customs.length;
5079
+ const roughCount = themeCount >= 100 ? `${Math.floor(themeCount / 100) * 100}+` : themeCount;
4099
5080
  return /* @__PURE__ */ jsxs("div", {
4100
5081
  ref: wrapRef,
4101
5082
  className: "space-y-3",
4102
5083
  children: [
4103
- /* @__PURE__ */ jsx(ScanCard, {}),
5084
+ features.scan && /* @__PURE__ */ jsx(ScanCard, {}),
4104
5085
  /* @__PURE__ */ jsxs("p", {
4105
5086
  className: "text-xs text-zinc-600",
4106
5087
  children: ["Current theme: ", /* @__PURE__ */ jsx("strong", {
@@ -4108,6 +5089,31 @@ function PresetGrid() {
4108
5089
  children: active.name
4109
5090
  })]
4110
5091
  }),
5092
+ features.colourStyle && /* @__PURE__ */ jsxs("div", {
5093
+ className: "rounded-xl border border-zinc-200 p-2.5",
5094
+ children: [/* @__PURE__ */ jsx("div", {
5095
+ role: "radiogroup",
5096
+ "aria-label": "Colour style",
5097
+ className: "grid grid-cols-2 gap-1 rounded-lg bg-zinc-100 p-1",
5098
+ children: COLOUR_STYLES.map(({ id, label, Icon }) => {
5099
+ const on = settings.colourStyle === id;
5100
+ return /* @__PURE__ */ jsxs("button", {
5101
+ type: "button",
5102
+ role: "radio",
5103
+ "aria-checked": on,
5104
+ onClick: () => update({ colourStyle: id }),
5105
+ className: `inline-flex h-8 cursor-pointer items-center justify-center gap-1.5 rounded-md text-xs font-semibold focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${on ? "bg-white text-zinc-900 shadow-sm" : "text-zinc-600 hover:text-zinc-900"}`,
5106
+ children: [/* @__PURE__ */ jsx(Icon, {
5107
+ className: "w-3.5 h-3.5",
5108
+ "aria-hidden": "true"
5109
+ }), label]
5110
+ }, id);
5111
+ })
5112
+ }), /* @__PURE__ */ jsx("p", {
5113
+ className: "mt-2 px-0.5 text-[11px] leading-snug text-zinc-600",
5114
+ children: settings.colourStyle === "colourful" ? "Colourful: the theme paints your header, hero, sections, cards, buttons and footer, like a designer would." : recolouring ? "Subtle: the theme only swaps the colours your site already has." : "Subtle: calm, nearly neutral backgrounds and cards, with the theme’s colour on buttons, links and highlights."
5115
+ })]
5116
+ }),
4111
5117
  overrideCount > 0 && /* @__PURE__ */ jsxs("p", {
4112
5118
  className: "flex items-center justify-between gap-2 rounded-lg bg-zinc-100 px-3 py-2 text-xs text-zinc-700",
4113
5119
  children: [
@@ -4126,38 +5132,94 @@ function PresetGrid() {
4126
5132
  })
4127
5133
  ]
4128
5134
  }),
4129
- /* @__PURE__ */ jsxs("div", {
5135
+ (features.search || features.surprise) && /* @__PURE__ */ jsxs("div", {
4130
5136
  className: "flex gap-2",
4131
- children: [
4132
- /* @__PURE__ */ jsx("label", {
4133
- htmlFor: searchId,
4134
- className: "sr-only",
4135
- children: "Search themes"
4136
- }),
4137
- /* @__PURE__ */ jsx("input", {
4138
- id: searchId,
4139
- type: "search",
4140
- className: field,
4141
- placeholder: `Search ${(library?.themes.length ?? 0) + presets.length + siteThemes.length + customs.length} themes…`,
4142
- value: query,
4143
- onChange: (e) => {
4144
- setQuery(e.target.value);
4145
- setLimit(PAGE_SIZE);
4146
- }
4147
- }),
4148
- /* @__PURE__ */ jsxs("button", {
4149
- type: "button",
4150
- className: `${btn} shrink-0`,
4151
- onClick: surprise,
4152
- disabled: !library && needsLibrary,
4153
- children: [/* @__PURE__ */ jsx(Shuffle, {
5137
+ children: [features.search && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("label", {
5138
+ htmlFor: searchId,
5139
+ className: "sr-only",
5140
+ children: "Search themes"
5141
+ }), /* @__PURE__ */ jsx("input", {
5142
+ id: searchId,
5143
+ type: "search",
5144
+ className: field,
5145
+ placeholder: `Search ${roughCount} themes…`,
5146
+ value: query,
5147
+ onChange: (e) => {
5148
+ setQuery(e.target.value);
5149
+ setLimit(PAGE_SIZE);
5150
+ }
5151
+ })] }), features.surprise && /* @__PURE__ */ jsxs("button", {
5152
+ type: "button",
5153
+ className: `${btn} relative shrink-0 ${features.search ? "" : "flex-1"}`,
5154
+ onClick: surprise,
5155
+ disabled: !library && needsLibrary,
5156
+ children: [
5157
+ /* @__PURE__ */ jsx(Shuffle, {
4154
5158
  className: "w-3.5 h-3.5",
4155
5159
  "aria-hidden": "true"
4156
- }), " Surprise me"]
5160
+ }),
5161
+ /* @__PURE__ */ jsxs("span", { children: ["Surprise", /* @__PURE__ */ jsx("span", {
5162
+ className: "hidden @min-[400px]:inline",
5163
+ children: " me"
5164
+ })] }),
5165
+ surpriseBurst.bursting && /* @__PURE__ */ jsx(Burst, {
5166
+ tokens,
5167
+ spread: 1.7
5168
+ }, surpriseBurst.burst)
5169
+ ]
5170
+ })]
5171
+ }),
5172
+ suggestions.length > 0 && /* @__PURE__ */ jsxs("div", {
5173
+ role: "group",
5174
+ "aria-label": "Suggestions",
5175
+ className: "-mt-1 flex flex-wrap items-center gap-1.5",
5176
+ children: [/* @__PURE__ */ jsx("span", {
5177
+ className: "text-[11px] text-zinc-500",
5178
+ children: "Try"
5179
+ }), suggestions.map((w) => /* @__PURE__ */ jsx("button", {
5180
+ type: "button",
5181
+ onClick: () => {
5182
+ setQuery(w);
5183
+ setLimit(PAGE_SIZE);
5184
+ },
5185
+ className: "rounded-full border border-zinc-300 bg-white px-2.5 py-0.5 text-xs font-medium text-zinc-800 capitalize hover:border-zinc-900 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
5186
+ children: w
5187
+ }, w))]
5188
+ }),
5189
+ q && /* @__PURE__ */ jsxs("div", {
5190
+ className: "flex items-center justify-between gap-2",
5191
+ children: [/* @__PURE__ */ jsxs("p", {
5192
+ className: "text-xs text-zinc-600",
5193
+ role: "status",
5194
+ children: [
5195
+ visible.length,
5196
+ " ",
5197
+ visible.length === 1 ? "theme" : "themes",
5198
+ " for “",
5199
+ query.trim(),
5200
+ "”"
5201
+ ]
5202
+ }), /* @__PURE__ */ jsx("div", {
5203
+ role: "radiogroup",
5204
+ "aria-label": "Show them in",
5205
+ className: "flex shrink-0 items-center rounded-lg border border-zinc-200 p-0.5",
5206
+ children: MODES.filter((m) => m.id !== "system").map(({ id, label, Icon }) => {
5207
+ const on = mode === id;
5208
+ return /* @__PURE__ */ jsxs("button", {
5209
+ type: "button",
5210
+ role: "radio",
5211
+ "aria-checked": on,
5212
+ onClick: () => update({ mode: id }),
5213
+ className: `inline-flex h-7 items-center gap-1 rounded-md px-2 text-xs font-semibold cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${on ? "bg-zinc-900 text-white" : "text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900"}`,
5214
+ children: [/* @__PURE__ */ jsx(Icon, {
5215
+ className: "w-3.5 h-3.5",
5216
+ "aria-hidden": "true"
5217
+ }), label]
5218
+ }, id);
4157
5219
  })
4158
- ]
5220
+ })]
4159
5221
  }),
4160
- /* @__PURE__ */ jsx("div", {
5222
+ !q && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
4161
5223
  role: "group",
4162
5224
  "aria-label": "Your groups",
4163
5225
  "data-groups": true,
@@ -4168,77 +5230,87 @@ function PresetGrid() {
4168
5230
  type: "button",
4169
5231
  "aria-pressed": on,
4170
5232
  "data-tip": `${g.description} (${g.count} ${g.count === 1 ? "theme" : "themes"})`,
4171
- onClick: () => choose(g.id),
4172
- className: `flex h-12 min-w-0 flex-col items-center justify-center gap-0.5 rounded-xl border px-2 text-xs font-semibold @min-[380px]:h-10 @min-[380px]:flex-row @min-[380px]:justify-start @min-[380px]:gap-1.5 @min-[440px]:px-2.5 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1 ${on ? "border-zinc-900 bg-zinc-900 text-white shadow-sm" : "border-zinc-200 bg-zinc-100 text-zinc-900 hover:bg-zinc-200"}`,
5233
+ onClick: () => choose(g.id, { scroll: true }),
5234
+ className: `flex min-h-12 min-w-0 flex-col items-center justify-center gap-1 rounded-xl border px-1.5 py-2 text-[11px] leading-tight font-semibold @min-[460px]:h-10 @min-[460px]:min-h-0 @min-[460px]:flex-row @min-[460px]:justify-start @min-[460px]:gap-1.5 @min-[460px]:px-2.5 @min-[460px]:py-0 @min-[460px]:text-xs cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1 ${on ? "border-zinc-900 bg-zinc-900 text-white shadow-sm" : "border-zinc-200 bg-zinc-100 text-zinc-900 hover:bg-zinc-200"}`,
4173
5235
  children: [
4174
5236
  /* @__PURE__ */ jsx(g.Icon, {
4175
5237
  className: "w-4 h-4 shrink-0",
4176
5238
  "aria-hidden": "true"
4177
5239
  }),
4178
5240
  /* @__PURE__ */ jsx("span", {
4179
- className: "max-w-full truncate",
5241
+ className: "max-w-full text-center break-words @min-[460px]:truncate @min-[460px]:text-left",
4180
5242
  children: g.label
4181
5243
  }),
4182
5244
  /* @__PURE__ */ jsx("span", {
4183
- className: `ml-auto hidden pl-1 font-medium tabular-nums @min-[440px]:inline ${on ? "text-zinc-300" : "text-zinc-500"}`,
5245
+ className: `ml-auto hidden pl-1 font-medium tabular-nums @min-[460px]:inline ${on ? "text-zinc-300" : "text-zinc-500"}`,
4184
5246
  children: g.count
4185
5247
  })
4186
5248
  ]
4187
5249
  }, g.id);
4188
5250
  })
4189
- }),
4190
- categories.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
4191
- type: "button",
4192
- "aria-expanded": !settings.libraryCollapsed,
4193
- "aria-controls": categoriesId,
4194
- onClick: () => update({ libraryCollapsed: !settings.libraryCollapsed }),
4195
- className: "flex w-full items-center gap-1.5 rounded text-[11px] font-semibold uppercase tracking-wide text-zinc-500 hover:text-zinc-900 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
4196
- children: [
4197
- /* @__PURE__ */ jsx(ChevronRight, {
4198
- className: `w-3.5 h-3.5 shrink-0 transition-transform motion-reduce:transition-none ${settings.libraryCollapsed ? "" : "rotate-90"}`,
4199
- "aria-hidden": "true"
4200
- }),
4201
- "Library",
4202
- settings.libraryCollapsed && activeCategory && /* @__PURE__ */ jsxs("span", {
4203
- className: "normal-case tracking-normal text-zinc-700",
4204
- children: ["· ", activeCategory.label]
4205
- }),
4206
- /* @__PURE__ */ jsx("span", { className: "h-px flex-1 bg-zinc-200" }),
4207
- /* @__PURE__ */ jsx("span", {
4208
- className: "normal-case tracking-normal font-medium",
4209
- children: settings.libraryCollapsed ? "Show" : "Hide"
4210
- })
4211
- ]
4212
- }), /* @__PURE__ */ jsx("div", {
4213
- id: categoriesId,
4214
- hidden: settings.libraryCollapsed,
4215
- role: "group",
4216
- "aria-label": "Library categories",
4217
- className: "grid grid-cols-2 @min-[380px]:grid-cols-3 gap-2",
4218
- children: categories.map((c) => {
4219
- const on = !q && category === c.id;
4220
- return /* @__PURE__ */ jsxs("button", {
4221
- type: "button",
4222
- "aria-pressed": on,
4223
- "data-tip": c.description,
4224
- onClick: () => choose(c.id, { scroll: true }),
4225
- className: `flex h-8 min-w-0 items-center gap-1 rounded-lg border px-2 text-xs font-medium cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1 ${on ? "border-zinc-900 bg-zinc-900 text-white" : "border-zinc-300 bg-white text-zinc-800 hover:bg-zinc-50"}`,
4226
- children: [
4227
- on && /* @__PURE__ */ jsx(Check, {
4228
- className: "w-3 h-3 shrink-0",
5251
+ }), categories.length > 0 && /* @__PURE__ */ jsxs("section", {
5252
+ "aria-label": "Theme library",
5253
+ className: "rounded-xl border border-zinc-200 bg-zinc-50 p-2.5",
5254
+ children: [/* @__PURE__ */ jsxs("button", {
5255
+ type: "button",
5256
+ "aria-expanded": !settings.libraryCollapsed,
5257
+ "aria-controls": categoriesId,
5258
+ onClick: () => update({ libraryCollapsed: !settings.libraryCollapsed }),
5259
+ className: "flex w-full items-center gap-2.5 rounded-lg px-1 py-0.5 text-left cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
5260
+ children: [
5261
+ /* @__PURE__ */ jsx(LibraryBig, {
5262
+ className: "h-5 w-5 shrink-0 text-zinc-700",
5263
+ "aria-hidden": "true"
5264
+ }),
5265
+ /* @__PURE__ */ jsxs("span", {
5266
+ className: "min-w-0 flex-1",
5267
+ children: [/* @__PURE__ */ jsx("span", {
5268
+ className: "block text-sm font-semibold text-zinc-900",
5269
+ children: "Library"
5270
+ }), /* @__PURE__ */ jsx("span", {
5271
+ className: "block text-[11px] leading-snug text-zinc-600",
5272
+ children: settings.libraryCollapsed && activeCategory ? `Showing ${activeCategory.label}` : `${library ? `${library.themes.length} palettes` : "Palettes"} sorted by mood. Pick one to see its themes.`
5273
+ })]
5274
+ }),
5275
+ /* @__PURE__ */ jsxs("span", {
5276
+ className: "flex shrink-0 items-center gap-1 text-[11px] font-medium text-zinc-600",
5277
+ children: [settings.libraryCollapsed ? "Show" : "Hide", /* @__PURE__ */ jsx(ChevronRight, {
5278
+ className: `w-3.5 h-3.5 transition-transform motion-reduce:transition-none ${settings.libraryCollapsed ? "" : "rotate-90"}`,
4229
5279
  "aria-hidden": "true"
4230
- }),
4231
- /* @__PURE__ */ jsx("span", {
4232
- className: "truncate",
4233
- children: c.label
4234
- }),
4235
- c.count != null && /* @__PURE__ */ jsx("span", {
4236
- className: `ml-auto pl-1 tabular-nums ${on ? "text-zinc-300" : "text-zinc-500"}`,
4237
- children: c.count
4238
- })
4239
- ]
4240
- }, c.id);
4241
- })
5280
+ })]
5281
+ })
5282
+ ]
5283
+ }), /* @__PURE__ */ jsx("div", {
5284
+ id: categoriesId,
5285
+ hidden: settings.libraryCollapsed,
5286
+ role: "group",
5287
+ "aria-label": "Library moods",
5288
+ className: "mt-2.5 grid grid-cols-2 @min-[380px]:grid-cols-3 gap-2",
5289
+ children: categories.map((c) => {
5290
+ const on = !q && category === c.id;
5291
+ return /* @__PURE__ */ jsxs("button", {
5292
+ type: "button",
5293
+ "aria-pressed": on,
5294
+ "data-tip": c.description,
5295
+ onClick: () => choose(c.id, { scroll: true }),
5296
+ className: `flex h-8 min-w-0 items-center gap-1 rounded-lg border px-2 text-xs font-medium cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 focus-visible:ring-offset-1 ${on ? "border-zinc-900 bg-zinc-900 text-white" : "border-zinc-300 bg-white text-zinc-800 hover:bg-zinc-50"}`,
5297
+ children: [
5298
+ on && /* @__PURE__ */ jsx(Check, {
5299
+ className: "w-3 h-3 shrink-0",
5300
+ "aria-hidden": "true"
5301
+ }),
5302
+ /* @__PURE__ */ jsx("span", {
5303
+ className: "truncate",
5304
+ children: c.label
5305
+ }),
5306
+ c.count != null && /* @__PURE__ */ jsx("span", {
5307
+ className: `ml-auto pl-1 tabular-nums ${on ? "text-zinc-300" : "text-zinc-500"}`,
5308
+ children: c.count
5309
+ })
5310
+ ]
5311
+ }, c.id);
5312
+ })
5313
+ })]
4242
5314
  })] }),
4243
5315
  /* @__PURE__ */ jsx("div", {
4244
5316
  ref: themesRef,
@@ -4264,7 +5336,7 @@ function PresetGrid() {
4264
5336
  "”."
4265
5337
  ]
4266
5338
  }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4267
- /* @__PURE__ */ jsxs("p", {
5339
+ !q && /* @__PURE__ */ jsxs("p", {
4268
5340
  className: "sr-only",
4269
5341
  role: "status",
4270
5342
  children: [visible.length, " themes"]
@@ -4383,58 +5455,73 @@ function ScanCard() {
4383
5455
  });
4384
5456
  }
4385
5457
  function ThemeCard({ theme: t, isActive, onSelect }) {
4386
- const { issues } = useTheme();
5458
+ const { issues, coloursOf, setThemeColours, features } = useTheme();
5459
+ const colours = coloursOf(t.id);
4387
5460
  const showContrast = useContext(ContrastNav);
4388
5461
  const stored = useMemo(() => t.library && !t.derived ? 0 : checkTheme(t.tokens).length, [t]);
4389
5462
  const count = isActive ? issues.length : stored;
4390
5463
  return /* @__PURE__ */ jsxs("div", {
4391
5464
  className: "relative",
4392
- children: [/* @__PURE__ */ jsxs("button", {
4393
- type: "button",
4394
- "aria-pressed": isActive,
4395
- onClick: onSelect,
4396
- className: `flex w-full flex-col gap-2 rounded-xl border p-2.5 text-left cursor-pointer hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${isActive ? "border-zinc-900 ring-1 ring-zinc-900" : "border-zinc-200"} ${count ? "pr-12" : ""}`,
4397
- children: [
4398
- /* @__PURE__ */ jsx("span", {
4399
- className: "truncate text-sm font-medium",
4400
- children: t.name
4401
- }),
4402
- /* @__PURE__ */ jsx(Swatches, { tokens: t.tokens }),
4403
- (isActive || t.custom || t.scanned) && /* @__PURE__ */ jsxs("span", {
4404
- className: "flex items-center gap-1.5 text-[11px]",
4405
- children: [
4406
- isActive && /* @__PURE__ */ jsxs("span", {
4407
- className: "flex items-center gap-0.5 font-semibold text-zinc-900",
4408
- children: [/* @__PURE__ */ jsx(Check, {
4409
- className: "w-3.5 h-3.5",
4410
- "aria-hidden": "true"
4411
- }), " Active"]
4412
- }),
4413
- t.custom && /* @__PURE__ */ jsx("span", {
4414
- className: "rounded bg-zinc-100 px-1 font-medium text-zinc-700",
4415
- children: "Custom"
4416
- }),
4417
- t.scanned && /* @__PURE__ */ jsx("span", {
4418
- className: "rounded bg-sky-50 px-1 font-medium text-sky-900",
4419
- children: t.match ? "Library match" : "From scan"
4420
- })
4421
- ]
5465
+ children: [
5466
+ /* @__PURE__ */ jsxs("button", {
5467
+ type: "button",
5468
+ "aria-pressed": isActive,
5469
+ onClick: onSelect,
5470
+ className: `flex w-full flex-col gap-2 rounded-xl border p-2.5 text-left cursor-pointer hover:bg-zinc-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900 ${isActive ? "border-zinc-900 ring-1 ring-zinc-900" : "border-zinc-200"} ${count ? "pr-12" : ""}`,
5471
+ children: [
5472
+ /* @__PURE__ */ jsx("span", {
5473
+ className: "truncate text-sm font-medium",
5474
+ children: t.name
5475
+ }),
5476
+ /* @__PURE__ */ jsx(Swatches, {
5477
+ tokens: t.tokens,
5478
+ count: colours
5479
+ }),
5480
+ (isActive || t.custom || t.scanned) && /* @__PURE__ */ jsxs("span", {
5481
+ className: "flex items-center gap-1.5 text-[11px]",
5482
+ children: [
5483
+ isActive && /* @__PURE__ */ jsxs("span", {
5484
+ className: "flex items-center gap-0.5 font-semibold text-zinc-900",
5485
+ children: [/* @__PURE__ */ jsx(Check, {
5486
+ className: "w-3.5 h-3.5",
5487
+ "aria-hidden": "true"
5488
+ }), " Active"]
5489
+ }),
5490
+ t.custom && /* @__PURE__ */ jsx("span", {
5491
+ className: "rounded bg-zinc-100 px-1 font-medium text-zinc-700",
5492
+ children: "Custom"
5493
+ }),
5494
+ t.scanned && /* @__PURE__ */ jsx("span", {
5495
+ className: "rounded bg-sky-50 px-1 font-medium text-sky-900",
5496
+ children: t.match ? "Library match" : "From scan"
5497
+ })
5498
+ ]
5499
+ })
5500
+ ]
5501
+ }),
5502
+ count > 0 && /* @__PURE__ */ jsxs("button", {
5503
+ type: "button",
5504
+ onClick: (e) => {
5505
+ if (!isActive) onSelect();
5506
+ showContrast(e.currentTarget);
5507
+ },
5508
+ "aria-label": `${t.name} has ${count} contrast ${count === 1 ? "problem" : "problems"}. Review and fix`,
5509
+ "data-tip": "Contrast problems: review and fix",
5510
+ className: "absolute right-1.5 top-1.5 inline-flex items-center gap-0.5 rounded-full border border-amber-300 bg-amber-100 px-1.5 py-0.5 text-[11px] font-semibold text-amber-900 hover:bg-amber-200 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
5511
+ children: [/* @__PURE__ */ jsx(AlertTriangle, {
5512
+ className: "w-3 h-3",
5513
+ "aria-hidden": "true"
5514
+ }), count]
5515
+ }),
5516
+ isActive && features.colourCount && /* @__PURE__ */ jsx("span", {
5517
+ className: "absolute right-2 bottom-2",
5518
+ children: /* @__PURE__ */ jsx(ColourStepper, {
5519
+ value: colours,
5520
+ onChange: (n) => setThemeColours(t.id, n),
5521
+ label: `Colours ${t.name} uses`
4422
5522
  })
4423
- ]
4424
- }), count > 0 && /* @__PURE__ */ jsxs("button", {
4425
- type: "button",
4426
- onClick: (e) => {
4427
- if (!isActive) onSelect();
4428
- showContrast(e.currentTarget);
4429
- },
4430
- "aria-label": `${t.name} has ${count} contrast ${count === 1 ? "problem" : "problems"}. Review and fix`,
4431
- "data-tip": "Contrast problems: review and fix",
4432
- className: "absolute right-1.5 top-1.5 inline-flex items-center gap-0.5 rounded-full border border-amber-300 bg-amber-100 px-1.5 py-0.5 text-[11px] font-semibold text-amber-900 hover:bg-amber-200 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-900",
4433
- children: [/* @__PURE__ */ jsx(AlertTriangle, {
4434
- className: "w-3 h-3",
4435
- "aria-hidden": "true"
4436
- }), count]
4437
- })]
5523
+ })
5524
+ ]
4438
5525
  });
4439
5526
  }
4440
5527
  function CustomPalettes() {
@@ -5681,7 +6768,7 @@ function AuditBar({ anchorRect, open, hiddenCount, themeIssues, onRecheck, onClo
5681
6768
  }
5682
6769
  //#endregion
5683
6770
  //#region src/panel-css.generated.js
5684
- var panel_css_generated_default = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (display:block){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-900:oklch(39.1% .09 240.876);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-white:#fff;--spacing:4px;--text-xs:12px;--text-xs--line-height:calc(1 / .75);--text-sm:14px;--text-sm--line-height:calc(1.25 / .875);--text-base:16px;--text-base--line-height:calc(1.5 / 1);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-normal:0em;--tracking-wide:.025em;--leading-snug:1.375;--radius-md:6px;--radius-lg:8px;--radius-xl:12px;--radius-2xl:16px;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.theme-switcher{letter-spacing:normal;color:var(--color-zinc-900);font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:16px;line-height:1.5}}@layer components;@layer utilities{.\\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-5{inset-block:calc(var(--spacing) * 5)}.-top-2{top:calc(var(--spacing) * -2)}.-top-\\[6px\\]{top:-6px}.top-0{top:0}.top-1\\.5{top:calc(var(--spacing) * 1.5)}.top-1\\/2{top:50%}.top-5{top:calc(var(--spacing) * 5)}.top-\\[84px\\]{top:84px}.-right-0\\.5{right:calc(var(--spacing) * -.5)}.-right-2{right:calc(var(--spacing) * -2)}.right-1\\.5{right:calc(var(--spacing) * 1.5)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-full{right:100%}.-bottom-0\\.5{bottom:calc(var(--spacing) * -.5)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-\\[6px\\]{bottom:-6px}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.-left-2{left:calc(var(--spacing) * -2)}.left-5{left:calc(var(--spacing) * 5)}.left-full{left:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-\\[55\\]{z-index:55}.z-\\[56\\]{z-index:56}.z-\\[57\\]{z-index:57}.z-\\[60\\]{z-index:60}.z-\\[61\\]{z-index:61}.z-\\[62\\]{z-index:62}.z-\\[70\\]{z-index:70}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-px{margin-top:1px}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2\\.5{margin-bottom:calc(var(--spacing) * 2.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-2\\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-12{height:calc(var(--spacing) * 12)}.h-16{height:calc(var(--spacing) * 16)}.h-px{height:1px}.max-h-40{max-height:calc(var(--spacing) * 40)}.min-h-0{min-height:0}.w-2\\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-9{width:calc(var(--spacing) * 9)}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-\\[5\\.5rem\\]{width:88px}.w-full{width:100%}.max-w-\\[240px\\]{max-width:240px}.max-w-\\[calc\\(100vw-80px\\)\\]{max-width:calc(100vw - 80px)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.animate-spin{animation:var(--animate-spin)}.cursor-ew-resize{cursor:ew-resize}.cursor-grabbing{cursor:grabbing}.cursor-nesw-resize{cursor:nesw-resize}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.scroll-mt-3{scroll-margin-top:calc(var(--spacing) * 3)}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-0\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[inherit\\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-900{border-color:var(--color-zinc-900)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-white{background-color:var(--color-white)}.bg-white\\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-200{background-color:var(--color-zinc-200)}.bg-zinc-900{background-color:var(--color-zinc-900)}.object-contain{object-fit:contain}.p-0\\.5{padding:calc(var(--spacing) * .5)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.px-1{padding-inline:var(--spacing)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pr-1\\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.align-super{vertical-align:super}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-normal{--tw-tracking:var(--tracking-normal);letter-spacing:var(--tracking-normal)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-red-700{color:var(--color-red-700)}.text-sky-900{color:var(--color-sky-900)}.text-white{color:var(--color-white)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-zinc-900{accent-color:var(--color-zinc-900)}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-white{--tw-ring-color:var(--color-white)}.ring-zinc-900{--tw-ring-color:var(--color-zinc-900)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\\[color\\,background-color\\,box-shadow\\,scale\\]{transition-property:color,background-color,box-shadow,scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}.placeholder\\:text-zinc-500::placeholder{color:var(--color-zinc-500)}@media (hover:hover){.hover\\:bg-amber-200:hover{background-color:var(--color-amber-200)}.hover\\:bg-white:hover{background-color:var(--color-white)}.hover\\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\\:bg-zinc-400\\/30:hover{background-color:#9f9fa94d}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-zinc-400\\/30:hover{background-color:color-mix(in oklab, var(--color-zinc-400) 30%, transparent)}}.hover\\:bg-zinc-700:hover{background-color:var(--color-zinc-700)}.hover\\:text-amber-950:hover{color:var(--color-amber-950)}.hover\\:text-zinc-900:hover{color:var(--color-zinc-900)}.hover\\:opacity-100:hover{opacity:1}}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-white:focus-visible{--tw-ring-color:var(--color-white)}.focus-visible\\:ring-zinc-900:focus-visible{--tw-ring-color:var(--color-zinc-900)}.focus-visible\\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-inset:focus-visible{--tw-ring-inset:inset}.disabled\\:opacity-50:disabled{opacity:.5}@media (prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}.motion-reduce\\:transition-none{transition-property:none}}@media (min-width:1200px){.min-\\[1200px\\]\\:top-\\[27px\\]{top:27px}.min-\\[1200px\\]\\:right-4{right:calc(var(--spacing) * 4)}}@media (min-width:640px){.sm\\:inline{display:inline}}@container (min-width:380px){.\\@min-\\[380px\\]\\:h-10{height:calc(var(--spacing) * 10)}.\\@min-\\[380px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\\@min-\\[380px\\]\\:flex-row{flex-direction:row}.\\@min-\\[380px\\]\\:justify-start{justify-content:flex-start}.\\@min-\\[380px\\]\\:gap-1\\.5{gap:calc(var(--spacing) * 1.5)}}@container (min-width:440px){.\\@min-\\[440px\\]\\:inline{display:inline}.\\@min-\\[440px\\]\\:px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}}@container (min-width:500px){.\\@min-\\[500px\\]\\:inline{display:inline}.\\@min-\\[500px\\]\\:px-2{padding-inline:calc(var(--spacing) * 2)}}@container (min-width:520px){.\\@min-\\[520px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}}:host{all:initial}.cbm-dark{color-scheme:dark;--color-white:oklch(21% .006 285.885);--color-zinc-50:oklch(24.5% .006 286);--color-zinc-100:oklch(27.4% .006 286.033);--color-zinc-200:oklch(33% .01 285.9);--color-zinc-300:oklch(42% .015 285.8);--color-zinc-400:oklch(52% .016 285.9);--color-zinc-500:oklch(68% .015 286.067);--color-zinc-600:oklch(74% .012 286);--color-zinc-700:oklch(82% .008 286.2);--color-zinc-800:oklch(88% .006 286.3);--color-zinc-900:oklch(96.7% .001 286.375);--color-zinc-950:oklch(98.5% 0 0);--color-amber-50:oklch(27.9% .077 45.635);--color-amber-100:oklch(33% .09 46);--color-amber-200:oklch(41.4% .112 45.904);--color-amber-300:oklch(55.5% .163 48.998);--color-amber-700:oklch(87.9% .169 91.605);--color-amber-800:oklch(92.4% .12 95.746);--color-amber-900:oklch(96.2% .059 95.617);--color-amber-950:oklch(98.7% .022 95.277);--color-emerald-50:oklch(26.2% .051 172.552);--color-emerald-800:oklch(90.5% .093 164.15);--color-emerald-900:oklch(95% .052 163.051);--color-sky-50:oklch(29.3% .066 243.157);--color-sky-100:oklch(39.1% .09 240.876);--color-sky-900:oklch(95.1% .026 236.824);--color-red-600:oklch(70.4% .191 22.216);--color-red-700:oklch(80.8% .114 19.571)}.cbm-dark *{--tw-ring-offset-color:oklch(21% .006 285.885)}.theme-scroll{--cbm-thumb:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 55%, var(--color-white))}}.theme-scroll{--cbm-thumb-hover:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb-hover:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 80%, var(--color-white))}}.theme-scroll{scrollbar-width:thin;scrollbar-color:var(--cbm-thumb) transparent}.theme-scroll:hover{scrollbar-color:var(--cbm-thumb-hover) transparent}.theme-scroll::-webkit-scrollbar{width:6px}.theme-scroll::-webkit-scrollbar-track{background:0 0}.theme-scroll::-webkit-scrollbar-thumb{background:var(--cbm-thumb);border-radius:9999px}.theme-scroll:hover::-webkit-scrollbar-thumb{background:var(--cbm-thumb-hover)}.theme-panel{transform-origin:100% 0;animation:.16s ease-out theme-panel-in}@keyframes theme-panel-in{0%{opacity:0;transform:translateY(-6px)scale(.98)}}.theme-hint{animation:.12s ease-out theme-hint-in}.theme-toast{animation:.18s ease-out theme-toast-in}@keyframes theme-toast-in{0%{opacity:0;transform:translateY(8px)}}@keyframes theme-hint-in{0%{opacity:0}}.theme-arrive{animation:.62s cubic-bezier(.34,1.56,.64,1) both theme-arrive}@keyframes theme-arrive{0%{opacity:0;transform:scale(.2)rotate(-45deg)}60%{opacity:1;transform:scale(1.16)rotate(8deg)}to{transform:scale(1)rotate(0)}}.theme-burst-piece{line-height:0;animation:.95s cubic-bezier(.15,.75,.3,1) both theme-burst-piece;display:block;position:absolute;top:50%;left:50%}@keyframes theme-burst-piece{0%{opacity:0;transform:translate(-50%, -50%) rotate(var(--r)) scale(.3)}12%{opacity:1}65%{opacity:1}to{opacity:0;transform:translate(calc(-50% + var(--x)), calc(-50% + var(--y))) rotate(calc(var(--r) + 150deg)) scale(1)}}.theme-piece-dot{background:currentColor;border-radius:9999px;width:6px;height:6px}.theme-piece-dash{background:currentColor;border-radius:9999px;width:10px;height:3px}@media (prefers-reduced-motion:reduce){.theme-panel,.theme-hint,.theme-toast{animation:none}.theme-arrive{animation:.3s ease-out both theme-hint-in}.theme-burst{display:none}}.theme-panel summary{list-style:none}.theme-panel summary::-webkit-details-marker{display:none}.theme-panel details[open]>summary .theme-chevron{transform:rotate(90deg)}@keyframes spin{to{transform:rotate(360deg)}}";
6771
+ var panel_css_generated_default = "/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */\n@layer properties{@supports (display:block){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-amber-50:oklch(98.7% .022 95.277);--color-amber-100:oklch(96.2% .059 95.617);--color-amber-200:oklch(92.4% .12 95.746);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-amber-700:oklch(55.5% .163 48.998);--color-amber-800:oklch(47.3% .137 46.201);--color-amber-900:oklch(41.4% .112 45.904);--color-amber-950:oklch(27.9% .077 45.635);--color-emerald-50:oklch(97.9% .021 166.113);--color-emerald-800:oklch(43.2% .095 166.913);--color-emerald-900:oklch(37.8% .077 168.94);--color-sky-50:oklch(97.7% .013 236.62);--color-sky-100:oklch(95.1% .026 236.824);--color-sky-900:oklch(39.1% .09 240.876);--color-zinc-50:oklch(98.5% 0 none);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-white:#fff;--spacing:4px;--text-xs:12px;--text-xs--line-height:calc(1 / .75);--text-sm:14px;--text-sm--line-height:calc(1.25 / .875);--text-base:16px;--text-base--line-height:calc(1.5 / 1);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wide:.025em;--leading-tight:1.25;--leading-snug:1.375;--radius-md:6px;--radius-lg:8px;--radius-xl:12px;--radius-2xl:16px;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", \"Noto Sans\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}.theme-switcher{letter-spacing:normal;color:var(--color-zinc-900);font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:16px;line-height:1.5}}@layer components;@layer utilities{.\\@container{container-type:inline-size}.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-x-3{inset-inline:calc(var(--spacing) * 3)}.inset-x-5{inset-inline:calc(var(--spacing) * 5)}.inset-y-5{inset-block:calc(var(--spacing) * 5)}.-top-2{top:calc(var(--spacing) * -2)}.-top-\\[6px\\]{top:-6px}.top-0{top:0}.top-1\\.5{top:calc(var(--spacing) * 1.5)}.top-1\\/2{top:50%}.top-5{top:calc(var(--spacing) * 5)}.top-\\[84px\\]{top:84px}.-right-0\\.5{right:calc(var(--spacing) * -.5)}.-right-2{right:calc(var(--spacing) * -2)}.right-1\\.5{right:calc(var(--spacing) * 1.5)}.right-2{right:calc(var(--spacing) * 2)}.right-5{right:calc(var(--spacing) * 5)}.right-6{right:calc(var(--spacing) * 6)}.right-full{right:100%}.-bottom-0\\.5{bottom:calc(var(--spacing) * -.5)}.-bottom-2{bottom:calc(var(--spacing) * -2)}.-bottom-\\[6px\\]{bottom:-6px}.bottom-2{bottom:calc(var(--spacing) * 2)}.bottom-3{bottom:calc(var(--spacing) * 3)}.bottom-5{bottom:calc(var(--spacing) * 5)}.-left-2{left:calc(var(--spacing) * -2)}.left-5{left:calc(var(--spacing) * 5)}.left-full{left:100%}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-\\[55\\]{z-index:55}.z-\\[56\\]{z-index:56}.z-\\[57\\]{z-index:57}.z-\\[60\\]{z-index:60}.z-\\[61\\]{z-index:61}.z-\\[62\\]{z-index:62}.z-\\[70\\]{z-index:70}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.-mt-1{margin-top:calc(var(--spacing) * -1)}.mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-2\\.5{margin-top:calc(var(--spacing) * 2.5)}.mt-px{margin-top:1px}.-mr-1{margin-right:calc(var(--spacing) * -1)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-1{margin-bottom:var(--spacing)}.mb-2\\.5{margin-bottom:calc(var(--spacing) * 2.5)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-2\\.5{height:calc(var(--spacing) * 2.5)}.h-3{height:calc(var(--spacing) * 3)}.h-3\\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-16{height:calc(var(--spacing) * 16)}.max-h-40{max-height:calc(var(--spacing) * 40)}.min-h-0{min-height:0}.min-h-12{min-height:calc(var(--spacing) * 12)}.w-2\\.5{width:calc(var(--spacing) * 2.5)}.w-3{width:calc(var(--spacing) * 3)}.w-3\\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-16{width:calc(var(--spacing) * 16)}.w-24{width:calc(var(--spacing) * 24)}.w-\\[5\\.5rem\\]{width:88px}.w-auto{width:auto}.w-full{width:100%}.max-w-\\[240px\\]{max-width:240px}.max-w-\\[calc\\(100vw-80px\\)\\]{max-width:calc(100vw - 80px)}.max-w-full{max-width:100%}.min-w-0{min-width:0}.min-w-\\[1\\.25rem\\]{min-width:20px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.scale-110{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x) var(--tw-scale-y)}.rotate-45{rotate:45deg}.rotate-90{rotate:90deg}.animate-spin{animation:var(--animate-spin)}.cursor-ew-resize{cursor:ew-resize}.cursor-grabbing{cursor:grabbing}.cursor-nesw-resize{cursor:nesw-resize}.cursor-ns-resize{cursor:ns-resize}.cursor-nwse-resize{cursor:nwse-resize}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.resize{resize:both}.scroll-mt-3{scroll-margin-top:calc(var(--spacing) * 3)}.list-decimal{list-style-type:decimal}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-0\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * .5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * .5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-1\\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-y-3\\.5{row-gap:calc(var(--spacing) * 3.5)}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.overscroll-contain{overscroll-behavior:contain}.rounded{border-radius:4px}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-\\[inherit\\]{border-radius:inherit}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-amber-300{border-color:var(--color-amber-300)}.border-amber-400{border-color:var(--color-amber-400)}.border-red-600{border-color:var(--color-red-600)}.border-transparent{border-color:#0000}.border-white{border-color:var(--color-white)}.border-zinc-200{border-color:var(--color-zinc-200)}.border-zinc-300{border-color:var(--color-zinc-300)}.border-zinc-900{border-color:var(--color-zinc-900)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-amber-100{background-color:var(--color-amber-100)}.bg-amber-400{background-color:var(--color-amber-400)}.bg-emerald-50{background-color:var(--color-emerald-50)}.bg-sky-50{background-color:var(--color-sky-50)}.bg-sky-100{background-color:var(--color-sky-100)}.bg-white{background-color:var(--color-white)}.bg-white\\/90{background-color:#ffffffe6}@supports (color:color-mix(in lab, red, red)){.bg-white\\/90{background-color:color-mix(in oklab, var(--color-white) 90%, transparent)}}.bg-zinc-50{background-color:var(--color-zinc-50)}.bg-zinc-100{background-color:var(--color-zinc-100)}.bg-zinc-900{background-color:var(--color-zinc-900)}.object-contain{object-fit:contain}.p-0\\.5{padding:calc(var(--spacing) * .5)}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.px-0\\.5{padding-inline:calc(var(--spacing) * .5)}.px-1{padding-inline:var(--spacing)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.pr-1\\.5{padding-right:calc(var(--spacing) * 1.5)}.pr-12{padding-right:calc(var(--spacing) * 12)}.pb-4{padding-bottom:calc(var(--spacing) * 4)}.pl-1{padding-left:var(--spacing)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.align-super{vertical-align:super}.font-mono{font-family:var(--font-mono)}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.leading-snug{--tw-leading:var(--leading-snug);line-height:var(--leading-snug)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-700{color:var(--color-amber-700)}.text-amber-800{color:var(--color-amber-800)}.text-amber-900{color:var(--color-amber-900)}.text-amber-950{color:var(--color-amber-950)}.text-emerald-800{color:var(--color-emerald-800)}.text-emerald-900{color:var(--color-emerald-900)}.text-red-700{color:var(--color-red-700)}.text-sky-900{color:var(--color-sky-900)}.text-white{color:var(--color-white)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-700{color:var(--color-zinc-700)}.text-zinc-800{color:var(--color-zinc-800)}.text-zinc-900{color:var(--color-zinc-900)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.underline-offset-2{text-underline-offset:2px}.accent-zinc-900{accent-color:var(--color-zinc-900)}.opacity-25{opacity:.25}.opacity-70{opacity:.7}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a), 0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-white{--tw-ring-color:var(--color-white)}.ring-zinc-900{--tw-ring-color:var(--color-zinc-900)}.ring-offset-1{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.invert{--tw-invert:invert(100%);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-\\[color\\,background-color\\,box-shadow\\,scale\\]{transition-property:color,background-color,box-shadow,scale;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-shadow{transition-property:box-shadow;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-700{--tw-duration:.7s;transition-duration:.7s}.ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.select-none{-webkit-user-select:none;user-select:none}.placeholder\\:text-zinc-500::placeholder{color:var(--color-zinc-500)}@media (hover:hover){.hover\\:border-zinc-900:hover{border-color:var(--color-zinc-900)}.hover\\:bg-amber-200:hover{background-color:var(--color-amber-200)}.hover\\:bg-white:hover{background-color:var(--color-white)}.hover\\:bg-zinc-50:hover{background-color:var(--color-zinc-50)}.hover\\:bg-zinc-100:hover{background-color:var(--color-zinc-100)}.hover\\:bg-zinc-200:hover{background-color:var(--color-zinc-200)}.hover\\:bg-zinc-400\\/30:hover{background-color:#9f9fa94d}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-zinc-400\\/30:hover{background-color:color-mix(in oklab, var(--color-zinc-400) 30%, transparent)}}.hover\\:bg-zinc-700:hover{background-color:var(--color-zinc-700)}.hover\\:text-amber-950:hover{color:var(--color-amber-950)}.hover\\:text-zinc-900:hover{color:var(--color-zinc-900)}.hover\\:opacity-100:hover{opacity:1}}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\\:ring-white:focus-visible{--tw-ring-color:var(--color-white)}.focus-visible\\:ring-zinc-900:focus-visible{--tw-ring-color:var(--color-zinc-900)}.focus-visible\\:ring-offset-1:focus-visible{--tw-ring-offset-width:1px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:ring-offset-2:focus-visible{--tw-ring-offset-width:2px;--tw-ring-offset-shadow:var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)}.focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.focus-visible\\:ring-inset:focus-visible{--tw-ring-inset:inset}.disabled\\:cursor-default:disabled{cursor:default}.disabled\\:opacity-35:disabled{opacity:.35}.disabled\\:opacity-50:disabled{opacity:.5}@media (hover:hover){.disabled\\:hover\\:border-zinc-300:disabled:hover{border-color:var(--color-zinc-300)}}@media (prefers-reduced-motion:reduce){.motion-reduce\\:animate-none{animation:none}.motion-reduce\\:transition-none{transition-property:none}}@media (min-width:1200px){.min-\\[1200px\\]\\:top-\\[27px\\]{top:27px}.min-\\[1200px\\]\\:right-4{right:calc(var(--spacing) * 4)}}@media (min-width:640px){.sm\\:inline{display:inline}}@container (min-width:380px){.\\@min-\\[380px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@container (min-width:400px){.\\@min-\\[400px\\]\\:inline{display:inline}}@container (min-width:460px){.\\@min-\\[460px\\]\\:inline{display:inline}.\\@min-\\[460px\\]\\:h-10{height:calc(var(--spacing) * 10)}.\\@min-\\[460px\\]\\:min-h-0{min-height:0}.\\@min-\\[460px\\]\\:flex-row{flex-direction:row}.\\@min-\\[460px\\]\\:justify-start{justify-content:flex-start}.\\@min-\\[460px\\]\\:gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.\\@min-\\[460px\\]\\:truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.\\@min-\\[460px\\]\\:px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.\\@min-\\[460px\\]\\:py-0{padding-block:0}.\\@min-\\[460px\\]\\:text-left{text-align:left}.\\@min-\\[460px\\]\\:text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}}@container (min-width:500px){.\\@min-\\[500px\\]\\:inline{display:inline}.\\@min-\\[500px\\]\\:px-2{padding-inline:calc(var(--spacing) * 2)}}@container (min-width:520px){.\\@min-\\[520px\\]\\:ml-auto{margin-left:auto}.\\@min-\\[520px\\]\\:w-auto{width:auto}.\\@min-\\[520px\\]\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.\\@min-\\[520px\\]\\:justify-start{justify-content:flex-start}}}:host{all:initial}.cbm-dark{color-scheme:dark;--color-white:oklch(21% .006 285.885);--color-zinc-50:oklch(24.5% .006 286);--color-zinc-100:oklch(27.4% .006 286.033);--color-zinc-200:oklch(33% .01 285.9);--color-zinc-300:oklch(42% .015 285.8);--color-zinc-400:oklch(52% .016 285.9);--color-zinc-500:oklch(68% .015 286.067);--color-zinc-600:oklch(74% .012 286);--color-zinc-700:oklch(82% .008 286.2);--color-zinc-800:oklch(88% .006 286.3);--color-zinc-900:oklch(96.7% .001 286.375);--color-zinc-950:oklch(98.5% 0 0);--color-amber-50:oklch(27.9% .077 45.635);--color-amber-100:oklch(33% .09 46);--color-amber-200:oklch(41.4% .112 45.904);--color-amber-300:oklch(55.5% .163 48.998);--color-amber-700:oklch(87.9% .169 91.605);--color-amber-800:oklch(92.4% .12 95.746);--color-amber-900:oklch(96.2% .059 95.617);--color-amber-950:oklch(98.7% .022 95.277);--color-emerald-50:oklch(26.2% .051 172.552);--color-emerald-800:oklch(90.5% .093 164.15);--color-emerald-900:oklch(95% .052 163.051);--color-sky-50:oklch(29.3% .066 243.157);--color-sky-100:oklch(39.1% .09 240.876);--color-sky-900:oklch(95.1% .026 236.824);--color-red-600:oklch(70.4% .191 22.216);--color-red-700:oklch(80.8% .114 19.571)}.cbm-dark *{--tw-ring-offset-color:oklch(21% .006 285.885)}.theme-scroll{--cbm-thumb:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 55%, var(--color-white))}}.theme-scroll{--cbm-thumb-hover:var(--color-primary,oklch(55.2% .016 285.938))}@supports (color:color-mix(in lab, red, red)){.theme-scroll{--cbm-thumb-hover:color-mix(in srgb, var(--color-primary,var(--color-zinc-500)) 80%, var(--color-white))}}.theme-scroll{scrollbar-width:thin;scrollbar-color:var(--cbm-thumb) transparent}.theme-scroll:hover{scrollbar-color:var(--cbm-thumb-hover) transparent}.theme-scroll::-webkit-scrollbar{width:6px}.theme-scroll::-webkit-scrollbar-track{background:0 0}.theme-scroll::-webkit-scrollbar-thumb{background:var(--cbm-thumb);border-radius:9999px}.theme-scroll:hover::-webkit-scrollbar-thumb{background:var(--cbm-thumb-hover)}.theme-panel{transform-origin:100% 0;animation:.16s ease-out theme-panel-in}@keyframes theme-panel-in{0%{opacity:0;transform:translateY(-6px)scale(.98)}}.theme-hint{animation:.12s ease-out theme-hint-in}.theme-toast{animation:.18s ease-out theme-toast-in}@keyframes theme-toast-in{0%{opacity:0;transform:translateY(8px)}}@keyframes theme-hint-in{0%{opacity:0}}.theme-arrive{animation:.62s cubic-bezier(.34,1.56,.64,1) both theme-arrive}@keyframes theme-arrive{0%{opacity:0;transform:scale(.2)rotate(-45deg)}60%{opacity:1;transform:scale(1.16)rotate(8deg)}to{transform:scale(1)rotate(0)}}.theme-burst-piece{line-height:0;animation:.95s cubic-bezier(.15,.75,.3,1) both theme-burst-piece;display:block;position:absolute;top:50%;left:50%}@keyframes theme-burst-piece{0%{opacity:0;transform:translate(-50%, -50%) rotate(var(--r)) scale(.3)}12%{opacity:1}65%{opacity:1}to{opacity:0;transform:translate(calc(-50% + var(--x)), calc(-50% + var(--y))) rotate(calc(var(--r) + 150deg)) scale(1)}}.theme-piece-dot{background:currentColor;border-radius:9999px;width:6px;height:6px}.theme-piece-dash{background:currentColor;border-radius:9999px;width:10px;height:3px}@media (prefers-reduced-motion:reduce){.theme-panel,.theme-hint,.theme-toast{animation:none}.theme-arrive{animation:.3s ease-out both theme-hint-in}.theme-burst{display:none}}.theme-panel summary{list-style:none}.theme-panel summary::-webkit-details-marker{display:none}.theme-panel details[open]>summary .theme-chevron{transform:rotate(90deg)}@keyframes spin{to{transform:rotate(360deg)}}";
5685
6772
  //#endregion
5686
6773
  //#region src/ThemeSwitcher.jsx
5687
6774
  var CORNERS = {
@@ -5701,7 +6788,6 @@ var DRAG_THRESHOLD = 5;
5701
6788
  var HINT_DELAY = 100;
5702
6789
  var DOT_INTERVAL = 1500;
5703
6790
  var INTRO_DELAY = 900;
5704
- var BURST_TIME = 1100;
5705
6791
  var FOCUSABLE = "a[href], button:not([disabled]), input:not([disabled]), textarea:not([disabled]), select:not([disabled]), summary, [tabindex]:not([tabindex=\"-1\"])";
5706
6792
  /** Tag of the element that hosts the switcher's shadow root. The site scan skips it. */
5707
6793
  var HOST_TAG = "colorsbymax-root";
@@ -5730,7 +6816,7 @@ function ThemeSwitcher() {
5730
6816
  return mount && createPortal(/* @__PURE__ */ jsx(Switcher, {}), mount);
5731
6817
  }
5732
6818
  function Switcher() {
5733
- const { issues, storageKey, tokens, position: requested, setLogoColouring, intro, mode, modeSetting, setMode, settingDefaults } = useTheme();
6819
+ const { issues, storageKey, tokens, position: requested, setLogoColouring, setColourStyle, setPaletteDefault, features, intro, mode, modeSetting, setMode, settingDefaults } = useTheme();
5734
6820
  const position = CORNERS[requested] ? requested : "bottom-right";
5735
6821
  const [open, setOpen] = useState(false);
5736
6822
  const [arrived, setArrived] = useState(!intro);
@@ -5758,6 +6844,8 @@ function Switcher() {
5758
6844
  }, [burst]);
5759
6845
  const [settings, setSettings] = useState(() => loadSettings(storageKey, settingDefaults));
5760
6846
  useEffect(() => setLogoColouring(settings.colourLogo), [settings.colourLogo, setLogoColouring]);
6847
+ useEffect(() => setColourStyle(settings.colourStyle), [settings.colourStyle, setColourStyle]);
6848
+ useEffect(() => setPaletteDefault(settings.paletteSize), [settings.paletteSize, setPaletteDefault]);
5761
6849
  const [auditOn, setAuditOn] = useState(false);
5762
6850
  const [findings, setFindings] = useState([]);
5763
6851
  const recheck = useCallback(() => {
@@ -5887,6 +6975,10 @@ function Switcher() {
5887
6975
  setOpen(false);
5888
6976
  buttonRef.current?.focus();
5889
6977
  }, []);
6978
+ const [kept, setKept] = useState(false);
6979
+ useEffect(() => {
6980
+ if (open) setKept(true);
6981
+ }, [open]);
5890
6982
  const onDragStart = (e) => {
5891
6983
  justDragged.current = false;
5892
6984
  hideHint();
@@ -5939,7 +7031,13 @@ function Switcher() {
5939
7031
  const settingsApi = {
5940
7032
  settings: {
5941
7033
  ...settings,
5942
- mode: modeSetting
7034
+ mode: modeSetting,
7035
+ showPicks: settings.showPicks && features.picks,
7036
+ showLibrary: settings.showLibrary && features.library,
7037
+ showCustom: settings.showCustom && features.custom,
7038
+ showOverrides: settings.showOverrides && features.overrides,
7039
+ showImportExport: settings.showImportExport && features.importExport,
7040
+ colourStyle: features.colourStyle ? settings.colourStyle : "colourful"
5943
7041
  },
5944
7042
  mode,
5945
7043
  audit: {
@@ -5974,7 +7072,7 @@ function Switcher() {
5974
7072
  const root = panel.getRootNode();
5975
7073
  const focused = () => root.activeElement ?? document.activeElement;
5976
7074
  const inside = (e, el) => e.composedPath().includes(el);
5977
- panel.querySelector(FOCUSABLE)?.focus();
7075
+ panel.querySelector(FOCUSABLE)?.focus({ preventScroll: true });
5978
7076
  const onKeyDown = (e) => {
5979
7077
  if (e.key === "Escape") {
5980
7078
  e.preventDefault();
@@ -6056,15 +7154,15 @@ function Switcher() {
6056
7154
  })
6057
7155
  ]
6058
7156
  }),
6059
- open && /* @__PURE__ */ jsxs("div", {
7157
+ (open || kept) && /* @__PURE__ */ jsxs("div", {
6060
7158
  ref: panelRef,
6061
7159
  id: "theme-panel",
6062
7160
  role: "dialog",
6063
7161
  "aria-modal": "true",
6064
7162
  "aria-labelledby": "theme-panel-title",
6065
7163
  style: layout.style,
6066
- className: "theme-switcher theme-panel fixed z-[60] flex flex-col rounded-2xl border border-zinc-200 bg-white text-zinc-900 shadow-2xl",
6067
- children: [/* @__PURE__ */ jsx(ThemePanel, {}), /* @__PURE__ */ jsx(ResizeHandles, {
7164
+ className: `theme-switcher theme-panel fixed z-[60] ${open ? "flex" : "hidden"} flex-col rounded-2xl border border-zinc-200 bg-white text-zinc-900 shadow-2xl`,
7165
+ children: [/* @__PURE__ */ jsx(ThemePanel, { open }), /* @__PURE__ */ jsx(ResizeHandles, {
6068
7166
  free: layout.free,
6069
7167
  onStart: onResizeStart,
6070
7168
  onMove: onResizeMove,
@@ -6126,59 +7224,6 @@ function CyclingDot({ tokens, animate }) {
6126
7224
  style: { backgroundColor: still ? tokens.primary : colours[index % colours.length] }
6127
7225
  });
6128
7226
  }
6129
- var BURST_KEYS = [
6130
- "primary",
6131
- "primary-alt",
6132
- "data-1",
6133
- "data-2",
6134
- "data-3",
6135
- "data-4",
6136
- "warning",
6137
- "data-5"
6138
- ];
6139
- var BURST_PIECES = Array.from({ length: 12 }, (_, i) => {
6140
- const angle = (i * 30 + (i % 2 ? 9 : -6)) * (Math.PI / 180);
6141
- const distance = 30 + i % 3 * 9;
6142
- return {
6143
- x: Math.round(Math.cos(angle) * distance),
6144
- y: Math.round(Math.sin(angle) * distance),
6145
- r: Math.round(angle * 180 / Math.PI),
6146
- shape: [
6147
- "squiggle",
6148
- "dot",
6149
- "dash"
6150
- ][i % 3],
6151
- delay: i % 4 * 25
6152
- };
6153
- });
6154
- function Burst({ tokens }) {
6155
- return /* @__PURE__ */ jsx("span", {
6156
- "aria-hidden": "true",
6157
- className: "theme-burst pointer-events-none absolute inset-0",
6158
- children: BURST_PIECES.map((p, i) => /* @__PURE__ */ jsx("span", {
6159
- className: `theme-burst-piece ${p.shape === "squiggle" ? "" : `theme-piece-${p.shape}`}`,
6160
- style: {
6161
- "--x": `${p.x}px`,
6162
- "--y": `${p.y}px`,
6163
- "--r": `${p.r}deg`,
6164
- color: tokens[BURST_KEYS[i % BURST_KEYS.length]],
6165
- animationDelay: `${p.delay}ms`
6166
- },
6167
- children: p.shape === "squiggle" && /* @__PURE__ */ jsx("svg", {
6168
- width: "16",
6169
- height: "10",
6170
- viewBox: "0 0 16 10",
6171
- fill: "none",
6172
- children: /* @__PURE__ */ jsx("path", {
6173
- d: "M1 5c1.6-3.6 3.4-3.6 4.6 0s3 3.6 4.6 0 3-3.6 4.4 0",
6174
- stroke: "currentColor",
6175
- strokeWidth: "2.2",
6176
- strokeLinecap: "round"
6177
- })
6178
- })
6179
- }, i))
6180
- });
6181
- }
6182
7227
  function usePrefersReducedMotion() {
6183
7228
  const query = "(prefers-reduced-motion: reduce)";
6184
7229
  const [reduce, setReduce] = useState(() => window.matchMedia(query).matches);