okchroma 0.1.6 → 0.2.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.
@@ -203,12 +203,12 @@ var SCALE_C_LIGHT = {
203
203
  6: { base: 0.068, sat: 0.85 },
204
204
  7: { base: 0.086, sat: 0.78 },
205
205
  8: { base: 0.142, sat: 0.78 },
206
- 9: { inkMult: 0.95, inkMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
206
+ 9: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
207
207
  // ink-10 keeps the FIRST-text register (ink-9's row, C49): the retired hover law
208
208
  // evaluated ink-9's chroma register at the between L, so the same params reproduce it;
209
209
  // the damped strong register below belongs to the top text stop alone.
210
- 10: { inkMult: 0.95, inkMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
211
- 11: { inkMult: 0.5, inkMaxC: 0.08, chromaFloor: chromaFloorBase(11) }
210
+ 10: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
211
+ 11: { textMult: 0.5, textMaxC: 0.08, chromaFloor: chromaFloorBase(11) }
212
212
  };
213
213
  var SCALE_C_DARK = {
214
214
  0: { sat: 0.4 },
@@ -220,9 +220,9 @@ var SCALE_C_DARK = {
220
220
  6: { sat: 0.76 },
221
221
  7: { sat: 0.8 },
222
222
  8: { sat: 0.84 },
223
- 9: { inkMult: 0.95, inkMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
224
- 10: { inkMult: 0.95, inkMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
225
- 11: { inkMult: 0.62, inkMaxC: 0.045, chromaFloor: chromaFloorBase(11) }
223
+ 9: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
224
+ 10: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
225
+ 11: { textMult: 0.62, textMaxC: 0.045, chromaFloor: chromaFloorBase(11) }
226
226
  };
227
227
  var DARK_CTA_C = {
228
228
  brand: {
@@ -239,10 +239,10 @@ var DARK_CTA_C = {
239
239
  };
240
240
  var STOP_8_NONTEXT_CONTRAST = 3;
241
241
  var DARK_BAND_TOP_LIFT = 1.19;
242
- var INK_9_CONTRAST = 4.5;
243
- var INK_10_CONTRAST = 4.5;
244
- var INK_11_CONTRAST_FLOOR = 7;
245
- var INK_30_GROUND = {
242
+ var PENCIL_9_CONTRAST = 4.5;
243
+ var PEN_10_CONTRAST = 4.5;
244
+ var PEN_11_CONTRAST_FLOOR = 7;
245
+ var PEN_70_GROUND = {
246
246
  wcag: {
247
247
  light: { L: 0.3334966864733433, C: 0.07399966383476321, H: 104.26776675496032 },
248
248
  dark: { L: 0.907688606079988, C: 0.045, H: 293.66431934911327 }
@@ -318,7 +318,7 @@ var NEUTRAL_TINT_POINTS = {
318
318
  [0.95, 0.895],
319
319
  // paper-3 ─┘
320
320
  [0.924, 1],
321
- // wash-4 ─┐ peak: the mid-wash band, the neutral's most-branded moment
321
+ // wash-4 ─┐ peak: the mid-highlighter band, the neutral's most-branded moment
322
322
  [0.892, 1],
323
323
  // wash-5 ─┘
324
324
  [0.852, 0.842],
@@ -326,7 +326,7 @@ var NEUTRAL_TINT_POINTS = {
326
326
  [0.801, 0.653],
327
327
  // wash-7 │
328
328
  [0.738, 0.505],
329
- // highlight-8
329
+ // crayon-26
330
330
  [0.6, 0.4],
331
331
  // knee (ex-scaffold) the drop-off: hue fades as you climb
332
332
  [0.56, 0.347],
@@ -352,7 +352,7 @@ var NEUTRAL_TINT_POINTS = {
352
352
  [0.42, 0.653],
353
353
  // wash-7
354
354
  [0.55, 0.505],
355
- // highlight-8
355
+ // crayon-26
356
356
  [0.6, 0.4],
357
357
  // knee (ex-scaffold)
358
358
  [0.64, 0.347],
@@ -721,26 +721,136 @@ function perceptualDarkC(L, H2, nativeC, keep = KEEP_DARK, gamut = MASTER_GAMUT)
721
721
  return solveCForApparent(L, H2, grayApparentL(L, gamut) + keep * meanBoost(L, nativeC, gamut), gamut);
722
722
  }
723
723
 
724
+ // src/engine/tokenNames.ts
725
+ var SHARED_NAMES = {
726
+ 1: "paper-1",
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 highlighter-collision machinery
731
+ // (collision.ts HIGHLIGHTER_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
+ 3: "paper-5",
737
+ 4: "highlighter-8",
738
+ 5: "highlighter-11",
739
+ 6: "highlighter-15",
740
+ 7: "highlighter-20",
741
+ 8: "crayon-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.
751
+ 9: "pencil-47",
752
+ 10: "pen-58",
753
+ 11: "pen-70"
754
+ };
755
+ var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
756
+ function stopTokenName(stop) {
757
+ const name = SHARED_NAMES[stop];
758
+ if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
759
+ return name;
760
+ }
761
+ var PAPER_0 = "paper-0";
762
+ var PEN_100 = "pen-100";
763
+ var STAMP_FILL = "stamp-fill";
764
+ var STAMP_FILL_HOVER = "stamp-fill-hover";
765
+ var STAMP_FILL_PRESSED = "stamp-fill-pressed";
766
+ var STAMP_EDGE = "stamp-edge";
767
+ var STAMP_ON = "stamp-on";
768
+ var STAMP_STATE_LEAVES = {
769
+ [STAMP_FILL]: "stamp/fill",
770
+ [STAMP_FILL_HOVER]: "stamp/fill-hover",
771
+ [STAMP_FILL_PRESSED]: "stamp/fill-pressed",
772
+ [STAMP_EDGE]: "stamp/edge",
773
+ [STAMP_ON]: "stamp/on"
774
+ };
775
+ var STAMP_LEAF = {
776
+ FILL: STAMP_STATE_LEAVES[STAMP_FILL],
777
+ FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
778
+ FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
779
+ EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
780
+ ON: STAMP_STATE_LEAVES[STAMP_ON]
781
+ };
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
+ SCRIM: "system/alpha/abs-black-060",
794
+ INK: "system/alpha/ink",
795
+ // the DIRECTIONAL ladders (owner 2026-08-31, renaming the 2026-08-18 bare rungs
796
+ // and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
797
+ // (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
798
+ // INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
799
+ // a "highlighter": that word belongs to the tinted band stops.) The reversal lives in
800
+ // the engine: a consumer on an inverted ground names one token and never picks a
801
+ // pole. Colored fills never ride these; their states stay engine-solved stops.
802
+ // Rungs are two-digit percent, harmonizing with shadow-04/08/12.
803
+ AWAY_FROM_BG_06: "system/alpha/away-from-bg/06",
804
+ AWAY_FROM_BG_08: "system/alpha/away-from-bg/08",
805
+ AWAY_FROM_BG_16: "system/alpha/away-from-bg/16",
806
+ TOWARD_BG_06: "system/alpha/toward-bg/06",
807
+ TOWARD_BG_08: "system/alpha/toward-bg/08",
808
+ TOWARD_BG_16: "system/alpha/toward-bg/16",
809
+ SHADOW_04: "system/alpha/shadow-04",
810
+ SHADOW_08: "system/alpha/shadow-08",
811
+ SHADOW_12: "system/alpha/shadow-12"
812
+ }
813
+ };
814
+ var SURFACE_PLANE_LAW = {
815
+ [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_0 },
816
+ [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
817
+ [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
818
+ [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_0, dark: SHARED_NAMES[3] }
819
+ };
820
+ var TOKEN_ORDER = [
821
+ ...Object.values(SHARED_NAMES),
822
+ STAMP_FILL,
823
+ STAMP_FILL_HOVER,
824
+ STAMP_FILL_PRESSED,
825
+ STAMP_ON,
826
+ "identity"
827
+ ];
828
+ function tokenOrder(name) {
829
+ const i = TOKEN_ORDER.indexOf(name);
830
+ return i === -1 ? TOKEN_ORDER.length : i;
831
+ }
832
+
724
833
  // src/engine/requirements/spec.ts
725
- var groupOf = (stop) => stop <= 2 ? "paper" : stop <= 7 ? "wash" : stop === 8 ? "highlight" : "ink";
834
+ var textLane = (g2) => g2 === "pencil" || g2 === "pen" || g2 === "lead" || g2 === "ink";
835
+ var groupOf = (stop) => (stop === 0 ? PAPER_0 : stopTokenName(stop)).split("-")[0];
726
836
  var PAPER0_DARK_ROOT_L = 0.16;
727
837
  var PL_LADDER = { hue: "warm-drift", L: "perceptual", chroma: "ladder" };
728
838
  var PL_TEXT = { hue: "warm-drift", L: "perceptual", chroma: "brand" };
729
839
  var P_LIFT = { hue: "warm-torsion", L: "perceptual-lift", chroma: "ladder" };
730
840
  var P_FIXED = { hue: "warm-torsion", L: "fixed", chroma: "ladder" };
731
841
  var P_TEXT = { hue: "warm-torsion", L: "perceptual", chroma: "brand" };
732
- var S8 = { metric: "wcag", against: "paper-95", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
733
- var T9 = { metric: "wcag", against: "paper-97", target: INK_9_CONTRAST, level: "AA" };
734
- var T10 = { metric: "wcag", against: "wash-80", target: INK_10_CONTRAST, level: "AA" };
735
- var T11 = { metric: "wcag", against: "paper-97", target: INK_11_CONTRAST_FLOOR, level: "AAA" };
842
+ var S8 = { metric: "wcag", against: "paper-5", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
843
+ var T9 = { metric: "wcag", against: "paper-3", target: PENCIL_9_CONTRAST, level: "AA" };
844
+ var T10 = { metric: "wcag", against: "highlighter-20", target: PEN_10_CONTRAST, level: "AA" };
845
+ var T11 = { metric: "wcag", against: "paper-3", target: PEN_11_CONTRAST_FLOOR, level: "AAA" };
736
846
  var ONS = { onFill: { metric: "apca-pole", enforce: true, ratioFloor: 4.5, coEnforceLc: 65 } };
737
847
  var LIGHT = {
738
848
  stops: [
739
- // paper-100 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
740
- { stop: 0, rootL: 1, group: "paper", produce: { hue: "warm-drift", L: "fixed", chroma: "ladder" }, satFraction: SCALE_C_LIGHT[0].sat, baseC: SCALE_C_LIGHT[0].base },
741
- // paper/wash/wax-74: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
849
+ // paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
850
+ { stop: 0, rootL: 1, group: groupOf(0), produce: { hue: "warm-drift", L: "fixed", chroma: "ladder" }, satFraction: SCALE_C_LIGHT[0].sat, baseC: SCALE_C_LIGHT[0].base },
851
+ // paper/highlighter/crayon-26: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
742
852
  // falls out of the shape (see above) — no min-separation require. Only stop 8 carries a require: the WCAG
743
- // 3:1 vs the resolved paper-97 (paper-2) (re-solves automatically since it references paper-97 (paper-2)).
853
+ // 3:1 vs the resolved paper-3 (paper-2) (re-solves automatically since it references paper-3 (paper-2)).
744
854
  ...[1, 2, 3, 4, 5, 6, 7, 8].map((stop) => ({
745
855
  stop,
746
856
  rootL: ROOT_L_LIGHT[stop],
@@ -750,12 +860,12 @@ var LIGHT = {
750
860
  baseC: SCALE_C_LIGHT[stop].base,
751
861
  require: stop === 8 ? S8 : void 0
752
862
  })),
753
- // ink text: perceptual + contrast-required. lead-53 (ink-9) is ALSO the emphasis fill (the
754
- // highlight-9 collapse, owner 2026-07-29). ink-42 (ink-10) is the between text stop (C49) —
863
+ // pen text: perceptual + contrast-required. pencil-47 (ink-9) is ALSO the emphasis fill (the
864
+ // highlight-9 collapse, owner 2026-07-29). pen-58 (ink-10) is the between text stop (C49) —
755
865
  // a normal stop like its neighbors; the three together are the text-register cta.
756
- { stop: 9, rootL: ROOT_L_LIGHT[9], group: "ink", produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[9].inkMult, inkMaxC: SCALE_C_LIGHT[9].inkMaxC, chromaFloor: SCALE_C_LIGHT[9].chromaFloor, require: T9 },
757
- { stop: 10, rootL: ROOT_L_LIGHT[10], group: "ink", produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[10].inkMult, inkMaxC: SCALE_C_LIGHT[10].inkMaxC, chromaFloor: SCALE_C_LIGHT[10].chromaFloor, require: T10 },
758
- { stop: 11, rootL: ROOT_L_LIGHT[11], group: "ink", produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[11].inkMult, inkMaxC: SCALE_C_LIGHT[11].inkMaxC, chromaFloor: SCALE_C_LIGHT[11].chromaFloor, require: T11 }
866
+ { stop: 9, rootL: ROOT_L_LIGHT[9], group: groupOf(9), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[9].textMult, textMaxC: SCALE_C_LIGHT[9].textMaxC, chromaFloor: SCALE_C_LIGHT[9].chromaFloor, require: T9 },
867
+ { stop: 10, rootL: ROOT_L_LIGHT[10], group: groupOf(10), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[10].textMult, textMaxC: SCALE_C_LIGHT[10].textMaxC, chromaFloor: SCALE_C_LIGHT[10].chromaFloor, require: T10 },
868
+ { stop: 11, rootL: ROOT_L_LIGHT[11], group: groupOf(11), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[11].textMult, textMaxC: SCALE_C_LIGHT[11].textMaxC, chromaFloor: SCALE_C_LIGHT[11].chromaFloor, require: T11 }
759
869
  ],
760
870
  roles: [
761
871
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: 0, chromaMult: 1 },
@@ -766,10 +876,10 @@ var LIGHT = {
766
876
  };
767
877
  var DARK = {
768
878
  stops: [
769
- // paper-100 (paper-0 pre-Stage-B): the resolved ladder extreme — one seam BELOW paper-99 (paper-1), deep and brand-tinted, never the
879
+ // paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — one seam BELOW paper-1 (paper-1), deep and brand-tinted, never the
770
880
  // absolute void (the old hard-coded #000000 was "too much"). Lift applies like the rest of the scale.
771
- { stop: 0, rootL: PAPER0_DARK_ROOT_L, group: "paper", produce: P_LIFT, satFraction: SCALE_C_DARK[0].sat },
772
- // paper/wash 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
881
+ { stop: 0, rootL: PAPER0_DARK_ROOT_L, group: groupOf(0), produce: P_LIFT, satFraction: SCALE_C_DARK[0].sat },
882
+ // paper/highlighter 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
773
883
  // 3:1 non-text require DECLARED (the Stage-5 flip, owner-approved) — the blue-recede failure is prevented
774
884
  // BY RULE, not by patch. The require now genuinely PLACES the stop rather than catching a few low-luminance
775
885
  // hues: on the delta-carry path it solves from the sentinel every time, and the old claim that "most hues
@@ -783,11 +893,11 @@ var DARK = {
783
893
  satFraction: SCALE_C_DARK[stop].sat,
784
894
  require: stop === 8 ? S8 : void 0
785
895
  })),
786
- // ink text: perceptual + the contrast requires DECLARED in dark too (Stage-5 flip): the scaffold already
896
+ // pen text: perceptual + the contrast requires DECLARED in dark too (Stage-5 flip): the scaffold already
787
897
  // clears them for every hue (the gate proves it), so values don't move — but the guarantee is now a rule.
788
- { stop: 9, rootL: ROOT_L_DARK[9], group: "ink", produce: P_TEXT, chromaMult: SCALE_C_DARK[9].inkMult, inkMaxC: SCALE_C_DARK[9].inkMaxC, chromaFloor: SCALE_C_DARK[9].chromaFloor, require: T9 },
789
- { stop: 10, rootL: ROOT_L_DARK[10], group: "ink", produce: P_TEXT, chromaMult: SCALE_C_DARK[10].inkMult, inkMaxC: SCALE_C_DARK[10].inkMaxC, chromaFloor: SCALE_C_DARK[10].chromaFloor, require: T10 },
790
- { stop: 11, rootL: ROOT_L_DARK[11], group: "ink", produce: P_TEXT, chromaMult: SCALE_C_DARK[11].inkMult, inkMaxC: SCALE_C_DARK[11].inkMaxC, chromaFloor: SCALE_C_DARK[11].chromaFloor, require: T11 }
898
+ { stop: 9, rootL: ROOT_L_DARK[9], group: groupOf(9), produce: P_TEXT, chromaMult: SCALE_C_DARK[9].textMult, textMaxC: SCALE_C_DARK[9].textMaxC, chromaFloor: SCALE_C_DARK[9].chromaFloor, require: T9 },
899
+ { stop: 10, rootL: ROOT_L_DARK[10], group: groupOf(10), produce: P_TEXT, chromaMult: SCALE_C_DARK[10].textMult, textMaxC: SCALE_C_DARK[10].textMaxC, chromaFloor: SCALE_C_DARK[10].chromaFloor, require: T10 },
900
+ { stop: 11, rootL: ROOT_L_DARK[11], group: groupOf(11), produce: P_TEXT, chromaMult: SCALE_C_DARK[11].textMult, textMaxC: SCALE_C_DARK[11].textMaxC, chromaFloor: SCALE_C_DARK[11].chromaFloor, require: T11 }
791
901
  ],
792
902
  roles: [
793
903
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: DARK_CTA_MIN_L, chromaMult: 1 },
@@ -798,7 +908,7 @@ var DARK = {
798
908
  };
799
909
  var MODE_SPECS = { light: LIGHT, dark: DARK };
800
910
 
801
- // ../../../Users/emilygerrity/okchroma/node_modules/helmlab/dist/index.js
911
+ // node_modules/helmlab/dist/index.js
802
912
  function o0(n) {
803
913
  let t = new Float64Array(9);
804
914
  for (let e = 0; e < 3; e++) for (let s = 0; s < 3; s++) t[e * 3 + s] = n[e][s];
@@ -1714,7 +1824,7 @@ function p2Diff(a, b) {
1714
1824
  var DEFAULT_APCA_LC_MAP = { 3: 30, 4.5: 75, 6.5: 85, 7: 90 };
1715
1825
  function toApca(req, lcMap) {
1716
1826
  if (req.metric !== "wcag") return req;
1717
- if (req.against === "wash-80") return { metric: "apca", against: "paper-97", targetLc: 85 };
1827
+ if (req.against === "highlighter-20") return { metric: "apca", against: "paper-3", targetLc: 85 };
1718
1828
  const targetLc = lcMap[req.target];
1719
1829
  if (targetLc === void 0) throw new Error(`apca profile: no Lc mapping for wcag target ${req.target}`);
1720
1830
  return { metric: "apca", against: req.against, targetLc };
@@ -1728,7 +1838,7 @@ function withProfile(spec, profile, lcMap = DEFAULT_APCA_LC_MAP) {
1728
1838
  // (The stop-9 CARVE-OUT is gone with the stop, owner 2026-07-29. It existed because
1729
1839
  // highlight-9's require was a fill-vs-its-own-plane bar with no honest slot in a map
1730
1840
  // of TEXT bars — 3 → 30 non-text, 4.5 → 75 body, 7 → 90 — so the apca lane kept its
1731
- // own hl-9 placement instead. Today's stop 9 is an INK stop: 4.5 is a text bar, the
1841
+ // own hl-9 placement instead. Today's stop 9 is a PEN stop: 4.5 is a text bar, the
1732
1842
  // 75 slot is its correct translation, and it maps like every other require.)
1733
1843
  stops: spec.stops.map((s) => s.require ? { ...s, require: toApca(s.require, lcMap) } : s),
1734
1844
  ons: {
@@ -1890,16 +2000,16 @@ function separationClampLight(ctx, placed, chromaAt, ref, target) {
1890
2000
  return { L: pass, C: ceilingAt(pass), H: ctx.lightHueAt(pass) };
1891
2001
  }
1892
2002
  function placeLightText(ctx, rootL, cMult, maxLFor, deepen, maxC = Infinity) {
1893
- const inkC = Math.min(cMult * ctx.brandC, maxC);
1894
- const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL, inkC, ctx.lightHueAt(rootL)), ctx.lightHueAt(rootL));
2003
+ const textC = Math.min(cMult * ctx.brandC, maxC);
2004
+ const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL, textC, ctx.lightHueAt(rootL)), ctx.lightHueAt(rootL));
1895
2005
  let H2 = ctx.lightHueAt(anchorL);
1896
- let C2 = clampChromaToGamut(anchorL, inkC, H2);
2006
+ let C2 = clampChromaToGamut(anchorL, textC, H2);
1897
2007
  let L = Math.min(anchorL, maxLFor(C2, H2));
1898
2008
  H2 = ctx.lightHueAt(L);
1899
- C2 = clampChromaToGamut(L, inkC, H2);
2009
+ C2 = clampChromaToGamut(L, textC, H2);
1900
2010
  L = Math.min(anchorL, maxLFor(C2, H2));
1901
2011
  L = Math.min(L - deepen, maxLFor(C2, ctx.lightHueAt(L - deepen)));
1902
- return { L, C: ctx.cAt("light", L, inkC), H: ctx.lightHueAt(L) };
2012
+ return { L, C: ctx.cAt("light", L, textC), H: ctx.lightHueAt(L) };
1903
2013
  }
1904
2014
  function onFillIsWhiteLight(ctx, enforce, ratioFloor) {
1905
2015
  const fill9ApcaY = apcaYAt(ctx.scaleL, clampChromaToGamut(ctx.scaleL, ctx.cAt("light", ctx.scaleL, ctx.brandC), ctx.brandH), ctx.brandH);
@@ -1913,7 +2023,7 @@ function buildDarkContext(ctx, specFloorL = DARK_CTA_MIN_L) {
1913
2023
  return { dark9L, darkC9, darkHueAtL };
1914
2024
  }
1915
2025
  var darkScaleChromaAt = (ctx, dctx, stopIndex, multiplier) => (L) => ctx.cAt("dark", L, ctx.opts?.darkChromaCurve ? ctx.opts.darkChromaCurve(L, dctx.darkHueAtL(L), ctx.brandC, dctx.darkC9) : applyChromaFloor(ctx.subtleC, multiplier, chromaFloorBase(stopIndex), ctx.darkFloorStrength));
1916
- var darkInkChromaAt = (ctx, dctx, floorBase, multiplier, maxC = Infinity) => (L) => ctx.cAt("dark", L, Math.min(applyChromaFloor(ctx.brandC, multiplier, floorBase, ctx.darkFloorStrength), maxC));
2026
+ var darkTextChromaAt = (ctx, dctx, floorBase, multiplier, maxC = Infinity) => (L) => ctx.cAt("dark", L, Math.min(applyChromaFloor(ctx.brandC, multiplier, floorBase, ctx.darkFloorStrength), maxC));
1917
2027
  function placeDark(dctx, rootL, chromaAt, lift = false) {
1918
2028
  let L = perceptualRungL(rootL, chromaAt(rootL), dctx.darkHueAtL(rootL));
1919
2029
  if (lift) L = Math.max(L, rootL);
@@ -2174,7 +2284,7 @@ function resolveRamp(hex, mode, spec, opts) {
2174
2284
  const seed = { hex, ...o };
2175
2285
  const ctx = buildContext(hex, opts);
2176
2286
  spec ?? (spec = MODE_SPECS[mode]);
2177
- if (ctx.opts?.inkGround) {
2287
+ if (ctx.opts?.textGround) {
2178
2288
  spec = {
2179
2289
  ...spec,
2180
2290
  stops: spec.stops.map((s) => s.stop === 10 && s.require?.metric === "wcag" ? { ...s, require: { ...s.require, target: 6.5 } } : s)
@@ -2196,12 +2306,12 @@ function resolveRamp(hex, mode, spec, opts) {
2196
2306
  if (!ref) throw new Error(`stop ${forWhom}: require against stop ${stopNum} but it is not resolved yet`);
2197
2307
  return ref;
2198
2308
  };
2199
- const AGAINST_STOP = { "paper-99": 1, "paper-97": 2, "paper-95": 3, "wash-80": 7 };
2309
+ const AGAINST_STOP = { "paper-1": 1, "paper-3": 2, "paper-5": 3, "highlighter-20": 7 };
2200
2310
  const declaredAnchor = (req) => req.metric === "min-separation" ? 1 : AGAINST_STOP[req.against] ?? 2;
2201
2311
  const wcagAnchorStop = (req, stop) => stop >= 9 && declaredAnchor(req) <= 3 ? 3 : declaredAnchor(req);
2202
- const inkGround = ctx.opts?.inkGround?.[mode];
2203
- const groundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(wcagAnchorStop(req, stop), stop);
2204
- const apcaGroundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(declaredAnchor(req), stop);
2312
+ const textGround = ctx.opts?.textGround?.[mode];
2313
+ const groundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(wcagAnchorStop(req, stop), stop);
2314
+ const apcaGroundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(declaredAnchor(req), stop);
2205
2315
  const NEUTRAL_P3_WORST_SHIP_Y = { light: 0.845015, dark: 0.014247 };
2206
2316
  const NEUTRAL_W80_WORST_SHIP_Y = { light: 0.506433, dark: 0.074262 };
2207
2317
  const maxLForOf = (req, forWhom, withMargin) => {
@@ -2219,14 +2329,14 @@ function resolveRamp(hex, mode, spec, opts) {
2219
2329
  }
2220
2330
  throw new Error(`stop ${forWhom}: ${req.metric} is not a contrast require`);
2221
2331
  };
2222
- const INK_DEEPEN = 0;
2332
+ const TEXT_DEEPEN = 0;
2223
2333
  for (const sp of spec.stops) {
2224
2334
  let placed;
2225
2335
  let clamped = false, unresolvable;
2226
2336
  if (mode === "light") {
2227
- if (sp.group === "ink") {
2228
- if (!sp.require) throw new Error(`light ink stop ${sp.stop} must declare a contrast require`);
2229
- placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), INK_DEEPEN, sp.inkMaxC);
2337
+ if (textLane(sp.group)) {
2338
+ if (!sp.require) throw new Error(`light pen stop ${sp.stop} must declare a contrast require`);
2339
+ placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), TEXT_DEEPEN, sp.textMaxC);
2230
2340
  clamped = true;
2231
2341
  } else if (sp.produce.L === "fixed") {
2232
2342
  const chromaAt = lightScaleChromaAt(ctx, sp.baseC ?? 0, sp.satFraction ?? 1);
@@ -2238,7 +2348,7 @@ function resolveRamp(hex, mode, spec, opts) {
2238
2348
  placed = placeLightScale(ctx, sp.rootL, chromaAt, contrastReq ? maxLForOf(contrastReq, sp.stop, true) : void 0, targetChromaAt);
2239
2349
  clamped = !!contrastReq;
2240
2350
  if (sp.require?.metric === "min-separation") {
2241
- const refStop = sp.require.against === "paper-99" ? 1 : sp.stop - 1;
2351
+ const refStop = sp.require.against === "paper-1" ? 1 : sp.stop - 1;
2242
2352
  const ref = stops.find((s) => s.stop === refStop);
2243
2353
  if (!ref) throw new Error(`stop ${sp.stop}: min-separation against stop ${refStop} but it is not resolved yet`);
2244
2354
  const before = placed.L;
@@ -2248,8 +2358,8 @@ function resolveRamp(hex, mode, spec, opts) {
2248
2358
  }
2249
2359
  } else {
2250
2360
  const d = dctx;
2251
- const inkTwin = sp.group === "ink" && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2252
- const chromaAt = sp.group === "ink" ? inkTwin ? ((_L) => inkTwin.C) : darkInkChromaAt(ctx, d, sp.chromaFloor ?? chromaFloorBase(sp.stop), sp.chromaMult ?? 1, sp.inkMaxC) : darkScaleChromaAt(ctx, d, Math.max(0, sp.stop - 1), sp.satFraction ?? 1);
2361
+ const textTwin = textLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2362
+ const chromaAt = textLane(sp.group) ? textTwin ? ((_L) => textTwin.C) : darkTextChromaAt(ctx, d, sp.chromaFloor ?? chromaFloorBase(sp.stop), sp.chromaMult ?? 1, sp.textMaxC) : darkScaleChromaAt(ctx, d, Math.max(0, sp.stop - 1), sp.satFraction ?? 1);
2253
2363
  let spineH = null;
2254
2364
  const dl = ctx.opts?.deltaLightStops;
2255
2365
  const ls = dl && sp.stop >= 1 && sp.stop <= 8 ? dl.find((s) => s.stop === sp.stop) : void 0;
@@ -2269,7 +2379,7 @@ function resolveRamp(hex, mode, spec, opts) {
2269
2379
  }
2270
2380
  if (ctx.opts.deltaHKPlace) L = perceptualRungL(sp.rootL, ls.C, ls.H);
2271
2381
  if (ctx.opts.deltaLiftFloor) L = Math.max(L, sp.rootL);
2272
- if (ctx.opts.deltaChromaEq && sp.group !== "ink") C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2382
+ if (ctx.opts.deltaChromaEq && !textLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2273
2383
  placed = { L, C: C2, H: spineH ?? ls.H };
2274
2384
  } else if (sp.produce.L === "fixed") {
2275
2385
  placed = ls ? placeDarkDelta(d, sp.rootL, chromaAt, ls) : { L: sp.rootL, C: chromaAt(sp.rootL), H: d.darkHueAtL(sp.rootL) };
@@ -2305,7 +2415,7 @@ function resolveRamp(hex, mode, spec, opts) {
2305
2415
  if (sp.require?.metric === "wcag") {
2306
2416
  const anchor = groundOf(sp.require, sp.stop);
2307
2417
  const anchorShipY = shippedY(anchor.L, anchor.C, anchor.H);
2308
- const paperBound = sp.stop >= 8 && !(inkGround && sp.stop >= 9);
2418
+ const paperBound = sp.stop >= 8 && !(textGround && sp.stop >= 9);
2309
2419
  const crossBoundY = wcagAnchorStop(sp.require, sp.stop) === 7 ? NEUTRAL_W80_WORST_SHIP_Y[mode] : NEUTRAL_P3_WORST_SHIP_Y[mode];
2310
2420
  const shipRatio = (L) => {
2311
2421
  const y2 = shippedY(L, placed.C, placed.H);
@@ -2333,7 +2443,7 @@ function resolveRamp(hex, mode, spec, opts) {
2333
2443
  const got = Math.abs(apcaLc(apcaYAt(placed.L, clampChromaToGamut(placed.L, placed.C, placed.H), placed.H), refA));
2334
2444
  if (got < sp.require.targetLc - APCA_TOL_LC) unresolvable = `stop ${sp.stop}: |Lc| ${got.toFixed(1)} < required ${sp.require.targetLc}`;
2335
2445
  } else if (sp.require?.metric === "min-separation") {
2336
- const ref = stops.find((s) => s.stop === (sp.require.against === "paper-99" ? 1 : sp.stop - 1));
2446
+ const ref = stops.find((s) => s.stop === (sp.require.against === "paper-1" ? 1 : sp.stop - 1));
2337
2447
  const rad2 = (h) => h * Math.PI / 180;
2338
2448
  const gC = clampChromaToGamut(placed.L, placed.C, placed.H);
2339
2449
  const got = Math.sqrt((placed.L - ref.L) ** 2 + (gC * Math.cos(rad2(placed.H)) - ref.C * Math.cos(rad2(ref.H))) ** 2 + (gC * Math.sin(rad2(placed.H)) - ref.C * Math.sin(rad2(ref.H))) ** 2);
@@ -2489,7 +2599,7 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2489
2599
  const dark = darkRamp.stops.filter((s) => s.stop >= 1).map(toStop).sort((a, b) => a.stop - b.stop);
2490
2600
  const p0Light = lightRamp.stops.find((s) => s.stop === 0);
2491
2601
  const p0Dark = darkRamp.stops.find((s) => s.stop === 0);
2492
- const inkPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2602
+ const penPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2493
2603
  const cta = makeStop(9, lightRamp.roles.cta.L, lightRamp.roles.cta.C, lightRamp.roles.cta.H);
2494
2604
  const ctaDark = makeStop(9, darkRamp.roles.cta.L, darkRamp.roles.cta.C, darkRamp.roles.cta.H);
2495
2605
  const roleStop = (r, stop) => makeStop(stop, r.L, r.C, r.H);
@@ -2513,8 +2623,8 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2513
2623
  identityHex: hex.toUpperCase(),
2514
2624
  paper0: p0Light ? makeStop(0, p0Light.L, p0Light.C, p0Light.H) : void 0,
2515
2625
  paper0Dark: p0Dark ? makeStop(0, p0Dark.L, p0Dark.C, p0Dark.H) : void 0,
2516
- ink0: inkPole(light, 0),
2517
- ink0Dark: inkPole(dark, 1)
2626
+ pen100: penPole(light, 0),
2627
+ pen100Dark: penPole(dark, 1)
2518
2628
  };
2519
2629
  }
2520
2630
  function generateNeutralScale(brandH, level = "default", contrastProfile) {
@@ -2586,8 +2696,7 @@ function generateSubtleSecondary(hex, opts) {
2586
2696
 
2587
2697
  // src/engine/tokenDescriptions.ts
2588
2698
  var AA_LARGE = "AA large text and UI elements";
2589
- var AA_BODY = "AA standard body text & Level AAA large text";
2590
- var AAA_BODY = "AAA standard body text";
2699
+ var AA_BODY = "AA standard body text";
2591
2700
  var FAMILY = {
2592
2701
  neutral: "neutral",
2593
2702
  brandPrimary: "brand",
@@ -2623,7 +2732,7 @@ var COLOR_WORD = {
2623
2732
  [FAMILY.info]: " (blue)"
2624
2733
  };
2625
2734
  var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2626
- var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2735
+ var HIGHLIGHTER = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2627
2736
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2628
2737
  var OVERLAY = {
2629
2738
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2631,25 +2740,25 @@ var OVERLAY = {
2631
2740
  collides: true
2632
2741
  };
2633
2742
  var SCALE = {
2634
- "paper-99": PAPER,
2743
+ "paper-1": PAPER,
2635
2744
  "paper-99-overlay": OVERLAY,
2636
- "paper-97": PAPER,
2745
+ "paper-3": PAPER,
2637
2746
  "paper-97-overlay": OVERLAY,
2638
- "paper-95": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2747
+ "paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2639
2748
  "paper-95-overlay": OVERLAY,
2640
- "wash-92": WASH,
2641
- "wash-89": WASH,
2642
- "wash-85": WASH,
2643
- "wash-80": WASH,
2749
+ "highlighter-8": HIGHLIGHTER,
2750
+ "highlighter-11": HIGHLIGHTER,
2751
+ "highlighter-15": HIGHLIGHTER,
2752
+ "highlighter-20": HIGHLIGHTER,
2644
2753
  // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2645
- // landing on the wax rows is worth its ic-ON-s noise — the one surviving body carrier)
2646
- "wax-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2647
- "lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2648
- "ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2754
+ // landing on the crayon rows is worth its ic-ON-s noise — the one surviving body carrier)
2755
+ "crayon-26": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2756
+ "pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2757
+ "pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2649
2758
  // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2650
2759
  // words, and a body carrying either floods that token's picker search — the C50 law;
2651
2760
  // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2652
- "ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2761
+ "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2653
2762
  // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2654
2763
  // these bodies on purpose: it stopped being a token label, so it floods nothing,
2655
2764
  // and a designer's "cta" query still lands on these rows.
@@ -2678,7 +2787,7 @@ var LINK_INVERSE = (state, contrast) => ({
2678
2787
  var SYSTEM = {
2679
2788
  "system/abs-black": ABS,
2680
2789
  "system/abs-white": ABS,
2681
- // (the community system/ink-0 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2790
+ // (the community system/pen-100 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2682
2791
  // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2683
2792
  // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2684
2793
  "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
@@ -2694,7 +2803,7 @@ var SYSTEM = {
2694
2803
  "system/alpha/away-from-bg/08": OFFSET,
2695
2804
  "system/alpha/away-from-bg/16": OFFSET,
2696
2805
  // the inverse ladder: the offset rungs with the pole flipped per mode, for
2697
- // washes and edges over inverted backgrounds ("interaction" avoided in the
2806
+ // highlighters and edges over inverted backgrounds ("interaction" avoided in the
2698
2807
  // bodies: acti-ON would feed the "on" flood)
2699
2808
  "system/alpha/toward-bg/06": OFFSET_INVERSE,
2700
2809
  "system/alpha/toward-bg/08": OFFSET_INVERSE,
@@ -2711,10 +2820,10 @@ var SYSTEM = {
2711
2820
  "system/surface/high": PLANE("topmost plane \u2014 modals, dialogs"),
2712
2821
  "system/link/default/enabled": LINK("", AA_BODY),
2713
2822
  "system/link/default/hover": LINK(" pointer-over", AA_BODY),
2714
- "system/link/default/pressed": LINK(" pressed", AAA_BODY),
2823
+ "system/link/default/pressed": LINK(" pressed", AA_BODY),
2715
2824
  "system/link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2716
2825
  "system/link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2717
- "system/link/inverse/pressed": LINK_INVERSE(" pressed", AAA_BODY)
2826
+ "system/link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2718
2827
  };
2719
2828
  var PREFIXES = [
2720
2829
  // community spellings FIRST: the 2026-08-21 family rename made the ext word a
@@ -2761,15 +2870,15 @@ function checkCollision(brand, signalScale, signalDef, mode) {
2761
2870
  collides: hueGate && deltaE <= threshold
2762
2871
  };
2763
2872
  }
2764
- var HUE_COLLISION_WASH_DEG = 15;
2873
+ var HUE_COLLISION_HIGHLIGHTER_DEG = 15;
2765
2874
  var HUE_COLLISION_MIN_V = 0.5;
2766
2875
  var SECONDARY_NOTE_MIN_V = HUE_NOISE_C / VIVID_C;
2767
- var WASH_STOPS = [3, 4, 5, 6, 7];
2768
- function washProximity(brand, sig, mode) {
2876
+ var HIGHLIGHTER_STOPS = [3, 4, 5, 6, 7];
2877
+ function highlighterProximity(brand, sig, mode) {
2769
2878
  const b = mode === "light" ? brand.light : brand.dark;
2770
2879
  const s = mode === "light" ? sig.light : sig.dark;
2771
2880
  let dH = Infinity, dE = Infinity;
2772
- for (const k of WASH_STOPS) {
2881
+ for (const k of HIGHLIGHTER_STOPS) {
2773
2882
  const bs = b.find((x2) => x2.stop === k), ss = s.find((x2) => x2.stop === k);
2774
2883
  if (!bs || !ss) continue;
2775
2884
  dH = Math.min(dH, hueDistance(bs.H, ss.H));
@@ -2778,15 +2887,15 @@ function washProximity(brand, sig, mode) {
2778
2887
  return { dH, dE };
2779
2888
  }
2780
2889
  function checkHueCollision(brand, signalScale, signalDef, opts) {
2781
- const l = washProximity(brand, signalScale, "light");
2782
- const d = washProximity(brand, signalScale, "dark");
2890
+ const l = highlighterProximity(brand, signalScale, "light");
2891
+ const d = highlighterProximity(brand, signalScale, "dark");
2783
2892
  const vividness = Math.min(1, brand.brandC / VIVID_C);
2784
2893
  return {
2785
2894
  signal: signalDef.name,
2786
- dHueWash: { light: l.dH, dark: d.dH },
2787
- washDeltaE: { light: l.dE, dark: d.dE },
2895
+ dHueHighlighter: { light: l.dH, dark: d.dH },
2896
+ highlighterDeltaE: { light: l.dE, dark: d.dE },
2788
2897
  vividness,
2789
- collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_WASH_DEG
2898
+ collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_HIGHLIGHTER_DEG
2790
2899
  };
2791
2900
  }
2792
2901
  var YELLOW_SPLIT_H = 96;
@@ -2979,7 +3088,7 @@ function resolveBrand(hex, name, opts) {
2979
3088
  // (buildSignalScales/signalShift) — neutral alone stays outside; exact has enforce off.
2980
3089
  apcaClearance: opts?.apcaClearance ?? true,
2981
3090
  darkCtaFlatApp: opts?.darkCtaFlatApp,
2982
- inkGround: opts?.inkGround
3091
+ textGround: opts?.textGround
2983
3092
  };
2984
3093
  let scale = generateScale(hex, name, void 0, floor);
2985
3094
  let pending = [];
@@ -3062,7 +3171,7 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3062
3171
  const s = resolveBrand(linkHex, "link", { skipCollisionRules: true, contrastProfile }).scale;
3063
3172
  const at = (arr, n) => {
3064
3173
  const st = arr.find((x2) => x2.stop === n);
3065
- if (!st) throw new Error(`resolveLinkTrio: the link resolve has no ink stop ${n}`);
3174
+ if (!st) throw new Error(`resolveLinkTrio: the link resolve has no pen stop ${n}`);
3066
3175
  return st;
3067
3176
  };
3068
3177
  return {
@@ -3075,16 +3184,16 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3075
3184
  };
3076
3185
  }
3077
3186
  function resolveLinkInverseTrio(seedHex, contrastProfile) {
3078
- const ground = INK_30_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3187
+ const ground = PEN_70_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3079
3188
  const s = resolveBrand(seedHex, "link-inverse", {
3080
3189
  skipCollisionRules: true,
3081
3190
  contrastProfile,
3082
3191
  // the cross — ramp keyed, ground mode-swapped
3083
- inkGround: { light: ground.dark, dark: ground.light }
3192
+ textGround: { light: ground.dark, dark: ground.light }
3084
3193
  }).scale;
3085
3194
  const at = (arr, n) => {
3086
3195
  const st = arr.find((x2) => x2.stop === n);
3087
- if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no ink stop ${n}`);
3196
+ if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no pen stop ${n}`);
3088
3197
  return st;
3089
3198
  };
3090
3199
  return {
@@ -3098,12 +3207,12 @@ function resolveLinkInverseTrio(seedHex, contrastProfile) {
3098
3207
  };
3099
3208
  }
3100
3209
  function escapeCtaFamily(nScale, mode, contrastProfile) {
3101
- const strongInk = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3102
- if (!strongInk) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3103
- const mk = (stop, L) => makeStop(stop, L, strongInk.C, strongInk.H);
3104
- const cta = mk(9, strongInk.L);
3105
- const ctaHover = mk(10, stateFillL(strongInk.L, mode, 1));
3106
- const ctaPressed = mk(11, stateFillL(strongInk.L, mode, 2));
3210
+ const strongText = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3211
+ if (!strongText) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3212
+ const mk = (stop, L) => makeStop(stop, L, strongText.C, strongText.H);
3213
+ const cta = mk(9, strongText.L);
3214
+ const ctaHover = mk(10, stateFillL(strongText.L, mode, 1));
3215
+ const ctaPressed = mk(11, stateFillL(strongText.L, mode, 2));
3107
3216
  const onEnforce = contrastProfile !== "apca";
3108
3217
  const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
3109
3218
  const onFillIsWhite = onTextIsWhite(apcaY(cta.r, cta.g, cta.b), cta.L, cta.C, cta.H, onEnforce, onFloor);
@@ -3179,7 +3288,7 @@ function resolveTheme(input) {
3179
3288
  for (const def of SIGNALS2) {
3180
3289
  if (adoptedForSecondary.has(def.name)) continue;
3181
3290
  const h = checkHueCollision(scale2, effectiveOf(def.name), def, { minV: SECONDARY_NOTE_MIN_V });
3182
- if (h.collides) out.push(wording(def.name, Math.min(h.washDeltaE.light, h.washDeltaE.dark)));
3291
+ if (h.collides) out.push(wording(def.name, Math.min(h.highlighterDeltaE.light, h.highlighterDeltaE.dark)));
3183
3292
  }
3184
3293
  return out;
3185
3294
  };
@@ -3229,7 +3338,7 @@ function resolveTheme(input) {
3229
3338
  derived: true,
3230
3339
  notes: [
3231
3340
  `secondary derived from the brand color (default model, seed ${liftedHex})`,
3232
- ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3341
+ ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3233
3342
  ],
3234
3343
  distinctness: ctaDistinctness(primary.scale, scale2)
3235
3344
  },
@@ -3254,7 +3363,7 @@ function resolveTheme(input) {
3254
3363
  derived: false,
3255
3364
  notes: [
3256
3365
  `secondary keeps your color through the ramp; the cta is a tint of it (tint seed ${tintedHex})`,
3257
- ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3366
+ ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3258
3367
  ],
3259
3368
  distinctness: distinctness2
3260
3369
  },
@@ -3266,7 +3375,7 @@ function resolveTheme(input) {
3266
3375
  const scale = rSec.scale;
3267
3376
  const level = "standard";
3268
3377
  mergeSecondarySignals(scale, input.secondaryHex);
3269
- const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (wash \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3378
+ const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (highlighter \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3270
3379
  const distinctness = ctaDistinctness(primary.scale, scale);
3271
3380
  if (distinctness.close)
3272
3381
  notes.push(`secondary reads close to the primary (\u0394E ${Math.min(distinctness.light, distinctness.dark).toFixed(2)}) \u2014 consider a more distinct color`);
@@ -3279,113 +3388,6 @@ function resolveTheme(input) {
3279
3388
  };
3280
3389
  }
3281
3390
 
3282
- // src/engine/tokenNames.ts
3283
- var SHARED_NAMES = {
3284
- 1: "paper-99",
3285
- 2: "paper-97",
3286
- // stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
3287
- // PLANE in both themes (light sink / dark pop), so it belongs to the surface band.
3288
- // NAME ONLY — generation is index-keyed and unchanged; the internal wash machinery
3289
- // (reqtoken groupOf, collision WASH_STOPS) deliberately still spans stops 3–7.
3290
- // Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
3291
- // paper-95 — same band, same index.
3292
- 3: "paper-95",
3293
- 4: "wash-92",
3294
- 5: "wash-89",
3295
- 6: "wash-85",
3296
- 7: "wash-80",
3297
- 8: "wax-74",
3298
- // THE INK BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
3299
- // 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
3300
- // deleted; this stop carries both jobs, its on-color a paper token), the between
3301
- // text stop (a value the retired text-cta hover state used to generate bespokely,
3302
- // promoted to a normal stop), and the strong text stop. The three read as states
3303
- // ARE the text-style cta (owner 2026-08-12). C49 restored the top
3304
- // two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
3305
- // literal in cssRender, not through this table) with the between stop taking the
3306
- // vacated index 10; the indices (9/10/11) are unchanged since.
3307
- 9: "lead-53",
3308
- 10: "ink-42",
3309
- 11: "ink-30"
3310
- };
3311
- var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
3312
- function stopTokenName(stop) {
3313
- const name = SHARED_NAMES[stop];
3314
- if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
3315
- return name;
3316
- }
3317
- var PAPER_100 = "paper-100";
3318
- var INK_0 = "ink-0";
3319
- var STAMP_FILL = "stamp-fill";
3320
- var STAMP_FILL_HOVER = "stamp-fill-hover";
3321
- var STAMP_FILL_PRESSED = "stamp-fill-pressed";
3322
- var STAMP_EDGE = "stamp-edge";
3323
- var STAMP_ON = "stamp-on";
3324
- var STAMP_STATE_LEAVES = {
3325
- [STAMP_FILL]: "stamp/fill",
3326
- [STAMP_FILL_HOVER]: "stamp/fill-hover",
3327
- [STAMP_FILL_PRESSED]: "stamp/fill-pressed",
3328
- [STAMP_EDGE]: "stamp/edge",
3329
- [STAMP_ON]: "stamp/on"
3330
- };
3331
- var STAMP_LEAF = {
3332
- FILL: STAMP_STATE_LEAVES[STAMP_FILL],
3333
- FILL_HOVER: STAMP_STATE_LEAVES[STAMP_FILL_HOVER],
3334
- FILL_PRESSED: STAMP_STATE_LEAVES[STAMP_FILL_PRESSED],
3335
- EDGE: STAMP_STATE_LEAVES[STAMP_EDGE],
3336
- ON: STAMP_STATE_LEAVES[STAMP_ON]
3337
- };
3338
- var SYSTEM_LEAF = {
3339
- ABS_BLACK: "system/abs-black",
3340
- ABS_WHITE: "system/abs-white",
3341
- SURFACE: {
3342
- DIM: "system/surface/dim",
3343
- LOW: "system/surface/low",
3344
- MID: "system/surface/mid",
3345
- HIGH: "system/surface/high"
3346
- },
3347
- ALPHA: {
3348
- TRANSPARENT: "system/alpha/transparent",
3349
- SCRIM: "system/alpha/abs-black-060",
3350
- INK: "system/alpha/ink",
3351
- // the DIRECTIONAL ladders (owner 2026-08-31, renaming the 2026-08-18 bare rungs
3352
- // and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
3353
- // (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
3354
- // INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
3355
- // a "wash": that word belongs to the tinted band stops.) The reversal lives in
3356
- // the engine: a consumer on an inverted ground names one token and never picks a
3357
- // pole. Colored fills never ride these; their states stay engine-solved stops.
3358
- // Rungs are two-digit percent, harmonizing with shadow-04/08/12.
3359
- AWAY_FROM_BG_06: "system/alpha/away-from-bg/06",
3360
- AWAY_FROM_BG_08: "system/alpha/away-from-bg/08",
3361
- AWAY_FROM_BG_16: "system/alpha/away-from-bg/16",
3362
- TOWARD_BG_06: "system/alpha/toward-bg/06",
3363
- TOWARD_BG_08: "system/alpha/toward-bg/08",
3364
- TOWARD_BG_16: "system/alpha/toward-bg/16",
3365
- SHADOW_04: "system/alpha/shadow-04",
3366
- SHADOW_08: "system/alpha/shadow-08",
3367
- SHADOW_12: "system/alpha/shadow-12"
3368
- }
3369
- };
3370
- var SURFACE_PLANE_LAW = {
3371
- [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
3372
- [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
3373
- [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
3374
- [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
3375
- };
3376
- var TOKEN_ORDER = [
3377
- ...Object.values(SHARED_NAMES),
3378
- STAMP_FILL,
3379
- STAMP_FILL_HOVER,
3380
- STAMP_FILL_PRESSED,
3381
- STAMP_ON,
3382
- "identity"
3383
- ];
3384
- function tokenOrder(name) {
3385
- const i = TOKEN_ORDER.indexOf(name);
3386
- return i === -1 ? TOKEN_ORDER.length : i;
3387
- }
3388
-
3389
3391
  // src/engine/cssRender.ts
3390
3392
  var { brandPrimary: CSS_BRAND, brandSecondary: CSS_SECONDARY, neutral: CSS_NEUTRAL } = CSS_FAMILY;
3391
3393
  function toHex2(r, g2, b) {
@@ -3432,7 +3434,7 @@ var alphaRootVars = (mode) => [
3432
3434
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3433
3435
  // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3434
3436
  // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
3435
- // called a "wash" — that word is the tinted band's). One spelling with the Figma
3437
+ // called a "highlighter" — that word is the tinted band's). One spelling with the Figma
3436
3438
  // row: --alpha-toward-bg-06 ↔ system/alpha/toward-bg/06.
3437
3439
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` --alpha-toward-bg-${offsetLeafName(r)}: ${offsetRgba(r, mode === "light" ? "dark" : "light")};`)
3438
3440
  ];
@@ -3459,7 +3461,7 @@ function brandKindBody(prefix, s, mode, page) {
3459
3461
  ` --${prefix}-stamp-fill-pressed: ${stopHex(f.ctaPressed)};`,
3460
3462
  ` --${prefix}-stamp-edge: var(${border ? offsetVarName(ctaBorderRung(prefix)) : TRANSPARENT_VAR});`,
3461
3463
  // the SOFT on-cta (owner 2026-08-04: "neutral cta on's should also be the alpha"): the
3462
- // neutral's cta is the scale-fed WASH-level fill, the system's other quiet cta, so its
3464
+ // neutral's cta is the scale-fed HIGHLIGHTER-level fill, the system's other quiet cta, so its
3463
3465
  // button text takes the pole AT ALPHA like the default-model secondary's — composited
3464
3466
  // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3465
3467
  // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
@@ -3489,16 +3491,16 @@ function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBor
3489
3491
  const p3Dark = brandKindP3Body(CSS_NEUTRAL, s, "dark");
3490
3492
  return [
3491
3493
  `${selector} {`,
3492
- ` --${PAPER_100}: ${p02(s.paper0, "#ffffff")};`,
3493
- ` --${INK_0}: ${p02(s.ink0, "#000000")};`,
3494
+ ` --${PAPER_0}: ${p02(s.paper0, "#ffffff")};`,
3495
+ ` --${PEN_100}: ${p02(s.pen100, "#000000")};`,
3494
3496
  // the neutral IS the page, so it is judged against its own paper stop
3495
3497
  // the neutral's overlays never read the visibility bar (the exempt family), so
3496
3498
  // the brandless chrome context passes 0
3497
3499
  ...brandKindBody(CSS_NEUTRAL, s, "light", nPage("light")),
3498
3500
  `}`,
3499
3501
  `${selector}[data-theme="dark"] {`,
3500
- ` --${PAPER_100}: ${p02(s.paper0Dark, "#000000")};`,
3501
- ` --${INK_0}: ${p02(s.ink0Dark, "#ffffff")};`,
3502
+ ` --${PAPER_0}: ${p02(s.paper0Dark, "#000000")};`,
3503
+ ` --${PEN_100}: ${p02(s.pen100Dark, "#ffffff")};`,
3502
3504
  ...brandKindBody(CSS_NEUTRAL, s, "dark", nPage("dark")),
3503
3505
  `}`,
3504
3506
  ...p3Light.length || p3Dark.length ? [
@@ -3587,8 +3589,8 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3587
3589
  const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3588
3590
  const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3589
3591
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
3590
- const lightAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${INK_0}: ${p0hex(nScale.ink0, "#000000")};`];
3591
- const darkAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${INK_0}: ${p0hex(nScale.ink0Dark, "#ffffff")};`];
3592
+ const lightAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${PEN_100}: ${p0hex(nScale.pen100, "#000000")};`];
3593
+ const darkAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${PEN_100}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
3592
3594
  const linkTrio = linkHex ? resolveLinkTrio(linkHex, contrastProfile) : null;
3593
3595
  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)};`] : [
3594
3596
  ` --link: var(--${CSS_BRAND}-${stopTokenName(9)});`,
@@ -3776,17 +3778,17 @@ function themeToFigma(r, input) {
3776
3778
  const build = (mode) => {
3777
3779
  const p02 = mode === "light" ? nScale.paper0 : nScale.paper0Dark;
3778
3780
  const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, neutralExtra(mode));
3779
- const i02 = mode === "light" ? nScale.ink0 : nScale.ink0Dark;
3780
- const spliceInk0 = (g3) => {
3781
+ const i02 = mode === "light" ? nScale.pen100 : nScale.pen100Dark;
3782
+ const splicePen100 = (g3) => {
3781
3783
  if (!i02) return g3;
3782
3784
  const out = {};
3783
3785
  for (const [k, v2] of Object.entries(g3)) {
3784
3786
  out[k] = v2;
3785
- if (k === stopTokenName(11)) out[INK_0] = colorFromStop(i02);
3787
+ if (k === stopTokenName(11)) out[PEN_100] = colorFromStop(i02);
3786
3788
  }
3787
3789
  return out;
3788
3790
  };
3789
- const neutralGroup = spliceInk0(p02 ? { [PAPER_100]: colorFromStop(p02), ...ramp } : ramp);
3791
+ const neutralGroup = splicePen100(p02 ? { [PAPER_0]: colorFromStop(p02), ...ramp } : ramp);
3790
3792
  const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, brandExtra(secondary, mode, CSS_FAMILY.brandSecondary));
3791
3793
  if (input.secondaryStyle === "outline" && input.secondary) {
3792
3794
  const s8 = secondary[mode].find((s) => s.stop === 8);
@@ -3824,12 +3826,12 @@ function themeToFigma(r, input) {
3824
3826
  putLeaf(g3, STAMP_ON, colorFromHex(esc.onFillIsWhite));
3825
3827
  }
3826
3828
  }
3827
- const scaleInkAt = (n) => {
3829
+ const scaleTextAt = (n) => {
3828
3830
  const s = scale[mode].find((x2) => x2.stop === n);
3829
- if (!s) throw new Error(`themeToFigma link: the brand scale has no ink stop ${n}`);
3831
+ if (!s) throw new Error(`themeToFigma link: the brand scale has no pen stop ${n}`);
3830
3832
  return s;
3831
3833
  };
3832
- 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(scaleInkAt(9)), "link-hover": colorFromStop(scaleInkAt(10)), "link-pressed": colorFromStop(scaleInkAt(11)) };
3834
+ 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)) };
3833
3835
  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) };
3834
3836
  const g2 = {
3835
3837
  brand: brandGroup,
@@ -3945,11 +3947,11 @@ export {
3945
3947
  DEFAULT_LINK_HEX,
3946
3948
  DISABLED_OPACITY,
3947
3949
  EXT_KEY,
3948
- INK_0,
3949
3950
  MODE_SPECS,
3950
3951
  OFFSET_ALPHAS,
3951
3952
  OUTLINE_HOVER_ALPHA,
3952
- PAPER_100,
3953
+ PAPER_0,
3954
+ PEN_100,
3953
3955
  RESOLVER_ID,
3954
3956
  SCALE_STOP_COUNT,
3955
3957
  SCRIM_ALPHA,