okchroma 0.6.0 → 0.7.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.
@@ -725,29 +725,14 @@ function perceptualDarkC(L, H2, nativeC, keep = KEEP_DARK, gamut = MASTER_GAMUT)
725
725
  var SHARED_NAMES = {
726
726
  1: "paper-1",
727
727
  2: "paper-3",
728
- // stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
729
- // PLANE in both themes (light sink / dark pop), so it belongs to the surface band.
730
- // NAME ONLY — generation is index-keyed and unchanged; the chalk-collision machinery
731
- // (collision.ts CHALK_STOPS) deliberately still spans stops 3–7. (The requirement
732
- // spec's `group` label followed the band to 'paper' on 2026-08-31 — labels only,
733
- // the spans never moved; see spec.ts groupOf.)
734
- // Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
735
- // paper-5 — same band, same index.
736
728
  3: "paper-5",
737
729
  4: "chalk-8",
738
730
  5: "chalk-11",
739
731
  6: "chalk-15",
740
732
  7: "chalk-20",
741
733
  8: "highlighter-26",
742
- // THE PEN BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
743
- // 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
744
- // deleted; this stop carries both jobs, its on-color a paper token), the between
745
- // text stop (a value the retired text-cta hover state used to generate bespokely,
746
- // promoted to a normal stop), and the strong text stop. The three read as states
747
- // ARE the text-style cta (owner 2026-08-12). C49 restored the top
748
- // two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
749
- // literal in cssRender, not through this table) with the between stop taking the
750
- // vacated index 10; the indices (9/10/11) are unchanged since.
734
+ // the pen band: the first text stop doubles as the emphasis fill, the between stop and
735
+ // the strong stop follow; the three read as states are the text-style CTA (C49)
751
736
  9: "pencil-47",
752
737
  10: "pen-58",
753
738
  11: "pen-70"
@@ -779,63 +764,21 @@ var STAMP_LEAF = {
779
764
  EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
780
765
  ON: STAMP_STATE_LEAVES[STAMP_ON]
781
766
  };
782
- var SYSTEM_LEAF = {
783
- ABS_BLACK: "system/abs-black",
784
- ABS_WHITE: "system/abs-white",
785
- SURFACE: {
786
- DIM: "system/surface/dim",
787
- LOW: "system/surface/low",
788
- MID: "system/surface/mid",
789
- HIGH: "system/surface/high"
790
- },
791
- ALPHA: {
792
- TRANSPARENT: "system/alpha/transparent",
793
- INK: "system/alpha/ink",
794
- // the DIRECTIONAL ladders (owner 2026-08-31, renaming the 2026-08-18 bare rungs
795
- // and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
796
- // (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
797
- // INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
798
- // a "chalk": that word belongs to the tinted band stops.) The reversal lives in
799
- // the engine: a consumer on an inverted ground names one token and never picks a
800
- // pole. Colored fills never ride these; their states stay engine-solved stops.
801
- // Rungs are two-digit percent, harmonizing with shadow-04/08/12.
802
- AWAY_FROM_BG_06: "system/alpha/away-from-bg/06",
803
- AWAY_FROM_BG_08: "system/alpha/away-from-bg/08",
804
- AWAY_FROM_BG_16: "system/alpha/away-from-bg/16",
805
- TOWARD_BG_06: "system/alpha/toward-bg/06",
806
- TOWARD_BG_08: "system/alpha/toward-bg/08",
807
- TOWARD_BG_16: "system/alpha/toward-bg/16",
808
- SHADOW_04: "system/alpha/shadow-04",
809
- SHADOW_08: "system/alpha/shadow-08",
810
- SHADOW_12: "system/alpha/shadow-12"
811
- },
812
- // the OPACITY LADDER's rows: bare numbers, never colors. `alpha` above is the group
813
- // of colors that carry transparency; `opacity` is the numbers they compose with
814
- // (values in cssRender OPACITY_RUNGS; the ext plugin zones these under utility/).
815
- OPACITY: {
816
- "004": "system/opacity/004",
817
- "008": "system/opacity/008",
818
- "012": "system/opacity/012",
819
- "016": "system/opacity/016",
820
- "024": "system/opacity/024",
821
- "032": "system/opacity/032",
822
- "048": "system/opacity/048",
823
- "064": "system/opacity/064"
824
- }
825
- };
826
- var SURFACE_PLANE_LAW = {
827
- [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_0 },
828
- [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
829
- [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
830
- [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_0, dark: SHARED_NAMES[3] }
831
- };
767
+ var cssVarName = (path) => `--${path.join("-")}`;
768
+ var figmaPathOf = (path) => path.join("/");
769
+ var familyPath = (family, leaf) => [family, ...(STAMP_STATE_LEAVES[leaf] ?? leaf).split("/")];
770
+ var LINK_GROUP = "link";
771
+ var LINK_POSTURES = ["default", "inverse"];
772
+ var LINK_STATES = ["enabled", "hover", "pressed"];
773
+ var linkPath = (posture, state) => [LINK_GROUP, posture, state];
774
+ var ABSOLUTE_GROUP = "absolute";
775
+ var absolutePath = (family) => [ABSOLUTE_GROUP, family];
832
776
  var TOKEN_ORDER = [
833
777
  ...Object.values(SHARED_NAMES),
834
778
  STAMP_FILL,
835
779
  STAMP_FILL_HOVER,
836
780
  STAMP_FILL_PRESSED,
837
- STAMP_ON,
838
- "identity"
781
+ STAMP_ON
839
782
  ];
840
783
  function tokenOrder(name) {
841
784
  const i = TOKEN_ORDER.indexOf(name);
@@ -2764,8 +2707,34 @@ var COLOR_WORD = {
2764
2707
  [FAMILY.positive]: " (green)",
2765
2708
  [FAMILY.info]: " (blue)"
2766
2709
  };
2767
- var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2768
- var CHALK = { req: "decorative borders, inverted text, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2710
+ var COLLIDES = "shifts to avoid similar colors";
2711
+ var GUARANTEE_SCOPE = {
2712
+ highlighter: { level: AA_LARGE, grounds: ["paper"] },
2713
+ pencil: { level: AA_BODY, grounds: ["paper"] },
2714
+ pen: { level: AA_BODY, grounds: ["paper", "chalk"] }
2715
+ };
2716
+ var BAND_STOPS = {
2717
+ highlighter: ["highlighter-26"],
2718
+ pencil: ["pencil-47"],
2719
+ pen: ["pen-58", "pen-70"]
2720
+ };
2721
+ var list = (words) => words.length > 1 ? `${words.slice(0, -1).join(", ")} and ${words[words.length - 1]}` : words[0];
2722
+ var ofFamilies = (fam) => fam === FAMILY.neutral ? "of every family" : "of this family and of the neutral";
2723
+ var fromFamilies = (fam) => fam === FAMILY.neutral ? "from every family" : "from this family and from the neutral";
2724
+ var claimOf = (band) => (fam) => `${GUARANTEE_SCOPE[band].level} on every ${GUARANTEE_SCOPE[band].grounds.join(" and ")} ${ofFamilies(fam)}`;
2725
+ var groundLine = (ground) => (fam) => {
2726
+ const bands = Object.keys(GUARANTEE_SCOPE).filter((b) => GUARANTEE_SCOPE[b].grounds.includes(ground));
2727
+ const byLevel = /* @__PURE__ */ new Map();
2728
+ for (const b of bands) {
2729
+ const stops = [...BAND_STOPS[b], ...b === "pen" && fam === FAMILY.neutral ? ["pen-100"] : []];
2730
+ byLevel.set(GUARANTEE_SCOPE[b].level, [...byLevel.get(GUARANTEE_SCOPE[b].level) ?? [], ...stops]);
2731
+ }
2732
+ const clears = [...byLevel.entries()].map(([level, stops]) => `${list(stops)} clear${stops.length === 1 ? "s" : ""} it as ${level}`).join("; ");
2733
+ const notHeld = Object.keys(GUARANTEE_SCOPE).filter((b) => !bands.includes(b));
2734
+ return `${clears}, ${fromFamilies(fam)}${notHeld.length ? `; ${list(notHeld.map((b) => `the ${b}`))} ${notHeld.length === 1 ? "is" : "are"} not held against it` : ""}`;
2735
+ };
2736
+ var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true, ground: groundLine("paper") };
2737
+ var CHALK = { req: "decorative borders, inverted text, illos, signal hierarchy", theming: (f) => TINT[f], collides: true, ground: groundLine("chalk") };
2769
2738
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2770
2739
  var OVERLAY = {
2771
2740
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2777,106 +2746,122 @@ var SCALE = {
2777
2746
  "paper-99-overlay": OVERLAY,
2778
2747
  "paper-3": PAPER,
2779
2748
  "paper-97-overlay": OVERLAY,
2780
- "paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2749
+ "paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true, ground: (f) => `${groundLine("paper")(f)}; the ground the text stops are solved against` },
2781
2750
  "paper-95-overlay": OVERLAY,
2782
2751
  "chalk-8": CHALK,
2783
2752
  "chalk-11": CHALK,
2784
2753
  "chalk-15": CHALK,
2785
2754
  "chalk-20": CHALK,
2786
- // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2787
- // landing on the highlighter rows is worth its ic-ON-s noise — the one surviving body carrier)
2788
- "highlighter-26": { req: "focus rings, icons, large text, translucent state layers over any ground", contrast: AA_LARGE, theming: solved, collides: true },
2789
- "pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2790
- "pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2791
- // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2792
- // words, and a body carrying either floods that token's picker search — the C50 law;
2793
- // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2794
- "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2795
- // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2796
- // these bodies on purpose: it stopped being a token label, so it floods nothing,
2797
- // and a designer's "cta" query still lands on these rows.
2798
- "stamp/fill": { req: "CTAs", theming: (f) => `fully re-solved per theme and family${COLOR_WORD[f]}` },
2799
- "stamp/fill-hover": { req: "CTA pointer-over state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}` },
2800
- "stamp/fill-pressed": { req: "CTA pressed state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}` },
2801
- "stamp/edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}` },
2802
- // ("fill" reworded out 2026-08-18: fill became a label word and is foreign here)
2803
- "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}` }
2755
+ // ("icons" stays although it carries "on": an "icon" query landing on the highlighter
2756
+ // rows is worth that noise)
2757
+ "highlighter-26": { req: "focus rings, icons, large text, translucent state layers over any ground", contrast: AA_LARGE, claim: claimOf("highlighter"), theming: solved, collides: true },
2758
+ "pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, claim: claimOf("pencil"), theming: solved, collides: true },
2759
+ "pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, claim: claimOf("pen"), theming: solved, collides: true },
2760
+ // ("heavy-emphasis": "high" and "low" are plane words in the community plugin and
2761
+ // "strong" carries "on")
2762
+ "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, claim: claimOf("pen"), theming: solved, collides: true },
2763
+ // the stamp family. "CTA" stays in these bodies on purpose: it is not a token label, so
2764
+ // it floods nothing, and a designer's "cta" query lands on these rows.
2765
+ // the usage lines are the owner's guidance, for the document rendering: the fill is
2766
+ // misused as a standalone color in the wild
2767
+ "stamp/fill": {
2768
+ req: "CTAs",
2769
+ theming: (f) => `fully re-solved per theme and family${COLOR_WORD[f]}`,
2770
+ use: "primary button fills; optional for avatars, badges and other elements that carry text. Not a standalone color: it has no contrast guarantee of its own, only its on-text has one. Use it alone or with its hover and pressed states. Always bordered with its edge"
2771
+ },
2772
+ "stamp/fill-hover": { req: "CTA pointer-over state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pointer-over state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2773
+ "stamp/fill-pressed": { req: "CTA pressed state", theming: (f) => `follows its rest fill${COLOR_WORD[f]}`, use: "the pressed state of the fill, with the fill; for nothing else. Always bordered with the edge" },
2774
+ "stamp/edge": { req: "min APCA visibility", theming: (f) => `draws for CTAs that sit close to the page; strength per family tier${COLOR_WORD[f]}`, use: "the border of the fill and its states, always rendered with them and never without them (it resolves to transparent unless the fill sits close to the page); for nothing else" },
2775
+ // ("fill" is a label word and foreign here)
2776
+ "stamp/on": { req: "text over the CTA color", contrast: `${AA_BODY} over its CTA`, theming: (f) => `whichever pole passes; quiet CTAs take the soft pole${COLOR_WORD[f]}`, use: "the only text color over the fill; never elsewhere" }
2777
+ };
2778
+ var NEUTRAL_ONLY = {
2779
+ // the pole is measured against the neutral's own stops only; as text, it is the on-color
2780
+ // of every family's emphasis fill (the band audit holds that)
2781
+ "paper-0": { req: "backgrounds, inverted text", theming: (f) => TINT[f], ground: () => `the neutral's highlighter-26 clears it as ${AA_LARGE}; its pencil-47, pen-58, pen-70 and pen-100 clear it as ${AA_BODY}; as text, ${AA_BODY} on every family's pencil-47` },
2782
+ // the literal pole carries no tint, so no theming line
2783
+ "pen-100": { req: "max-emphasis text", contrast: AA_BODY, claim: () => `${AA_BODY} on every paper and chalk of every family` }
2804
2784
  };
2805
- var ABS = { req: "extreme poles for CTAs, aliased global endpoints" };
2806
- var OFFSET = { req: "min APCA visibility", theming: "offsets CTAs in themes where they sit close to the page" };
2807
- var OFFSET_INVERSE = { req: "state layers over inverted backgrounds", theming: "the offset ladder with its pole flipped, so inverted grounds keep the same rungs" };
2808
- var SHADOW = { req: "drop shadows" };
2809
- var OPACITY_RUNG = { req: "translucency weight for any color", theming: "a bare number; a translucent row composes its color with one rung" };
2810
- var OPACITY_STATE = { req: "translucency weight for any color; the state layer weight of the non-text stop over any ground", theming: "a bare number; a translucent row composes its color with one rung" };
2811
- var PLANE = (req) => ({ req, theming: "aliased to the gray ramp" });
2812
2785
  var LINK = (state, contrast) => ({
2813
2786
  req: "links" + state,
2814
2787
  contrast,
2815
- theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme"
2788
+ claim: () => `${contrast} on every paper of the neutral`,
2789
+ theming: "rides the theme\u2019s link color; custom seed re-solves; overridable per theme",
2790
+ use: "hyperlinks; not a text-style CTA, which is the text stops read as states"
2816
2791
  });
2817
2792
  var LINK_INVERSE = (state, contrast) => ({
2818
2793
  req: "links over inverted backgrounds" + state,
2819
2794
  contrast,
2820
- theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme"
2795
+ claim: () => `${contrast} on any family's pen-70`,
2796
+ theming: "same seed as the link, re-solved for inverted backgrounds; overridable per theme",
2797
+ use: "hyperlinks on a pen-70 ground; not a text-style CTA"
2821
2798
  });
2822
2799
  var SYSTEM = {
2823
- "system/abs-black": ABS,
2824
- "system/abs-white": ABS,
2825
- // (the community system/pen-100 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2826
- // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2827
- // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2828
- "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
2829
- // ("companion" reworded 2026-08-28: compani-ON fed the "on" flood)
2830
- "system/abs-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given" },
2831
- "system/alpha/transparent": { req: "aliased off-states" },
2832
- // the OPACITY LADDER: bare numbers a translucent color row composes with (the
2833
- // scrim is the absolute black at the top rung). No conformance line of its own:
2834
- // the state-layer claim rides the non-text stop these weights apply to
2835
- "system/opacity/004": OPACITY_RUNG,
2836
- "system/opacity/008": OPACITY_STATE,
2837
- "system/opacity/012": OPACITY_STATE,
2838
- "system/opacity/016": OPACITY_STATE,
2839
- "system/opacity/024": OPACITY_STATE,
2840
- "system/opacity/032": OPACITY_STATE,
2841
- "system/opacity/048": OPACITY_RUNG,
2842
- "system/opacity/064": OPACITY_RUNG,
2843
- // ("fills" reworded 2026-08-18: fill became a label word and is foreign here;
2844
- // "on-color" reworded 2026-08-28: the bare on fed the "on" flood)
2845
- "system/alpha/ink": { req: "soft text pole for quiet CTAs" },
2846
- "system/alpha/away-from-bg/06": OFFSET,
2847
- "system/alpha/away-from-bg/08": OFFSET,
2848
- "system/alpha/away-from-bg/16": OFFSET,
2849
- // the inverse ladder: the offset rungs with the pole flipped per mode, for
2850
- // chalks and edges over inverted backgrounds ("interaction" avoided in the
2851
- // bodies: acti-ON would feed the "on" flood)
2852
- "system/alpha/toward-bg/06": OFFSET_INVERSE,
2853
- "system/alpha/toward-bg/08": OFFSET_INVERSE,
2854
- "system/alpha/toward-bg/16": OFFSET_INVERSE,
2855
- "system/alpha/shadow-04": SHADOW,
2856
- "system/alpha/shadow-08": SHADOW,
2857
- "system/alpha/shadow-12": SHADOW,
2858
- // ("elevation" dropped 2026-08-28: elevati-ON fed the "on" flood; the sibling
2859
- // planes never carried the word)
2860
- "system/surface/dim": PLANE("recessed plane"),
2861
- "system/surface/low": PLANE("resting page plane"),
2862
- "system/surface/mid": PLANE("raised plane \u2014 cards, menus"),
2863
- // ("overlays" reworded 2026-08-13: paper-overlay took the word as a label — the C50 law)
2864
- "system/surface/high": PLANE("topmost plane \u2014 modals, dialogs"),
2865
- "system/link/default/enabled": LINK("", AA_BODY),
2866
- "system/link/default/hover": LINK(" pointer-over", AA_BODY),
2867
- "system/link/default/pressed": LINK(" pressed", AA_BODY),
2868
- "system/link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2869
- "system/link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2870
- "system/link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2800
+ "absolute/brand": { req: "identity seed reference", theming: "the theme\u2019s own input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2801
+ "absolute/brand-alt": { req: "identity seed reference", theming: "the theme\u2019s paired input, as given", use: "a reference value (logos, swatches of the input); never a UI color, it carries no contrast guarantee" },
2802
+ "link/default/enabled": LINK("", AA_BODY),
2803
+ "link/default/hover": LINK(" pointer-over", AA_BODY),
2804
+ "link/default/pressed": LINK(" pressed", AA_BODY),
2805
+ "link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2806
+ "link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2807
+ "link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2871
2808
  };
2872
2809
  var PREFIXES = [
2873
- // community spellings FIRST: the 2026-08-21 family rename made the ext word a
2874
- // PREFIX of the community shapes ('brand/' would shadow 'brand/primary/' and
2875
- // 'brand/alt/'), so most-specific must be tried before the family words
2810
+ // community spellings first: the family word is a prefix of the community shapes
2811
+ // ('brand/' would shadow 'brand/primary/' and 'brand/alt/'), so most-specific wins
2876
2812
  ["brand/primary/", FAMILY.brandPrimary],
2877
2813
  ["brand/alt/", FAMILY.brandSecondary],
2878
2814
  ...FAMILIES.map((f) => [f + "/", f])
2879
2815
  ];
2816
+ var ZONE_MAP = [
2817
+ // brand-alt before brand: canonicalize is first-match startsWith, and brand is a prefix
2818
+ // of brand-alt
2819
+ ["system/abs-alt", "absolute/brand-alt"],
2820
+ ["system/abs-primary", "absolute/brand"],
2821
+ ["system/link/", "link/"],
2822
+ ["base/", ""],
2823
+ ["primitive/", ""]
2824
+ ];
2825
+ function canonicalize(path) {
2826
+ for (const [prefix, home] of ZONE_MAP)
2827
+ if (path.startsWith(prefix)) return home + path.slice(prefix.length);
2828
+ return path;
2829
+ }
2830
+ function bodyFor(path) {
2831
+ const canonical = canonicalize(path);
2832
+ if (SYSTEM[canonical]) return { body: SYSTEM[canonical], fam: FAMILY.neutral };
2833
+ const hit = PREFIXES.find(([p]) => canonical.startsWith(p));
2834
+ if (!hit) return void 0;
2835
+ const [prefix, fam] = hit;
2836
+ const leaf = canonical.slice(prefix.length);
2837
+ const body = fam === FAMILY.neutral && NEUTRAL_ONLY[leaf] || SCALE[leaf];
2838
+ return body ? { body, fam } : void 0;
2839
+ }
2840
+ function describeToken(path) {
2841
+ const canonical = canonicalize(path);
2842
+ const title = canonical.replace(/[/-]/g, " ");
2843
+ const hit = bodyFor(canonical);
2844
+ if (!hit) return title;
2845
+ const { body, fam } = hit;
2846
+ const lines = [title, `Req for: ${body.req}`];
2847
+ if (body.contrast) lines.push(body.contrast);
2848
+ if (body.theming) lines.push(`Theming: ${typeof body.theming === "function" ? body.theming(fam) : body.theming}`);
2849
+ if (body.collides && SIGNALS.includes(fam)) lines.push(COLLIDES);
2850
+ return lines.join("\n");
2851
+ }
2852
+ function describeDocument(path) {
2853
+ const hit = bodyFor(canonicalize(path));
2854
+ if (!hit) return "";
2855
+ const { body, fam } = hit;
2856
+ const lines = [`Req for: ${body.req}`];
2857
+ if (body.claim) lines.push(body.claim(fam));
2858
+ else if (body.contrast) lines.push(body.contrast);
2859
+ if (body.ground) lines.push(`Ground: ${body.ground(fam)}`);
2860
+ if (body.use) lines.push(`Use: ${body.use}`);
2861
+ if (body.theming) lines.push(`Theming: ${typeof body.theming === "function" ? body.theming(fam) : body.theming}`);
2862
+ if (body.collides && SIGNALS.includes(fam)) lines.push(COLLIDES);
2863
+ return lines.join("\n");
2864
+ }
2880
2865
 
2881
2866
  // src/engine/signals.ts
2882
2867
  var SIGNALS2 = [
@@ -3195,7 +3180,7 @@ var OUTLINE_PRESSED_ALPHA = 0.18;
3195
3180
  var SOFT_ON_CTA_ALPHA = { light: 0.75, dark: 0.8 };
3196
3181
  function softOnCtaPasses(s, mode) {
3197
3182
  const white = mode === "light" ? s.onFillTextIsWhite : s.onFillTextIsWhiteDark;
3198
- const pole = white ? 1 : 0;
3183
+ const pole2 = white ? 1 : 0;
3199
3184
  const a = SOFT_ON_CTA_ALPHA[mode];
3200
3185
  const states = mode === "light" ? [s.cta, s.ctaHover, s.ctaPressed] : [s.ctaDark, s.ctaHoverDark, s.ctaPressedDark];
3201
3186
  const lin = (v2) => v2 <= 0.04045 ? v2 / 12.92 : ((v2 + 0.055) / 1.055) ** 2.4;
@@ -3205,7 +3190,7 @@ function softOnCtaPasses(s, mode) {
3205
3190
  const e = srgbEmitChannels(st);
3206
3191
  const fr = q2(e.r), fg = q2(e.g), fb = q2(e.b);
3207
3192
  const fillY = relY(fr, fg, fb);
3208
- const textY = relY(pole * a + fr * (1 - a), pole * a + fg * (1 - a), pole * a + fb * (1 - a));
3193
+ const textY = relY(pole2 * a + fr * (1 - a), pole2 * a + fg * (1 - a), pole2 * a + fb * (1 - a));
3209
3194
  if (contrastRatio(textY, fillY) < 4.5) return false;
3210
3195
  }
3211
3196
  return true;
@@ -3451,7 +3436,7 @@ var p3Differs = (s) => s.C > clampChromaToGamut(s.L, s.C, s.H, "srgb") + 1e-4;
3451
3436
  var P3_SUPPORTS = "@supports (color: color(display-p3 1 1 1))";
3452
3437
  var P3_MEDIA = "@media (color-gamut: p3)";
3453
3438
  function stopsToVars(stops, prefix) {
3454
- return [...stops].sort((a, b) => tokenOrder(stopTokenName(a.stop)) - tokenOrder(stopTokenName(b.stop))).map((s) => ` --${prefix}-${stopTokenName(s.stop)}: ${stopHex(s)};`).join("\n");
3439
+ return [...stops].sort((a, b) => tokenOrder(stopTokenName(a.stop)) - tokenOrder(stopTokenName(b.stop))).map((s) => ` ${cssVarName(familyPath(prefix, stopTokenName(s.stop)))}: ${stopHex(s)};`).join("\n");
3455
3440
  }
3456
3441
  var onColor = (white) => white ? "#ffffff" : "#000000";
3457
3442
  var CTA_BORDER_LC_FLOOR = 15;
@@ -3462,34 +3447,7 @@ function pageStopFor(neutral, mode) {
3462
3447
  }
3463
3448
  var OFFSET_ALPHAS = { 6: 0.06, 8: 0.08, 16: 0.16 };
3464
3449
  var ctaBorderRung = (prefix) => prefix === CSS_NEUTRAL ? 8 : prefix === CSS_SECONDARY ? 6 : 16;
3465
- var offsetLeafName = (rung) => String(rung).padStart(2, "0");
3466
- var offsetVarName = (rung) => `--alpha-away-from-bg-${offsetLeafName(rung)}`;
3467
- var OPACITY_RUNGS = { 4: 0.04, 8: 0.08, 12: 0.12, 16: 0.16, 24: 0.24, 32: 0.32, 48: 0.48, 64: 0.64 };
3468
- var INTERACTION_RUNGS = [8, 12, 16, 24, 32];
3469
- var opacityLeafName = (rung) => String(rung).padStart(3, "0");
3470
- var opacityVarName = (rung) => `--opacity-${opacityLeafName(rung)}`;
3471
- var opacityTokenPath = (rung) => SYSTEM_LEAF.OPACITY[opacityLeafName(rung)];
3472
- var SHADOW_ALPHAS = {
3473
- 4: { light: OPACITY_RUNGS[4], dark: OPACITY_RUNGS[32] },
3474
- 8: { light: OPACITY_RUNGS[8], dark: OPACITY_RUNGS[48] },
3475
- 12: { light: OPACITY_RUNGS[12], dark: OPACITY_RUNGS[64] }
3476
- };
3477
- var SCRIM_ALPHA = OPACITY_RUNGS[64];
3478
- var DISABLED_OPACITY = 0.38;
3479
- var TRANSPARENT_VAR = "--alpha-transparent";
3480
3450
  var offsetRgba = (rung, mode) => mode === "light" ? `rgba(0, 0, 0, ${OFFSET_ALPHAS[rung]})` : `rgba(255, 255, 255, ${OFFSET_ALPHAS[rung]})`;
3481
- var alphaRootVars = (mode) => [
3482
- ` ${TRANSPARENT_VAR}: transparent;`,
3483
- // the opacity ladder, mode-invariant like transparent; repeated in the dark block to
3484
- // keep the set together
3485
- ...Object.keys(OPACITY_RUNGS).map(Number).map((r) => ` ${opacityVarName(r)}: ${OPACITY_RUNGS[r]};`),
3486
- ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3487
- // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3488
- // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
3489
- // called a "chalk" — that word is the tinted band's). One spelling with the Figma
3490
- // row: --alpha-toward-bg-06 ↔ system/alpha/toward-bg/06.
3491
- ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` --alpha-toward-bg-${offsetLeafName(r)}: ${offsetRgba(r, mode === "light" ? "dark" : "light")};`)
3492
- ];
3493
3451
  function ctaPageLc(s, mode, page) {
3494
3452
  const cta = mode === "light" ? s.cta : s.ctaDark;
3495
3453
  return Math.abs(apcaLc(apcaY(cta.r, cta.g, cta.b), apcaY(page.r, page.g, page.b)));
@@ -3504,23 +3462,20 @@ function brandKindBody(prefix, s, mode, page, on = prefix === CSS_NEUTRAL ? "sof
3504
3462
  const onCta = mode === "light" ? s.onFillTextIsWhite : s.onFillTextIsWhiteDark;
3505
3463
  const border = ctaNeedsBorder(s, mode, page);
3506
3464
  const scaleBlock = [
3507
- ...stops.map((x2) => ({ name: stopTokenName(x2.stop), line: ` --${prefix}-${stopTokenName(x2.stop)}: ${stopHex(x2)};` }))
3465
+ ...stops.map((x2) => ({ name: stopTokenName(x2.stop), line: ` ${cssVarName(familyPath(prefix, stopTokenName(x2.stop)))}: ${stopHex(x2)};` }))
3508
3466
  ].sort((a, b) => tokenOrder(a.name) - tokenOrder(b.name)).map((v2) => v2.line).join("\n");
3509
3467
  return [
3510
3468
  scaleBlock,
3511
- ` --${prefix}-stamp-fill: ${stopHex(f.cta)};`,
3512
- ` --${prefix}-stamp-fill-hover: ${stopHex(f.ctaHover)};`,
3513
- ` --${prefix}-stamp-fill-pressed: ${stopHex(f.ctaPressed)};`,
3514
- ` --${prefix}-stamp-edge: var(${border ? offsetVarName(ctaBorderRung(prefix)) : TRANSPARENT_VAR});`,
3515
- // the SOFT on-cta (owner 2026-08-04: "neutral cta on's should also be the alpha"): the
3516
- // neutral's cta is the scale-fed CHALK-level fill, the system's other quiet cta, so its
3517
- // button text takes the pole AT ALPHA like the default-model secondary's — composited
3518
- // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3519
- // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
3520
- // primitive. The neutral's is unconditional (the parameter default); the secondary's is
3521
- // the caller's per-mode verdict. Loud fills (brand, signals, the cta escape) keep the
3522
- // solid pole.
3523
- on === "soft" ? ` --${prefix}-stamp-on: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` --${prefix}-stamp-on: ${onColor(onCta)};`
3469
+ ` ${cssVarName(familyPath(prefix, STAMP_FILL))}: ${stopHex(f.cta)};`,
3470
+ ` ${cssVarName(familyPath(prefix, STAMP_FILL_HOVER))}: ${stopHex(f.ctaHover)};`,
3471
+ ` ${cssVarName(familyPath(prefix, STAMP_FILL_PRESSED))}: ${stopHex(f.ctaPressed)};`,
3472
+ ` ${cssVarName(familyPath(prefix, STAMP_EDGE))}: ${border ? offsetRgba(ctaBorderRung(prefix), mode) : "transparent"};`,
3473
+ // the soft on-text: a quiet fill's button text is the pole at alpha, composited over
3474
+ // whatever state the fill is in so hover and pressed carry their own legibility (C47).
3475
+ // The neutral's is unconditional (the parameter default); the secondary's is the
3476
+ // caller's per-mode verdict. Loud fills (the brand, the signals, the cta escape) keep
3477
+ // the solid pole.
3478
+ on === "soft" ? ` ${cssVarName(familyPath(prefix, STAMP_ON))}: rgba(${onCta ? "255, 255, 255" : "0, 0, 0"}, ${SOFT_ON_CTA_ALPHA[mode]});` : ` ${cssVarName(familyPath(prefix, STAMP_ON))}: ${onColor(onCta)};`
3524
3479
  ];
3525
3480
  }
3526
3481
  function ctaFamilyOf(s, mode) {
@@ -3530,9 +3485,9 @@ function brandKindP3Body(prefix, s, mode) {
3530
3485
  const stops = mode === "light" ? s.light : s.dark;
3531
3486
  const f = ctaFamilyOf(s, mode);
3532
3487
  const out = [];
3533
- for (const st of stops) if (p3Differs(st)) out.push(` --${prefix}-${stopTokenName(st.stop)}: ${p3Value(st)};`);
3534
- for (const [name, st] of [["stamp-fill", f.cta], ["stamp-fill-hover", f.ctaHover], ["stamp-fill-pressed", f.ctaPressed]])
3535
- if (p3Differs(st)) out.push(` --${prefix}-${name}: ${p3Value(st)};`);
3488
+ for (const st of stops) if (p3Differs(st)) out.push(` ${cssVarName(familyPath(prefix, stopTokenName(st.stop)))}: ${p3Value(st)};`);
3489
+ for (const [name, st] of [[STAMP_FILL, f.cta], [STAMP_FILL_HOVER, f.ctaHover], [STAMP_FILL_PRESSED, f.ctaPressed]])
3490
+ if (p3Differs(st)) out.push(` ${cssVarName(familyPath(prefix, name))}: ${p3Value(st)};`);
3536
3491
  return out;
3537
3492
  }
3538
3493
  function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBorder = true) {
@@ -3543,16 +3498,14 @@ function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBor
3543
3498
  const p3Dark = brandKindP3Body(CSS_NEUTRAL, s, "dark");
3544
3499
  return [
3545
3500
  `${selector} {`,
3546
- ` --${PAPER_0}: ${p02(s.paper0, "#ffffff")};`,
3547
- ` --${PEN_100}: ${p02(s.pen100, "#000000")};`,
3548
- // the neutral IS the page, so it is judged against its own paper stop
3549
- // the neutral's overlays never read the visibility bar (the exempt family), so
3550
- // the brandless chrome context passes 0
3501
+ ` ${cssVarName(familyPath(CSS_NEUTRAL, PAPER_0))}: ${p02(s.paper0, "#ffffff")};`,
3502
+ ` ${cssVarName(familyPath(CSS_NEUTRAL, PEN_100))}: ${p02(s.pen100, "#000000")};`,
3503
+ // the neutral is the page, so it is judged against its own paper stop
3551
3504
  ...brandKindBody(CSS_NEUTRAL, s, "light", nPage("light")),
3552
3505
  `}`,
3553
3506
  `${selector}[data-theme="dark"] {`,
3554
- ` --${PAPER_0}: ${p02(s.paper0Dark, "#000000")};`,
3555
- ` --${PEN_100}: ${p02(s.pen100Dark, "#ffffff")};`,
3507
+ ` ${cssVarName(familyPath(CSS_NEUTRAL, PAPER_0))}: ${p02(s.paper0Dark, "#000000")};`,
3508
+ ` ${cssVarName(familyPath(CSS_NEUTRAL, PEN_100))}: ${p02(s.pen100Dark, "#ffffff")};`,
3556
3509
  ...brandKindBody(CSS_NEUTRAL, s, "dark", nPage("dark")),
3557
3510
  `}`,
3558
3511
  ...p3Light.length || p3Dark.length ? [
@@ -3584,16 +3537,9 @@ function signalsCss(contrastProfile) {
3584
3537
  return [
3585
3538
  `/* Signal scales \u2014 engine-generated from canonical hexes, shared across brands */`,
3586
3539
  `:root {`,
3587
- // the system alpha variables every family's cta-border aliases (owner 2026-07-29) — this is
3588
- // the engine's one global :root, the CSS counterpart of the Figma side's system/alpha/* rows.
3589
- // Emitted in BOTH blocks because the offset rungs are scheme-DIVERGENT (black in light,
3590
- // white in dark); --alpha-transparent and the opacity ladder (mode-invariant) repeat
3591
- // harmlessly and keep the set together.
3592
- ...alphaRootVars("light"),
3593
3540
  ...lightBlocks,
3594
3541
  `}`,
3595
3542
  `${SIGNALS_DARK_SELECTOR} {`,
3596
- ...alphaRootVars("dark"),
3597
3543
  ...darkBlocks,
3598
3544
  `}`,
3599
3545
  ...p3LightBlocks.length || p3DarkBlocks.length ? [
@@ -3627,69 +3573,72 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3627
3573
  const page = ctaBorder ? { light: pageStopFor(nScale, "light"), dark: pageStopFor(nScale, "dark") } : { light: void 0, dark: void 0 };
3628
3574
  const mirrorBody = (prefix, mode) => {
3629
3575
  const stops = mode === "light" ? scale.light : scale.dark;
3630
- const alias = (name) => ` --${prefix}-${name}: var(--${CSS_BRAND}-${name});`;
3576
+ const alias = (name) => ` ${cssVarName(familyPath(prefix, name))}: var(${cssVarName(familyPath(CSS_BRAND, name))});`;
3631
3577
  return [
3632
3578
  ...stops.map((x2) => alias(stopTokenName(x2.stop))),
3633
- alias("stamp-fill"),
3634
- alias("stamp-fill-hover"),
3635
- alias("stamp-fill-pressed"),
3636
- alias("stamp-edge"),
3637
- alias("stamp-on")
3579
+ alias(STAMP_FILL),
3580
+ alias(STAMP_FILL_HOVER),
3581
+ alias(STAMP_FILL_PRESSED),
3582
+ alias(STAMP_EDGE),
3583
+ alias(STAMP_ON)
3638
3584
  ];
3639
3585
  };
3640
3586
  const secondaryOn = (mode) => secondary && secondaryStyle !== "outline" && softOnCtaPasses(secondary, mode) ? "soft" : "solid";
3641
3587
  const secondaryLight = secondary ? brandKindBody(CSS_SECONDARY, secondary, "light", page.light, secondaryOn("light")) : mirrorBody(CSS_SECONDARY, "light");
3642
3588
  const secondaryDark = secondary ? brandKindBody(CSS_SECONDARY, secondary, "dark", page.dark, secondaryOn("dark")) : mirrorBody(CSS_SECONDARY, "dark");
3643
- const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3644
- const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3589
+ const brandIdentity = ` ${cssVarName(absolutePath(CSS_BRAND))}: ${scale.identityHex};`;
3590
+ const secondaryIdentity = secondary ? ` ${cssVarName(absolutePath(CSS_SECONDARY))}: ${secondary.identityHex};` : ` ${cssVarName(absolutePath(CSS_SECONDARY))}: var(${cssVarName(absolutePath(CSS_BRAND))});`;
3645
3591
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
3646
- const lightAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${PEN_100}: ${p0hex(nScale.pen100, "#000000")};`];
3647
- const darkAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${PEN_100}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
3592
+ const lightAnchors = [` ${cssVarName(familyPath(CSS_NEUTRAL, PAPER_0))}: ${p0hex(nScale.paper0, "#ffffff")};`, ` ${cssVarName(familyPath(CSS_NEUTRAL, PEN_100))}: ${p0hex(nScale.pen100, "#000000")};`];
3593
+ const darkAnchors = [` ${cssVarName(familyPath(CSS_NEUTRAL, PAPER_0))}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` ${cssVarName(familyPath(CSS_NEUTRAL, PEN_100))}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
3648
3594
  const linkTrio = linkHex ? resolveLinkTrio(linkHex, contrastProfile) : null;
3649
- const link = (mode) => linkTrio ? mode === "light" ? [` --link: ${stopHex(linkTrio.link)};`, ` --link-hover: ${stopHex(linkTrio.linkHover)};`, ` --link-pressed: ${stopHex(linkTrio.linkPressed)};`] : [` --link: ${stopHex(linkTrio.linkDark)};`, ` --link-hover: ${stopHex(linkTrio.linkHoverDark)};`, ` --link-pressed: ${stopHex(linkTrio.linkPressedDark)};`] : [
3650
- ` --link: var(--${CSS_BRAND}-${stopTokenName(9)});`,
3651
- ` --link-hover: var(--${CSS_BRAND}-${stopTokenName(10)});`,
3652
- ` --link-pressed: var(--${CSS_BRAND}-${stopTokenName(11)});`
3595
+ const linkVar = (state) => cssVarName(linkPath("default", state));
3596
+ const link = (mode) => linkTrio ? mode === "light" ? [` ${linkVar("enabled")}: ${stopHex(linkTrio.link)};`, ` ${linkVar("hover")}: ${stopHex(linkTrio.linkHover)};`, ` ${linkVar("pressed")}: ${stopHex(linkTrio.linkPressed)};`] : [` ${linkVar("enabled")}: ${stopHex(linkTrio.linkDark)};`, ` ${linkVar("hover")}: ${stopHex(linkTrio.linkHoverDark)};`, ` ${linkVar("pressed")}: ${stopHex(linkTrio.linkPressedDark)};`] : [
3597
+ ` ${linkVar("enabled")}: var(${cssVarName(familyPath(CSS_BRAND, stopTokenName(9)))});`,
3598
+ ` ${linkVar("hover")}: var(${cssVarName(familyPath(CSS_BRAND, stopTokenName(10)))});`,
3599
+ ` ${linkVar("pressed")}: var(${cssVarName(familyPath(CSS_BRAND, stopTokenName(11)))});`
3653
3600
  ];
3654
3601
  const linkP3 = (mode) => {
3655
3602
  if (!linkTrio) return [];
3656
- const trio = mode === "light" ? [["link", linkTrio.link], ["link-hover", linkTrio.linkHover], ["link-pressed", linkTrio.linkPressed]] : [["link", linkTrio.linkDark], ["link-hover", linkTrio.linkHoverDark], ["link-pressed", linkTrio.linkPressedDark]];
3657
- return trio.filter(([, s]) => p3Differs(s)).map(([n, s]) => ` --${n}: ${p3Value(s)};`);
3603
+ const trio = mode === "light" ? [["enabled", linkTrio.link], ["hover", linkTrio.linkHover], ["pressed", linkTrio.linkPressed]] : [["enabled", linkTrio.linkDark], ["hover", linkTrio.linkHoverDark], ["pressed", linkTrio.linkPressedDark]];
3604
+ return trio.filter(([, s]) => p3Differs(s)).map(([n, s]) => ` ${linkVar(n)}: ${p3Value(s)};`);
3658
3605
  };
3659
3606
  const inverseSeed = linkHex ?? scale.identityHex ?? stopHex(scale.light.find((x2) => x2.stop === 9));
3660
3607
  const linkInverseTrio = resolveLinkInverseTrio(inverseSeed, contrastProfile);
3661
- const linkInverse = (mode) => mode === "light" ? [` --link-inverse: ${stopHex(linkInverseTrio.link)};`, ` --link-inverse-hover: ${stopHex(linkInverseTrio.linkHover)};`, ` --link-inverse-pressed: ${stopHex(linkInverseTrio.linkPressed)};`] : [` --link-inverse: ${stopHex(linkInverseTrio.linkDark)};`, ` --link-inverse-hover: ${stopHex(linkInverseTrio.linkHoverDark)};`, ` --link-inverse-pressed: ${stopHex(linkInverseTrio.linkPressedDark)};`];
3608
+ const inverseVar = (state) => cssVarName(linkPath("inverse", state));
3609
+ const linkInverse = (mode) => mode === "light" ? [` ${inverseVar("enabled")}: ${stopHex(linkInverseTrio.link)};`, ` ${inverseVar("hover")}: ${stopHex(linkInverseTrio.linkHover)};`, ` ${inverseVar("pressed")}: ${stopHex(linkInverseTrio.linkPressed)};`] : [` ${inverseVar("enabled")}: ${stopHex(linkInverseTrio.linkDark)};`, ` ${inverseVar("hover")}: ${stopHex(linkInverseTrio.linkHoverDark)};`, ` ${inverseVar("pressed")}: ${stopHex(linkInverseTrio.linkPressedDark)};`];
3662
3610
  const linkInverseP3 = (mode) => {
3663
- const trio = mode === "light" ? [["link-inverse", linkInverseTrio.link], ["link-inverse-hover", linkInverseTrio.linkHover], ["link-inverse-pressed", linkInverseTrio.linkPressed]] : [["link-inverse", linkInverseTrio.linkDark], ["link-inverse-hover", linkInverseTrio.linkHoverDark], ["link-inverse-pressed", linkInverseTrio.linkPressedDark]];
3664
- return trio.filter(([, s]) => p3Differs(s)).map(([n, s]) => ` --${n}: ${p3Value(s)};`);
3611
+ const trio = mode === "light" ? [["enabled", linkInverseTrio.link], ["hover", linkInverseTrio.linkHover], ["pressed", linkInverseTrio.linkPressed]] : [["enabled", linkInverseTrio.linkDark], ["hover", linkInverseTrio.linkHoverDark], ["pressed", linkInverseTrio.linkPressedDark]];
3612
+ return trio.filter(([, s]) => p3Differs(s)).map(([n, s]) => ` ${inverseVar(n)}: ${p3Value(s)};`);
3665
3613
  };
3666
3614
  const escape = (mode) => {
3667
3615
  if (!ctaEscape) return [];
3668
3616
  const esc = escapeCtaFamily(nScale, mode, contrastProfile);
3669
3617
  return [
3670
- ` --${CSS_BRAND}-stamp-fill: ${stopHex(esc.cta)};`,
3671
- ` --${CSS_BRAND}-stamp-fill-hover: ${stopHex(esc.ctaHover)};`,
3672
- ` --${CSS_BRAND}-stamp-fill-pressed: ${stopHex(esc.ctaPressed)};`,
3673
- ` --${CSS_BRAND}-stamp-on: ${onColor(esc.onFillIsWhite)};`
3618
+ ` ${cssVarName(familyPath(CSS_BRAND, STAMP_FILL))}: ${stopHex(esc.cta)};`,
3619
+ ` ${cssVarName(familyPath(CSS_BRAND, STAMP_FILL_HOVER))}: ${stopHex(esc.ctaHover)};`,
3620
+ ` ${cssVarName(familyPath(CSS_BRAND, STAMP_FILL_PRESSED))}: ${stopHex(esc.ctaPressed)};`,
3621
+ ` ${cssVarName(familyPath(CSS_BRAND, STAMP_ON))}: ${onColor(esc.onFillIsWhite)};`
3674
3622
  ];
3675
3623
  };
3676
3624
  const outline = (mode) => {
3677
3625
  if (secondaryStyle !== "outline" || !secondary) return [];
3678
3626
  const s8 = (mode === "light" ? secondary.light : secondary.dark).find((s) => s.stop === 8);
3679
- const c = (v2) => Math.round(Math.min(1, Math.max(0, v2)) * 255);
3627
+ const c = (v3) => Math.round(Math.min(1, Math.max(0, v3)) * 255);
3680
3628
  const s8e = s8 ? srgbEmitChannels(s8) : null;
3629
+ const v2 = (leaf) => cssVarName(familyPath(CSS_SECONDARY, leaf));
3681
3630
  return [
3682
- ` --${CSS_SECONDARY}-stamp-fill: transparent;`,
3631
+ ` ${v2(STAMP_FILL)}: transparent;`,
3683
3632
  ...s8e ? [
3684
- ` --${CSS_SECONDARY}-stamp-fill-hover: rgba(${c(s8e.r)}, ${c(s8e.g)}, ${c(s8e.b)}, ${OUTLINE_HOVER_ALPHA});`,
3685
- ` --${CSS_SECONDARY}-stamp-fill-pressed: rgba(${c(s8e.r)}, ${c(s8e.g)}, ${c(s8e.b)}, ${OUTLINE_PRESSED_ALPHA});`
3633
+ ` ${v2(STAMP_FILL_HOVER)}: rgba(${c(s8e.r)}, ${c(s8e.g)}, ${c(s8e.b)}, ${OUTLINE_HOVER_ALPHA});`,
3634
+ ` ${v2(STAMP_FILL_PRESSED)}: rgba(${c(s8e.r)}, ${c(s8e.g)}, ${c(s8e.b)}, ${OUTLINE_PRESSED_ALPHA});`
3686
3635
  ] : [],
3687
- ` --${CSS_SECONDARY}-stamp-edge: var(--${CSS_SECONDARY}-${stopTokenName(8)});`,
3688
- ` --${CSS_SECONDARY}-stamp-on: var(--${CSS_SECONDARY}-${stopTokenName(9)});`
3636
+ ` ${v2(STAMP_EDGE)}: var(${v2(stopTokenName(8))});`,
3637
+ ` ${v2(STAMP_ON)}: var(${v2(stopTokenName(9))});`
3689
3638
  ];
3690
3639
  };
3691
- const dropOutlineCta = (lines) => secondaryStyle === "outline" ? lines.filter((l) => !l.startsWith(` --${CSS_SECONDARY}-stamp-fill:`) && !l.startsWith(` --${CSS_SECONDARY}-stamp-fill-hover:`) && !l.startsWith(` --${CSS_SECONDARY}-stamp-fill-pressed:`)) : lines;
3692
- const dropEscapeCta = (lines) => ctaEscape ? lines.filter((l) => !new RegExp(`^ --${CSS_BRAND}-stamp-fill(-hover|-pressed)?:`).test(l)) : lines;
3640
+ const dropOutlineCta = (lines) => secondaryStyle === "outline" ? lines.filter((l) => ![STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED].some((leaf) => l.startsWith(` ${cssVarName(familyPath(CSS_SECONDARY, leaf))}:`))) : lines;
3641
+ const dropEscapeCta = (lines) => ctaEscape ? lines.filter((l) => ![STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED].some((leaf) => l.startsWith(` ${cssVarName(familyPath(CSS_BRAND, leaf))}:`))) : lines;
3693
3642
  const p3Light = [
3694
3643
  ...dropEscapeCta(brandKindP3Body(CSS_BRAND, scale, "light")),
3695
3644
  ...secondary ? dropOutlineCta(brandKindP3Body(CSS_SECONDARY, secondary, "light")) : [],
@@ -3797,17 +3746,6 @@ function resolveReferences(own, shared) {
3797
3746
  for (const name of /* @__PURE__ */ new Set([...own.keys(), ...shared.keys()])) out[name] = resolve(lookup(name), 0);
3798
3747
  return out;
3799
3748
  }
3800
- function systemCss() {
3801
- const planeRef = (stop) => stop === PAPER_0 ? `var(--${PAPER_0})` : `var(--${CSS_FAMILY.neutral}-${stop})`;
3802
- const pad = (r) => String(r).padStart(2, "0");
3803
- const lines = (mode) => [
3804
- ...Object.entries(SURFACE_PLANE_LAW).map(([leaf, stops]) => ` --surface-${leaf.split("/").pop()}: ${planeRef(stops[mode])};`),
3805
- ...Object.keys(SHADOW_ALPHAS).map(Number).map((r) => ` --shadow-${pad(r)}: rgba(0, 0, 0, ${SHADOW_ALPHAS[r][mode]});`),
3806
- ` --scrim: rgba(0, 0, 0, ${SCRIM_ALPHA});`,
3807
- ` --disabled-opacity: ${DISABLED_OPACITY};`
3808
- ];
3809
- return [":root {", ...lines("light"), "}", `:root${DARK_MARK}, ${DARK_MARK} {`, ...lines("dark"), "}"].join("\n");
3810
- }
3811
3749
  function tokensFromEmission(slug, css) {
3812
3750
  const { light, dark, order } = readEmission(css);
3813
3751
  const resolvedLight = resolveReferences(light, /* @__PURE__ */ new Map());
@@ -3819,7 +3757,6 @@ function tokensFromEmission(slug, css) {
3819
3757
  function themeTokens(input) {
3820
3758
  const css = [
3821
3759
  signalsCss(input.contrastProfile),
3822
- systemCss(),
3823
3760
  brandCss(
3824
3761
  input.slug,
3825
3762
  input.displayName ?? input.slug,
@@ -3838,168 +3775,35 @@ function themeTokens(input) {
3838
3775
  return tokensFromEmission(input.slug, css);
3839
3776
  }
3840
3777
 
3841
- // src/engine/interaction.ts
3842
- var INTERACTION_POLE_FAMILY = { strong: "neutral-strong", inverse: "neutral-inverse" };
3843
- var INTERACTION_FAMILIES = [
3844
- CSS_FAMILY.neutral,
3845
- CSS_FAMILY.brandPrimary,
3846
- CSS_FAMILY.brandSecondary,
3847
- ...SIGNALS2.map((s) => s.emitName),
3848
- INTERACTION_POLE_FAMILY.strong,
3849
- INTERACTION_POLE_FAMILY.inverse
3850
- ];
3851
- var INTERACTION_STATES = ["enabled", "hover", "pressed", "selected"];
3852
- var INTERACTION_LADDER = {
3853
- subtle: { enabled: 8, hover: 12, pressed: 16, selected: 24 },
3854
- hint: { enabled: null, hover: 8, pressed: 12, selected: 16 }
3855
- };
3856
- var INTERACTION_ROWS = [
3857
- "fg",
3858
- "fg-strong",
3859
- "fg-on-hint",
3860
- "solid-bg-enabled",
3861
- "solid-bg-hover",
3862
- "solid-bg-pressed",
3863
- "solid-border",
3864
- "solid-fg",
3865
- "subtle-bg-enabled",
3866
- "subtle-bg-hover",
3867
- "subtle-bg-pressed",
3868
- "subtle-bg-selected",
3869
- "hint-bg-enabled",
3870
- "hint-bg-hover",
3871
- "hint-bg-pressed",
3872
- "hint-bg-selected"
3873
- ];
3874
- var interactionVarName = (family, row) => `--${family}-${row}`;
3875
- var isPole = (family) => family === INTERACTION_POLE_FAMILY.strong || family === INTERACTION_POLE_FAMILY.inverse;
3876
- var hexChannels = (hex) => {
3877
- const m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex.trim());
3878
- return m ? [parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16)] : null;
3879
- };
3880
- function interactionTintName(family) {
3881
- if (family === INTERACTION_POLE_FAMILY.strong) return PEN_100;
3882
- if (family === INTERACTION_POLE_FAMILY.inverse) return PAPER_0;
3883
- return `${family}-${stopTokenName(8)}`;
3884
- }
3885
- function interactionRows(family, mode, tokens) {
3886
- const neutral = CSS_FAMILY.neutral;
3887
- const v2 = (name) => `var(--${name})`;
3888
- const pen = (stop) => `${family}-${stopTokenName(stop)}`;
3889
- const tintHex = tokens[mode][interactionTintName(family)];
3890
- const ch = tintHex ? hexChannels(tintHex) : null;
3891
- if (!ch) throw new Error(`interactionRows: no sRGB hex for ${interactionTintName(family)} in ${mode}`);
3892
- const at = (rung) => rung === null ? v2(TRANSPARENT_VAR.slice(2)) : `rgba(${ch[0]}, ${ch[1]}, ${ch[2]}, ${OPACITY_RUNGS[rung]})`;
3893
- const text = isPole(family) ? [["fg", v2(interactionTintName(family))], ["fg-strong", v2(interactionTintName(family))], ["fg-on-hint", v2(interactionTintName(family))]] : [["fg", v2(pen(10))], ["fg-strong", v2(pen(11))], ["fg-on-hint", v2(pen(9))]];
3894
- const solid = family === INTERACTION_POLE_FAMILY.strong ? [
3895
- ["solid-bg-enabled", v2(PEN_100)],
3896
- ["solid-bg-hover", v2(`${neutral}-${stopTokenName(11)}`)],
3897
- ["solid-bg-pressed", v2(`${neutral}-${stopTokenName(10)}`)],
3898
- ["solid-border", v2(TRANSPARENT_VAR.slice(2))],
3899
- ["solid-fg", v2(PAPER_0)]
3900
- ] : family === INTERACTION_POLE_FAMILY.inverse ? [
3901
- ["solid-bg-enabled", v2(PAPER_0)],
3902
- ["solid-bg-hover", v2(`${neutral}-${stopTokenName(2)}`)],
3903
- ["solid-bg-pressed", v2(`${neutral}-${stopTokenName(3)}`)],
3904
- ["solid-border", v2(TRANSPARENT_VAR.slice(2))],
3905
- ["solid-fg", v2(PEN_100)]
3906
- ] : [
3907
- ["solid-bg-enabled", v2(`${family}-${STAMP_FILL}`)],
3908
- ["solid-bg-hover", v2(`${family}-${STAMP_FILL_HOVER}`)],
3909
- ["solid-bg-pressed", v2(`${family}-${STAMP_FILL_PRESSED}`)],
3910
- ["solid-border", v2(`${family}-${STAMP_EDGE}`)],
3911
- ["solid-fg", v2(`${family}-${STAMP_ON}`)]
3912
- ];
3913
- const ladder = (tier) => INTERACTION_STATES.map((state) => [`${tier}-bg-${state}`, at(INTERACTION_LADDER[tier][state])]);
3914
- return [...text, ...solid, ...ladder("subtle"), ...ladder("hint")];
3915
- }
3916
- var rowVariesByMode = (row) => /^(subtle|hint)-bg-/.test(row) && row !== "hint-bg-enabled";
3917
- function interactionCss(slug, tokens) {
3918
- const scope = `[data-brand="${slug}"]`;
3919
- const light = [];
3920
- const dark = [];
3921
- for (const family of INTERACTION_FAMILIES) {
3922
- for (const [row, value] of interactionRows(family, "light", tokens)) light.push(` ${interactionVarName(family, row)}: ${value};`);
3923
- for (const [row, value] of interactionRows(family, "dark", tokens)) if (rowVariesByMode(row)) dark.push(` ${interactionVarName(family, row)}: ${value};`);
3924
- }
3925
- const scoped = INTERACTION_FAMILIES.flatMap((family) => [
3926
- `${scope} [data-family="${family}"], ${scope}[data-family="${family}"] {`,
3927
- ...INTERACTION_ROWS.map((row) => ` --${row}: var(${interactionVarName(family, row)});`),
3928
- `}`
3929
- ]);
3930
- return [
3931
- `/* Interaction register: solid, subtle and hint tiers per family, the translucent rows at the engine's opacity rungs */`,
3932
- `${scope} {`,
3933
- ...light,
3934
- `}`,
3935
- `${scope}[data-theme="dark"] {`,
3936
- ...dark,
3937
- `}`,
3938
- ...scoped
3939
- ].join("\n");
3940
- }
3941
- function interactionTokens(tokens) {
3942
- const add = (mode) => {
3943
- const m = /* @__PURE__ */ new Map();
3944
- for (const family of INTERACTION_FAMILIES)
3945
- for (const [row, value] of interactionRows(family, mode, tokens)) m.set(interactionVarName(family, row).slice(2), value);
3946
- return m;
3947
- };
3948
- const rawLight = add("light");
3949
- const rawDark = add("dark");
3950
- const baseLight = new Map(Object.entries(tokens.light));
3951
- const baseDark = new Map(Object.entries(tokens.dark));
3952
- const light = resolveReferences(rawLight, baseLight);
3953
- const dark = resolveReferences(rawDark, baseDark);
3954
- const names = [...tokens.names, ...rawLight.keys()];
3955
- const invariant = [...tokens.invariant, ...[...rawLight.keys()].filter((n) => rawLight.get(n) === rawDark.get(n))];
3956
- return {
3957
- slug: tokens.slug,
3958
- names,
3959
- light: { ...tokens.light, ...Object.fromEntries([...rawLight.keys()].map((n) => [n, light[n]])) },
3960
- dark: { ...tokens.dark, ...Object.fromEntries([...rawDark.keys()].map((n) => [n, dark[n]])) },
3961
- invariant,
3962
- raw: {
3963
- light: { ...tokens.raw.light, ...Object.fromEntries(rawLight) },
3964
- dark: { ...tokens.raw.dark, ...Object.fromEntries(rawDark) }
3965
- }
3966
- };
3967
- }
3968
-
3969
3778
  // src/engine/figmaRender.ts
3970
- var clamp01 = (v2) => Math.min(1, Math.max(0, v2));
3971
- var TRANSPARENT_TOKEN = {
3972
- $type: "color",
3973
- $value: { colorSpace: "srgb", components: [0, 0, 0], alpha: 0, hex: "#000000" }
3974
- };
3779
+ var channel = (v2) => Math.round(Math.round(Math.min(1, Math.max(0, v2)) * 255) / 255 * 1e4) / 1e4;
3780
+ function color(r, g2, b, alpha = 1) {
3781
+ return { $type: "color", $value: { colorSpace: "srgb", components: [channel(r), channel(g2), channel(b)], alpha, hex: toHex2(r, g2, b) } };
3782
+ }
3783
+ var TRANSPARENT_TOKEN = color(0, 0, 0, 0);
3975
3784
  var OFFSET_TOKEN = (rung, mode) => {
3976
3785
  const c = mode === "light" ? 0 : 1;
3977
- return {
3978
- $type: "color",
3979
- $value: { colorSpace: "srgb", components: [c, c, c], alpha: OFFSET_ALPHAS[rung], hex: mode === "light" ? "#000000" : "#ffffff" }
3980
- };
3786
+ return color(c, c, c, OFFSET_ALPHAS[rung]);
3981
3787
  };
3982
3788
  function colorFromStop(s) {
3983
3789
  const { r, g: g2, b } = srgbEmitChannels(s);
3984
- return {
3985
- $type: "color",
3986
- $value: { colorSpace: "srgb", components: [clamp01(r), clamp01(g2), clamp01(b)], alpha: 1, hex: toHex2(r, g2, b) }
3987
- };
3988
- }
3989
- function colorFromHex(white) {
3990
- const v2 = white ? 1 : 0;
3991
- return { $type: "color", $value: { colorSpace: "srgb", components: [v2, v2, v2], alpha: 1, hex: white ? "#ffffff" : "#000000" } };
3790
+ return color(r, g2, b);
3992
3791
  }
3792
+ var pole = (white, alpha = 1) => white ? color(1, 1, 1, alpha) : color(0, 0, 0, alpha);
3993
3793
  function colorFromHexString(hex) {
3994
3794
  const h = hex.replace("#", "");
3995
- const r = parseInt(h.slice(0, 2), 16) / 255;
3996
- const g2 = parseInt(h.slice(2, 4), 16) / 255;
3997
- const b = parseInt(h.slice(4, 6), 16) / 255;
3998
- return { $type: "color", $value: { colorSpace: "srgb", components: [r, g2, b], alpha: 1, hex: hex.toLowerCase() } };
3795
+ return color(parseInt(h.slice(0, 2), 16) / 255, parseInt(h.slice(2, 4), 16) / 255, parseInt(h.slice(4, 6), 16) / 255);
3999
3796
  }
4000
3797
  function bandedLeaf(flat) {
4001
3798
  return STAMP_STATE_LEAVES[flat] ?? flat;
4002
3799
  }
3800
+ function groupEntries(g2) {
3801
+ const entries = Object.entries(g2);
3802
+ const digitLeading = (k) => /^\d/.test(k);
3803
+ if (entries.length > 1 && entries.every(([k]) => digitLeading(k)))
3804
+ return entries.sort((a, b) => parseInt(b[0], 10) - parseInt(a[0], 10));
3805
+ return entries;
3806
+ }
4003
3807
  function putLeaf(g2, flat, tok) {
4004
3808
  const path = bandedLeaf(flat).split("/");
4005
3809
  let cur = g2;
@@ -4017,8 +3821,7 @@ function rampGroup(stops, onFillWhite, extra) {
4017
3821
  if (extra?.ctaHover) putLeaf(g2, STAMP_FILL_HOVER, colorFromStop(extra.ctaHover));
4018
3822
  if (extra?.ctaPressed) putLeaf(g2, STAMP_FILL_PRESSED, colorFromStop(extra.ctaPressed));
4019
3823
  if (extra?.cta) putLeaf(g2, STAMP_EDGE, extra.ctaBorder ?? TRANSPARENT_TOKEN);
4020
- putLeaf(g2, STAMP_ON, colorFromHex(onFillWhite));
4021
- if (extra?.identityHex) g2["identity"] = colorFromHexString(extra.identityHex);
3824
+ putLeaf(g2, STAMP_ON, pole(onFillWhite));
4022
3825
  return g2;
4023
3826
  }
4024
3827
  function themeToFigma(r, input) {
@@ -4031,10 +3834,6 @@ function themeToFigma(r, input) {
4031
3834
  ctaBorder: ctaNeedsBorder(s, mode, borderPage(mode)) ? OFFSET_TOKEN(ctaBorderRung(prefix), mode) : TRANSPARENT_TOKEN,
4032
3835
  ...mode === "light" ? { cta: s.cta, ctaHover: s.ctaHover, ctaPressed: s.ctaPressed } : { cta: s.ctaDark, ctaHover: s.ctaHoverDark, ctaPressed: s.ctaPressedDark }
4033
3836
  });
4034
- const brandExtra = (s, mode, prefix) => ({
4035
- identityHex: s.identityHex,
4036
- ...ctaFamily(s, mode, prefix)
4037
- });
4038
3837
  const nScale = generateNeutralScale(input.neutralH ?? scale.brandH, input.neutralLevel ?? "default", input.contrastProfile);
4039
3838
  const lt = input.linkHex ? resolveLinkTrio(input.linkHex, input.contrastProfile) : null;
4040
3839
  const invSeed = input.linkHex ?? scale.identityHex ?? (() => {
@@ -4043,10 +3842,9 @@ function themeToFigma(r, input) {
4043
3842
  return toHex2(e.r, e.g, e.b);
4044
3843
  })();
4045
3844
  const invLt = resolveLinkInverseTrio(invSeed, input.contrastProfile);
4046
- const neutralExtra = (mode) => ctaFamily(nScale, mode, CSS_FAMILY.neutral);
4047
3845
  const build = (mode) => {
4048
3846
  const p02 = mode === "light" ? nScale.paper0 : nScale.paper0Dark;
4049
- const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, neutralExtra(mode));
3847
+ const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, ctaFamily(nScale, mode, CSS_FAMILY.neutral));
4050
3848
  const i02 = mode === "light" ? nScale.pen100 : nScale.pen100Dark;
4051
3849
  const splicePen100 = (g3) => {
4052
3850
  if (!i02) return g3;
@@ -4058,41 +3856,31 @@ function themeToFigma(r, input) {
4058
3856
  return out;
4059
3857
  };
4060
3858
  const neutralGroup = splicePen100(p02 ? { [PAPER_0]: colorFromStop(p02), ...ramp } : ramp);
4061
- const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, brandExtra(secondary, mode, CSS_FAMILY.brandSecondary));
3859
+ const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, ctaFamily(secondary, mode, CSS_FAMILY.brandSecondary));
4062
3860
  if (input.secondaryStyle === "outline" && input.secondary) {
4063
3861
  const s8 = secondary[mode].find((s) => s.stop === 8);
4064
3862
  const s9 = secondary[mode].find((s) => s.stop === 9);
4065
3863
  putLeaf(secondaryGroup, STAMP_FILL, TRANSPARENT_TOKEN);
4066
3864
  if (s8) {
4067
3865
  const e = srgbEmitChannels(s8);
4068
- const alphaTint = (alpha) => ({
4069
- $type: "color",
4070
- $value: { colorSpace: "srgb", components: [clamp01(e.r), clamp01(e.g), clamp01(e.b)], alpha, hex: toHex2(e.r, e.g, e.b) }
4071
- });
4072
- putLeaf(secondaryGroup, STAMP_FILL_HOVER, alphaTint(OUTLINE_HOVER_ALPHA));
4073
- putLeaf(secondaryGroup, STAMP_FILL_PRESSED, alphaTint(OUTLINE_PRESSED_ALPHA));
3866
+ putLeaf(secondaryGroup, STAMP_FILL_HOVER, color(e.r, e.g, e.b, OUTLINE_HOVER_ALPHA));
3867
+ putLeaf(secondaryGroup, STAMP_FILL_PRESSED, color(e.r, e.g, e.b, OUTLINE_PRESSED_ALPHA));
3868
+ putLeaf(secondaryGroup, STAMP_EDGE, colorFromStop(s8));
4074
3869
  }
4075
- if (s8) putLeaf(secondaryGroup, STAMP_EDGE, colorFromStop(s8));
4076
3870
  if (s9) putLeaf(secondaryGroup, STAMP_ON, colorFromStop(s9));
4077
3871
  }
4078
- const softOnCta = (g3, white) => {
4079
- const p = white ? 1 : 0;
4080
- putLeaf(g3, STAMP_ON, {
4081
- $type: "color",
4082
- $value: { colorSpace: "srgb", components: [p, p, p], alpha: SOFT_ON_CTA_ALPHA[mode], hex: white ? "#ffffff" : "#000000" }
4083
- });
4084
- };
3872
+ const softOnCta = (g3, white) => putLeaf(g3, STAMP_ON, pole(white, SOFT_ON_CTA_ALPHA[mode]));
4085
3873
  softOnCta(neutralGroup, mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark);
4086
3874
  if (input.secondary && input.secondaryStyle !== "outline" && softOnCtaPasses(input.secondary, mode))
4087
3875
  softOnCta(secondaryGroup, mode === "light" ? secondaryOnFillLight : secondaryOnFillDark);
4088
- const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, brandExtra(scale, mode, CSS_FAMILY.brandPrimary));
3876
+ const brandGroup = rampGroup(scale[mode], mode === "light" ? scale.onFillTextIsWhite : scale.onFillTextIsWhiteDark, ctaFamily(scale, mode, CSS_FAMILY.brandPrimary));
4089
3877
  const esc = input.ctaEscape ? escapeCtaFamily(nScale, mode, input.contrastProfile) : null;
4090
3878
  if (esc) {
4091
3879
  for (const g3 of input.secondary ? [brandGroup] : [brandGroup, secondaryGroup]) {
4092
3880
  putLeaf(g3, STAMP_FILL, colorFromStop(esc.cta));
4093
3881
  putLeaf(g3, STAMP_FILL_HOVER, colorFromStop(esc.ctaHover));
4094
3882
  putLeaf(g3, STAMP_FILL_PRESSED, colorFromStop(esc.ctaPressed));
4095
- putLeaf(g3, STAMP_ON, colorFromHex(esc.onFillIsWhite));
3883
+ putLeaf(g3, STAMP_ON, pole(esc.onFillIsWhite));
4096
3884
  }
4097
3885
  }
4098
3886
  const scaleTextAt = (n) => {
@@ -4100,146 +3888,126 @@ function themeToFigma(r, input) {
4100
3888
  if (!s) throw new Error(`themeToFigma link: the brand scale has no pen stop ${n}`);
4101
3889
  return s;
4102
3890
  };
4103
- const linkGroup = lt ? mode === "light" ? { "link": colorFromStop(lt.link), "link-hover": colorFromStop(lt.linkHover), "link-pressed": colorFromStop(lt.linkPressed) } : { "link": colorFromStop(lt.linkDark), "link-hover": colorFromStop(lt.linkHoverDark), "link-pressed": colorFromStop(lt.linkPressedDark) } : { "link": colorFromStop(scaleTextAt(9)), "link-hover": colorFromStop(scaleTextAt(10)), "link-pressed": colorFromStop(scaleTextAt(11)) };
4104
- const linkInverseGroup = mode === "light" ? { "link": colorFromStop(invLt.link), "link-hover": colorFromStop(invLt.linkHover), "link-pressed": colorFromStop(invLt.linkPressed) } : { "link": colorFromStop(invLt.linkDark), "link-hover": colorFromStop(invLt.linkHoverDark), "link-pressed": colorFromStop(invLt.linkPressedDark) };
3891
+ const trio = (posture) => {
3892
+ const t = posture === "default" ? lt ? mode === "light" ? [lt.link, lt.linkHover, lt.linkPressed] : [lt.linkDark, lt.linkHoverDark, lt.linkPressedDark] : [scaleTextAt(9), scaleTextAt(10), scaleTextAt(11)] : mode === "light" ? [invLt.link, invLt.linkHover, invLt.linkPressed] : [invLt.linkDark, invLt.linkHoverDark, invLt.linkPressedDark];
3893
+ return Object.fromEntries(LINK_STATES.map((state, i) => [state, colorFromStop(t[i])]));
3894
+ };
4105
3895
  const g2 = {
4106
- brand: brandGroup,
4107
- secondary: secondaryGroup,
4108
- neutral: neutralGroup,
4109
- link: linkGroup,
4110
- "link-inverse": linkInverseGroup
3896
+ [CSS_FAMILY.neutral]: neutralGroup,
3897
+ [CSS_FAMILY.brandPrimary]: brandGroup,
3898
+ [CSS_FAMILY.brandSecondary]: secondaryGroup
4111
3899
  };
4112
3900
  for (const sig of input.signals) {
4113
- g2[sig.name] = rampGroup(
3901
+ const role = SIGNAL_EMIT_NAME[sig.name] ?? sig.name;
3902
+ g2[role] = rampGroup(
4114
3903
  sig.scale[mode],
4115
3904
  mode === "light" ? sig.scale.onFillTextIsWhite : sig.scale.onFillTextIsWhiteDark,
4116
- // signals rank with the primary (ctaBorderRung: anything not neutral/secondary takes 16).
4117
- // Unreachable at the Lc 15 gate — no signal gets within 4 Lc of it — but defined, not accidental.
4118
- ctaFamily(sig.scale, mode, sig.name)
3905
+ ctaFamily(sig.scale, mode, role)
4119
3906
  );
4120
3907
  }
4121
- const pole = (white, alpha) => {
4122
- const c = white ? 1 : 0;
4123
- return {
4124
- $type: "color",
4125
- $value: { colorSpace: "srgb", components: [c, c, c], alpha, hex: white ? "#ffffff" : "#000000" }
4126
- };
3908
+ g2[LINK_GROUP] = { default: trio("default"), inverse: trio("inverse") };
3909
+ g2[ABSOLUTE_GROUP] = {
3910
+ [CSS_FAMILY.brandPrimary]: colorFromHexString(scale.identityHex ?? invSeed),
3911
+ [CSS_FAMILY.brandSecondary]: colorFromHexString((input.secondary ?? scale).identityHex ?? invSeed)
4127
3912
  };
4128
- putLeaf(g2, SYSTEM_LEAF.ABS_BLACK, colorFromHex(false));
4129
- putLeaf(g2, SYSTEM_LEAF.ABS_WHITE, colorFromHex(true));
4130
- for (const [path, law] of Object.entries(SURFACE_PLANE_LAW)) {
4131
- const tok = neutralGroup[law[mode]];
4132
- if (tok && "$type" in tok) putLeaf(g2, path, tok);
4133
- }
4134
- putLeaf(g2, SYSTEM_LEAF.ALPHA.TRANSPARENT, pole(true, 0));
4135
- for (const rung of Object.keys(OPACITY_RUNGS).map(Number))
4136
- putLeaf(g2, opacityTokenPath(rung), { $type: "number", $value: rung });
4137
- putLeaf(g2, SYSTEM_LEAF.ALPHA.INK, pole(mode === "dark", SOFT_ON_CTA_ALPHA[mode]));
4138
- putLeaf(g2, SYSTEM_LEAF.ALPHA.AWAY_FROM_BG_06, OFFSET_TOKEN(6, mode));
4139
- putLeaf(g2, SYSTEM_LEAF.ALPHA.AWAY_FROM_BG_08, OFFSET_TOKEN(8, mode));
4140
- putLeaf(g2, SYSTEM_LEAF.ALPHA.AWAY_FROM_BG_16, OFFSET_TOKEN(16, mode));
4141
- const inv = mode === "light" ? "dark" : "light";
4142
- putLeaf(g2, SYSTEM_LEAF.ALPHA.TOWARD_BG_06, OFFSET_TOKEN(6, inv));
4143
- putLeaf(g2, SYSTEM_LEAF.ALPHA.TOWARD_BG_08, OFFSET_TOKEN(8, inv));
4144
- putLeaf(g2, SYSTEM_LEAF.ALPHA.TOWARD_BG_16, OFFSET_TOKEN(16, inv));
4145
- putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_04, pole(false, SHADOW_ALPHAS[4][mode]));
4146
- putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_08, pole(false, SHADOW_ALPHAS[8][mode]));
4147
- putLeaf(g2, SYSTEM_LEAF.ALPHA.SHADOW_12, pole(false, SHADOW_ALPHAS[12][mode]));
4148
3913
  return g2;
4149
3914
  };
4150
3915
  return { light: build("light"), dark: build("dark") };
4151
3916
  }
4152
3917
 
4153
- // src/engine/requirements/dtcg.ts
4154
- var EXT_KEY = "org.okchroma.requirement";
4155
- var RESOLVER_ID = "okchroma-reqtoken@2";
4156
- var LEGACY_ROLE_NAMES = {
4157
- "cta": STAMP_FILL,
4158
- "cta-hover": STAMP_FILL_HOVER,
4159
- "cta-pressed": STAMP_FILL_PRESSED
4160
- };
4161
- var hexToValue = (hex) => {
4162
- const h = hex.replace("#", "");
4163
- const c = (i) => Math.round(parseInt(h.slice(i, i + 2), 16) / 255 * 1e4) / 1e4;
4164
- return { colorSpace: "srgb", components: [c(0), c(2), c(4)], alpha: 1, hex: hex.toLowerCase() };
4165
- };
4166
- function emitDtcgRamp(hex, mode, groupName, spec) {
4167
- spec ?? (spec = MODE_SPECS[mode]);
4168
- const ramp = resolveRamp(hex, mode, spec);
4169
- const seedRef = `{${groupName}.seed}`;
4170
- const group = {
4171
- seed: { $type: "color", $value: hexToValue(hex) },
4172
- $extensions: { [EXT_KEY]: { resolver: RESOLVER_ID, ons: spec.ons } }
4173
- };
4174
- for (const sp of spec.stops) {
4175
- const st = ramp.stops.find((s) => s.stop === sp.stop);
4176
- group[String(sp.stop)] = { $type: "color", $value: hexToValue(st.hex), $extensions: { [EXT_KEY]: { resolver: RESOLVER_ID, seed: seedRef, mode, ...sp } } };
4177
- }
4178
- const roleVal = {
4179
- [STAMP_FILL]: ramp.roles.cta,
4180
- [STAMP_FILL_HOVER]: ramp.roles.ctaHover,
4181
- [STAMP_FILL_PRESSED]: ramp.roles.ctaPressed
3918
+ // src/engine/dtcgRender.ts
3919
+ var STAMP = [STAMP_FILL, STAMP_FILL_HOVER, STAMP_FILL_PRESSED, STAMP_EDGE, STAMP_ON];
3920
+ var STOPS = Array.from({ length: SCALE_STOP_COUNT }, (_, i) => stopTokenName(i + 1));
3921
+ function tokenPaths() {
3922
+ const family = (word, poles) => [
3923
+ ...poles ? [familyPath(word, PAPER_0)] : [],
3924
+ ...STOPS.map((leaf) => familyPath(word, leaf)),
3925
+ ...poles ? [familyPath(word, PEN_100)] : [],
3926
+ ...STAMP.map((leaf) => familyPath(word, leaf))
3927
+ ];
3928
+ return [
3929
+ ...family(CSS_FAMILY.neutral, true),
3930
+ ...family(CSS_FAMILY.brandPrimary, false),
3931
+ ...family(CSS_FAMILY.brandSecondary, false),
3932
+ ...SIGNALS2.flatMap((s) => family(s.emitName, false)),
3933
+ ...LINK_POSTURES.flatMap((posture) => LINK_STATES.map((state) => linkPath(posture, state))),
3934
+ absolutePath(CSS_FAMILY.brandPrimary),
3935
+ absolutePath(CSS_FAMILY.brandSecondary)
3936
+ ];
3937
+ }
3938
+ var byName = () => new Map(tokenPaths().map((p) => [cssVarName(p).slice(2), p]));
3939
+ function tokenPathOf(name) {
3940
+ return byName().get(name);
3941
+ }
3942
+ function descriptionPathOf(name) {
3943
+ const p = tokenPathOf(name);
3944
+ return p && figmaPathOf(p);
3945
+ }
3946
+ var component = (c8) => Math.round(c8 / 255 * 1e4) / 1e4;
3947
+ var colorValue = (r, g2, b, alpha) => ({
3948
+ colorSpace: "srgb",
3949
+ components: [component(r), component(g2), component(b)],
3950
+ alpha,
3951
+ hex: "#" + [r, g2, b].map((c) => c.toString(16).padStart(2, "0")).join("")
3952
+ });
3953
+ function dtcgColor(value) {
3954
+ let m;
3955
+ if (m = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(value)) return colorValue(parseInt(m[1], 16), parseInt(m[2], 16), parseInt(m[3], 16), 1);
3956
+ if (m = /^rgba\((\d+), (\d+), (\d+), ([0-9.]+)\)$/.exec(value)) return colorValue(+m[1], +m[2], +m[3], +m[4]);
3957
+ if (value === "transparent") return colorValue(0, 0, 0, 0);
3958
+ throw new Error(`dtcgColor: not a color the emitters write: ${value}`);
3959
+ }
3960
+ var ALIAS = /^var\(--([a-z0-9-]+)\)$/;
3961
+ function tokensToDtcg(tokens) {
3962
+ const paths = byName();
3963
+ const roster = new Set(paths.keys());
3964
+ for (const name of tokens.names) if (!roster.has(name)) throw new Error(`tokensToDtcg: the emit carries a name the roster does not know: ${name}`);
3965
+ for (const name of roster) if (!(name in tokens.light)) throw new Error(`tokensToDtcg: the emit lacks ${name}`);
3966
+ const build = (mode) => {
3967
+ const doc = {};
3968
+ for (const [name, path] of paths) {
3969
+ const raw = tokens.raw[mode][name] ?? tokens.raw.light[name];
3970
+ const alias = ALIAS.exec(raw);
3971
+ let token;
3972
+ if (alias) {
3973
+ const target = paths.get(alias[1]);
3974
+ if (!target) throw new Error(`tokensToDtcg: ${name} references a name outside the roster: ${alias[1]}`);
3975
+ token = { $type: "color", $value: `{${target.join(".")}}` };
3976
+ } else {
3977
+ token = { $type: "color", $value: dtcgColor(tokens[mode][name]) };
3978
+ }
3979
+ const body = describeDocument(figmaPathOf(path));
3980
+ if (body) token.$description = body;
3981
+ let cur = doc;
3982
+ for (const seg of path.slice(0, -1)) {
3983
+ const next = cur[seg];
3984
+ if (next && "$type" in next) throw new Error(`tokensToDtcg: ${seg} is both a token and a group on the way to ${path.join("/")}`);
3985
+ cur = cur[seg] ?? (cur[seg] = {});
3986
+ }
3987
+ const leaf = path[path.length - 1];
3988
+ if (leaf in cur) throw new Error(`tokensToDtcg: duplicate path ${path.join("/")}`);
3989
+ cur[leaf] = token;
3990
+ }
3991
+ return doc;
4182
3992
  };
4183
- for (const rr of spec.roles) {
4184
- const rv = roleVal[rr.role];
4185
- group[rr.role] = { $type: "color", $value: hexToValue(rv.hex), $extensions: { [EXT_KEY]: { resolver: RESOLVER_ID, seed: seedRef, mode, ...rr } } };
4186
- }
4187
- return group;
4188
- }
4189
- function parseToken(token) {
4190
- const ext = token.$extensions?.[EXT_KEY];
4191
- if (!ext) throw new Error("missing $extensions." + EXT_KEY);
4192
- if (ext.resolver !== RESOLVER_ID) throw new Error(`unknown resolver "${ext.resolver}" (this resolver is ${RESOLVER_ID})`);
4193
- const { resolver, seed, mode, ...req } = ext;
4194
- const isRole = "role" in req;
4195
- if (!isRole) {
4196
- for (const k of ["stop", "rootL", "group", "produce"]) if (req[k] === void 0) throw new Error(`stop requirement missing "${k}"`);
4197
- }
4198
- if (isRole) {
4199
- for (const k of ["role", "produce", "floorL", "chromaMult"]) if (req[k] === void 0) throw new Error(`role requirement missing "${k}"`);
4200
- }
4201
- if (isRole && LEGACY_ROLE_NAMES[req.role]) req.role = LEGACY_ROLE_NAMES[req.role];
4202
- return { req, seedRef: seed, mode };
4203
- }
4204
- function resolveDtcgRamp(group) {
4205
- const seedHex = group.seed.$value.hex;
4206
- const ons = group.$extensions?.[EXT_KEY]?.ons;
4207
- if (!ons) throw new Error("missing group-level ons declaration");
4208
- const stops = [];
4209
- const roles = [];
4210
- let mode;
4211
- for (const [k, t] of Object.entries(group)) {
4212
- if (k === "seed" || k === "$extensions") continue;
4213
- const { req, mode: m } = parseToken(t);
4214
- mode ?? (mode = m);
4215
- "role" in req ? roles.push(req) : stops.push(req);
4216
- }
4217
- if (!mode) throw new Error("empty ramp group");
4218
- stops.sort((a, b) => a.stop - b.stop);
4219
- return resolveRamp(seedHex, mode, { stops, roles, ons });
3993
+ return { light: build("light"), dark: build("dark") };
4220
3994
  }
4221
3995
  export {
3996
+ ABSOLUTE_GROUP,
3997
+ BAND_STOPS,
4222
3998
  CSS_FAMILY,
4223
3999
  DEFAULT_LINK_HEX,
4224
- DISABLED_OPACITY,
4225
- EXT_KEY,
4226
- INTERACTION_FAMILIES,
4227
- INTERACTION_LADDER,
4228
- INTERACTION_POLE_FAMILY,
4229
- INTERACTION_ROWS,
4230
- INTERACTION_RUNGS,
4231
- INTERACTION_STATES,
4000
+ GUARANTEE_SCOPE,
4001
+ LINK_GROUP,
4002
+ LINK_POSTURES,
4003
+ LINK_STATES,
4232
4004
  MODE_SPECS,
4233
4005
  OFFSET_ALPHAS,
4234
- OPACITY_RUNGS,
4235
4006
  OUTLINE_HOVER_ALPHA,
4236
4007
  PAPER_0,
4237
4008
  PEN_100,
4238
- RESOLVER_ID,
4239
4009
  SCALE_STOP_COUNT,
4240
- SCRIM_ALPHA,
4241
4010
  SECONDARY_DISTINCT_DELTA_E,
4242
- SHADOW_ALPHAS,
4243
4011
  SIGNALS2 as SIGNALS,
4244
4012
  SIGNAL_EMIT_NAME,
4245
4013
  SIGNAL_SCALES,
@@ -4247,38 +4015,35 @@ export {
4247
4015
  STAMP_FILL,
4248
4016
  STAMP_FILL_HOVER,
4249
4017
  STAMP_FILL_PRESSED,
4018
+ STAMP_LEAF,
4250
4019
  STAMP_ON,
4251
4020
  STAMP_STATE_LEAVES,
4252
4021
  SUBTLE_PASTEL_K,
4253
4022
  SUBTLE_TINT_MULT,
4254
- SURFACE_PLANE_LAW,
4255
- SYSTEM_LEAF,
4023
+ absolutePath,
4256
4024
  brandCss,
4257
4025
  checkAllCollisions,
4258
4026
  checkCollision,
4259
4027
  checkHueCollision,
4260
4028
  classifyArchetype,
4029
+ cssVarName,
4261
4030
  ctaBorderRung,
4262
4031
  ctaNeedsBorder,
4263
- emitDtcgRamp,
4032
+ describeDocument,
4033
+ describeToken,
4034
+ descriptionPathOf,
4035
+ familyPath,
4036
+ figmaPathOf,
4264
4037
  generateNeutralScale,
4265
4038
  generateScale,
4266
4039
  generateSubtleSecondary,
4267
- interactionCss,
4268
- interactionRows,
4269
- interactionTintName,
4270
- interactionTokens,
4271
- interactionVarName,
4040
+ groupEntries,
4041
+ linkPath,
4272
4042
  neutralCss,
4273
4043
  neutralTintHue,
4274
- opacityLeafName,
4275
- opacityTokenPath,
4276
- opacityVarName,
4277
4044
  pageStopFor,
4278
- parseToken,
4279
4045
  readEmission,
4280
4046
  resolveBrand,
4281
- resolveDtcgRamp,
4282
4047
  resolveLinkInverseTrio,
4283
4048
  resolveLinkTrio,
4284
4049
  resolveReferences,
@@ -4288,10 +4053,11 @@ export {
4288
4053
  stopHex,
4289
4054
  stopTokenName,
4290
4055
  stopsToVars,
4291
- systemCss,
4292
4056
  themeToFigma,
4293
4057
  themeTokens,
4294
4058
  toHex2 as toHex,
4295
4059
  tokenOrder,
4296
- tokensFromEmission
4060
+ tokenPathOf,
4061
+ tokensFromEmission,
4062
+ tokensToDtcg
4297
4063
  };