okchroma 0.1.7 → 0.2.2

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
- // wax-74
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
- // wax-74
355
+ // crayon-26
356
356
  [0.6, 0.4],
357
357
  // knee (ex-scaffold)
358
358
  [0.64, 0.347],
@@ -723,23 +723,23 @@ function perceptualDarkC(L, H2, nativeC, keep = KEEP_DARK, gamut = MASTER_GAMUT)
723
723
 
724
724
  // src/engine/tokenNames.ts
725
725
  var SHARED_NAMES = {
726
- 1: "paper-99",
727
- 2: "paper-97",
726
+ 1: "paper-1",
727
+ 2: "paper-3",
728
728
  // stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
729
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 wash-collision machinery
731
- // (collision.ts WASH_STOPS) deliberately still spans stops 3–7. (The requirement
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
732
  // spec's `group` label followed the band to 'paper' on 2026-08-31 — labels only,
733
733
  // the spans never moved; see spec.ts groupOf.)
734
734
  // Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
735
- // paper-95 — same band, same index.
736
- 3: "paper-95",
737
- 4: "wash-92",
738
- 5: "wash-89",
739
- 6: "wash-85",
740
- 7: "wash-80",
741
- 8: "wax-74",
742
- // THE INK BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
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
743
  // 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
744
744
  // deleted; this stop carries both jobs, its on-color a paper token), the between
745
745
  // text stop (a value the retired text-cta hover state used to generate bespokely,
@@ -748,9 +748,9 @@ var SHARED_NAMES = {
748
748
  // two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
749
749
  // literal in cssRender, not through this table) with the between stop taking the
750
750
  // vacated index 10; the indices (9/10/11) are unchanged since.
751
- 9: "lead-53",
752
- 10: "ink-42",
753
- 11: "ink-30"
751
+ 9: "pencil-47",
752
+ 10: "pen-58",
753
+ 11: "pen-70"
754
754
  };
755
755
  var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
756
756
  function stopTokenName(stop) {
@@ -758,8 +758,8 @@ function stopTokenName(stop) {
758
758
  if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
759
759
  return name;
760
760
  }
761
- var PAPER_100 = "paper-100";
762
- var INK_0 = "ink-0";
761
+ var PAPER_0 = "paper-0";
762
+ var PEN_100 = "pen-100";
763
763
  var STAMP_FILL = "stamp-fill";
764
764
  var STAMP_FILL_HOVER = "stamp-fill-hover";
765
765
  var STAMP_FILL_PRESSED = "stamp-fill-pressed";
@@ -796,7 +796,7 @@ var SYSTEM_LEAF = {
796
796
  // and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
797
797
  // (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
798
798
  // INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
799
- // a "wash": that word belongs to the tinted band stops.) The reversal lives in
799
+ // a "highlighter": that word belongs to the tinted band stops.) The reversal lives in
800
800
  // the engine: a consumer on an inverted ground names one token and never picks a
801
801
  // pole. Colored fills never ride these; their states stay engine-solved stops.
802
802
  // Rungs are two-digit percent, harmonizing with shadow-04/08/12.
@@ -812,10 +812,10 @@ var SYSTEM_LEAF = {
812
812
  }
813
813
  };
814
814
  var SURFACE_PLANE_LAW = {
815
- [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
815
+ [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_0 },
816
816
  [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
817
817
  [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
818
- [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
818
+ [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_0, dark: SHARED_NAMES[3] }
819
819
  };
820
820
  var TOKEN_ORDER = [
821
821
  ...Object.values(SHARED_NAMES),
@@ -831,26 +831,26 @@ function tokenOrder(name) {
831
831
  }
832
832
 
833
833
  // src/engine/requirements/spec.ts
834
- var inkLane = (g2) => g2 === "lead" || g2 === "ink";
835
- var groupOf = (stop) => (stop === 0 ? PAPER_100 : stopTokenName(stop)).split("-")[0];
834
+ var textLane = (g2) => g2 === "pencil" || g2 === "pen" || g2 === "lead" || g2 === "ink";
835
+ var groupOf = (stop) => (stop === 0 ? PAPER_0 : stopTokenName(stop)).split("-")[0];
836
836
  var PAPER0_DARK_ROOT_L = 0.16;
837
837
  var PL_LADDER = { hue: "warm-drift", L: "perceptual", chroma: "ladder" };
838
838
  var PL_TEXT = { hue: "warm-drift", L: "perceptual", chroma: "brand" };
839
839
  var P_LIFT = { hue: "warm-torsion", L: "perceptual-lift", chroma: "ladder" };
840
840
  var P_FIXED = { hue: "warm-torsion", L: "fixed", chroma: "ladder" };
841
841
  var P_TEXT = { hue: "warm-torsion", L: "perceptual", chroma: "brand" };
842
- var S8 = { metric: "wcag", against: "paper-95", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
843
- var T9 = { metric: "wcag", against: "paper-97", target: INK_9_CONTRAST, level: "AA" };
844
- var T10 = { metric: "wcag", against: "wash-80", target: INK_10_CONTRAST, level: "AA" };
845
- 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" };
846
846
  var ONS = { onFill: { metric: "apca-pole", enforce: true, ratioFloor: 4.5, coEnforceLc: 65 } };
847
847
  var LIGHT = {
848
848
  stops: [
849
- // paper-100 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
849
+ // paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
850
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/wash/wax-74: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
851
+ // paper/highlighter/crayon-26: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
852
852
  // falls out of the shape (see above) — no min-separation require. Only stop 8 carries a require: the WCAG
853
- // 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)).
854
854
  ...[1, 2, 3, 4, 5, 6, 7, 8].map((stop) => ({
855
855
  stop,
856
856
  rootL: ROOT_L_LIGHT[stop],
@@ -860,12 +860,12 @@ var LIGHT = {
860
860
  baseC: SCALE_C_LIGHT[stop].base,
861
861
  require: stop === 8 ? S8 : void 0
862
862
  })),
863
- // ink text: perceptual + contrast-required. lead-53 (ink-9) is ALSO the emphasis fill (the
864
- // 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) —
865
865
  // a normal stop like its neighbors; the three together are the text-register cta.
866
- { stop: 9, rootL: ROOT_L_LIGHT[9], group: groupOf(9), produce: PL_TEXT, chromaMult: SCALE_C_LIGHT[9].inkMult, inkMaxC: SCALE_C_LIGHT[9].inkMaxC, 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].inkMult, inkMaxC: SCALE_C_LIGHT[10].inkMaxC, 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].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 }
869
869
  ],
870
870
  roles: [
871
871
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: 0, chromaMult: 1 },
@@ -876,10 +876,10 @@ var LIGHT = {
876
876
  };
877
877
  var DARK = {
878
878
  stops: [
879
- // 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
880
880
  // absolute void (the old hard-coded #000000 was "too much"). Lift applies like the rest of the scale.
881
881
  { stop: 0, rootL: PAPER0_DARK_ROOT_L, group: groupOf(0), produce: P_LIFT, satFraction: SCALE_C_DARK[0].sat },
882
- // paper/wash 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
882
+ // paper/highlighter 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
883
883
  // 3:1 non-text require DECLARED (the Stage-5 flip, owner-approved) — the blue-recede failure is prevented
884
884
  // BY RULE, not by patch. The require now genuinely PLACES the stop rather than catching a few low-luminance
885
885
  // hues: on the delta-carry path it solves from the sentinel every time, and the old claim that "most hues
@@ -893,11 +893,11 @@ var DARK = {
893
893
  satFraction: SCALE_C_DARK[stop].sat,
894
894
  require: stop === 8 ? S8 : void 0
895
895
  })),
896
- // 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
897
897
  // clears them for every hue (the gate proves it), so values don't move — but the guarantee is now a rule.
898
- { stop: 9, rootL: ROOT_L_DARK[9], group: groupOf(9), produce: P_TEXT, chromaMult: SCALE_C_DARK[9].inkMult, inkMaxC: SCALE_C_DARK[9].inkMaxC, 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].inkMult, inkMaxC: SCALE_C_DARK[10].inkMaxC, 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].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 }
901
901
  ],
902
902
  roles: [
903
903
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: DARK_CTA_MIN_L, chromaMult: 1 },
@@ -908,7 +908,7 @@ var DARK = {
908
908
  };
909
909
  var MODE_SPECS = { light: LIGHT, dark: DARK };
910
910
 
911
- // ../../../Users/emilygerrity/okchroma/node_modules/helmlab/dist/index.js
911
+ // node_modules/helmlab/dist/index.js
912
912
  function o0(n) {
913
913
  let t = new Float64Array(9);
914
914
  for (let e = 0; e < 3; e++) for (let s = 0; s < 3; s++) t[e * 3 + s] = n[e][s];
@@ -1824,7 +1824,7 @@ function p2Diff(a, b) {
1824
1824
  var DEFAULT_APCA_LC_MAP = { 3: 30, 4.5: 75, 6.5: 85, 7: 90 };
1825
1825
  function toApca(req, lcMap) {
1826
1826
  if (req.metric !== "wcag") return req;
1827
- 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 };
1828
1828
  const targetLc = lcMap[req.target];
1829
1829
  if (targetLc === void 0) throw new Error(`apca profile: no Lc mapping for wcag target ${req.target}`);
1830
1830
  return { metric: "apca", against: req.against, targetLc };
@@ -1838,7 +1838,7 @@ function withProfile(spec, profile, lcMap = DEFAULT_APCA_LC_MAP) {
1838
1838
  // (The stop-9 CARVE-OUT is gone with the stop, owner 2026-07-29. It existed because
1839
1839
  // highlight-9's require was a fill-vs-its-own-plane bar with no honest slot in a map
1840
1840
  // of TEXT bars — 3 → 30 non-text, 4.5 → 75 body, 7 → 90 — so the apca lane kept its
1841
- // 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
1842
1842
  // 75 slot is its correct translation, and it maps like every other require.)
1843
1843
  stops: spec.stops.map((s) => s.require ? { ...s, require: toApca(s.require, lcMap) } : s),
1844
1844
  ons: {
@@ -2000,16 +2000,16 @@ function separationClampLight(ctx, placed, chromaAt, ref, target) {
2000
2000
  return { L: pass, C: ceilingAt(pass), H: ctx.lightHueAt(pass) };
2001
2001
  }
2002
2002
  function placeLightText(ctx, rootL, cMult, maxLFor, deepen, maxC = Infinity) {
2003
- const inkC = Math.min(cMult * ctx.brandC, maxC);
2004
- 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));
2005
2005
  let H2 = ctx.lightHueAt(anchorL);
2006
- let C2 = clampChromaToGamut(anchorL, inkC, H2);
2006
+ let C2 = clampChromaToGamut(anchorL, textC, H2);
2007
2007
  let L = Math.min(anchorL, maxLFor(C2, H2));
2008
2008
  H2 = ctx.lightHueAt(L);
2009
- C2 = clampChromaToGamut(L, inkC, H2);
2009
+ C2 = clampChromaToGamut(L, textC, H2);
2010
2010
  L = Math.min(anchorL, maxLFor(C2, H2));
2011
2011
  L = Math.min(L - deepen, maxLFor(C2, ctx.lightHueAt(L - deepen)));
2012
- 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) };
2013
2013
  }
2014
2014
  function onFillIsWhiteLight(ctx, enforce, ratioFloor) {
2015
2015
  const fill9ApcaY = apcaYAt(ctx.scaleL, clampChromaToGamut(ctx.scaleL, ctx.cAt("light", ctx.scaleL, ctx.brandC), ctx.brandH), ctx.brandH);
@@ -2023,7 +2023,7 @@ function buildDarkContext(ctx, specFloorL = DARK_CTA_MIN_L) {
2023
2023
  return { dark9L, darkC9, darkHueAtL };
2024
2024
  }
2025
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));
2026
- 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));
2027
2027
  function placeDark(dctx, rootL, chromaAt, lift = false) {
2028
2028
  let L = perceptualRungL(rootL, chromaAt(rootL), dctx.darkHueAtL(rootL));
2029
2029
  if (lift) L = Math.max(L, rootL);
@@ -2279,12 +2279,14 @@ function solveDarkCtaExit(cur, cFor, darkH, redDark, enforceLc, coLc) {
2279
2279
  var oklchToSrgb = (L, C2, H2) => Object.values(srgbEmitChannels({ L, C: C2, H: H2 })).map((c) => Math.max(0, Math.min(1, c)));
2280
2280
  var toHex = (rgb) => "#" + rgb.map((c) => Math.round(c * 255).toString(16).padStart(2, "0")).join("");
2281
2281
  var CTA_CLEARANCE_CAPS = [0.05, 0.92];
2282
+ var CHROMATIC_W80_WORST_SHIP_Y = { light: 0.459357, dark: 0.074936 };
2283
+ var CHROMATIC_P3_WORST_SHIP_Y = { light: 0.836007, dark: 0.016041 };
2282
2284
  function resolveRamp(hex, mode, spec, opts) {
2283
2285
  const o = hexToOklch(hex);
2284
2286
  const seed = { hex, ...o };
2285
2287
  const ctx = buildContext(hex, opts);
2286
2288
  spec ?? (spec = MODE_SPECS[mode]);
2287
- if (ctx.opts?.inkGround) {
2289
+ if (ctx.opts?.textGround) {
2288
2290
  spec = {
2289
2291
  ...spec,
2290
2292
  stops: spec.stops.map((s) => s.stop === 10 && s.require?.metric === "wcag" ? { ...s, require: { ...s.require, target: 6.5 } } : s)
@@ -2306,12 +2308,12 @@ function resolveRamp(hex, mode, spec, opts) {
2306
2308
  if (!ref) throw new Error(`stop ${forWhom}: require against stop ${stopNum} but it is not resolved yet`);
2307
2309
  return ref;
2308
2310
  };
2309
- const AGAINST_STOP = { "paper-99": 1, "paper-97": 2, "paper-95": 3, "wash-80": 7 };
2311
+ const AGAINST_STOP = { "paper-1": 1, "paper-3": 2, "paper-5": 3, "highlighter-20": 7 };
2310
2312
  const declaredAnchor = (req) => req.metric === "min-separation" ? 1 : AGAINST_STOP[req.against] ?? 2;
2311
2313
  const wcagAnchorStop = (req, stop) => stop >= 9 && declaredAnchor(req) <= 3 ? 3 : declaredAnchor(req);
2312
- const inkGround = ctx.opts?.inkGround?.[mode];
2313
- const groundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(wcagAnchorStop(req, stop), stop);
2314
- const apcaGroundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(declaredAnchor(req), stop);
2314
+ const textGround = ctx.opts?.textGround?.[mode];
2315
+ const groundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(wcagAnchorStop(req, stop), stop);
2316
+ const apcaGroundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(declaredAnchor(req), stop);
2315
2317
  const NEUTRAL_P3_WORST_SHIP_Y = { light: 0.845015, dark: 0.014247 };
2316
2318
  const NEUTRAL_W80_WORST_SHIP_Y = { light: 0.506433, dark: 0.074262 };
2317
2319
  const maxLForOf = (req, forWhom, withMargin) => {
@@ -2329,14 +2331,14 @@ function resolveRamp(hex, mode, spec, opts) {
2329
2331
  }
2330
2332
  throw new Error(`stop ${forWhom}: ${req.metric} is not a contrast require`);
2331
2333
  };
2332
- const INK_DEEPEN = 0;
2334
+ const TEXT_DEEPEN = 0;
2333
2335
  for (const sp of spec.stops) {
2334
2336
  let placed;
2335
2337
  let clamped = false, unresolvable;
2336
2338
  if (mode === "light") {
2337
- if (inkLane(sp.group)) {
2338
- if (!sp.require) throw new Error(`light ink stop ${sp.stop} must declare a contrast require`);
2339
- placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), INK_DEEPEN, sp.inkMaxC);
2339
+ if (textLane(sp.group)) {
2340
+ if (!sp.require) throw new Error(`light pen stop ${sp.stop} must declare a contrast require`);
2341
+ placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), TEXT_DEEPEN, sp.textMaxC);
2340
2342
  clamped = true;
2341
2343
  } else if (sp.produce.L === "fixed") {
2342
2344
  const chromaAt = lightScaleChromaAt(ctx, sp.baseC ?? 0, sp.satFraction ?? 1);
@@ -2348,7 +2350,7 @@ function resolveRamp(hex, mode, spec, opts) {
2348
2350
  placed = placeLightScale(ctx, sp.rootL, chromaAt, contrastReq ? maxLForOf(contrastReq, sp.stop, true) : void 0, targetChromaAt);
2349
2351
  clamped = !!contrastReq;
2350
2352
  if (sp.require?.metric === "min-separation") {
2351
- const refStop = sp.require.against === "paper-99" ? 1 : sp.stop - 1;
2353
+ const refStop = sp.require.against === "paper-1" ? 1 : sp.stop - 1;
2352
2354
  const ref = stops.find((s) => s.stop === refStop);
2353
2355
  if (!ref) throw new Error(`stop ${sp.stop}: min-separation against stop ${refStop} but it is not resolved yet`);
2354
2356
  const before = placed.L;
@@ -2358,8 +2360,8 @@ function resolveRamp(hex, mode, spec, opts) {
2358
2360
  }
2359
2361
  } else {
2360
2362
  const d = dctx;
2361
- const inkTwin = inkLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2362
- const chromaAt = inkLane(sp.group) ? 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);
2363
+ const textTwin = textLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2364
+ 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);
2363
2365
  let spineH = null;
2364
2366
  const dl = ctx.opts?.deltaLightStops;
2365
2367
  const ls = dl && sp.stop >= 1 && sp.stop <= 8 ? dl.find((s) => s.stop === sp.stop) : void 0;
@@ -2379,7 +2381,7 @@ function resolveRamp(hex, mode, spec, opts) {
2379
2381
  }
2380
2382
  if (ctx.opts.deltaHKPlace) L = perceptualRungL(sp.rootL, ls.C, ls.H);
2381
2383
  if (ctx.opts.deltaLiftFloor) L = Math.max(L, sp.rootL);
2382
- if (ctx.opts.deltaChromaEq && !inkLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2384
+ if (ctx.opts.deltaChromaEq && !textLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2383
2385
  placed = { L, C: C2, H: spineH ?? ls.H };
2384
2386
  } else if (sp.produce.L === "fixed") {
2385
2387
  placed = ls ? placeDarkDelta(d, sp.rootL, chromaAt, ls) : { L: sp.rootL, C: chromaAt(sp.rootL), H: d.darkHueAtL(sp.rootL) };
@@ -2415,18 +2417,25 @@ function resolveRamp(hex, mode, spec, opts) {
2415
2417
  if (sp.require?.metric === "wcag") {
2416
2418
  const anchor = groundOf(sp.require, sp.stop);
2417
2419
  const anchorShipY = shippedY(anchor.L, anchor.C, anchor.H);
2418
- const paperBound = sp.stop >= 8 && !(inkGround && sp.stop >= 9);
2420
+ const paperBound = sp.stop >= 8 && !(textGround && sp.stop >= 9);
2419
2421
  const crossBoundY = wcagAnchorStop(sp.require, sp.stop) === 7 ? NEUTRAL_W80_WORST_SHIP_Y[mode] : NEUTRAL_P3_WORST_SHIP_Y[mode];
2420
2422
  const shipRatio = (L) => {
2421
2423
  const y2 = shippedY(L, placed.C, placed.H);
2422
2424
  const own = contrastRatio(y2, anchorShipY);
2423
2425
  return paperBound ? Math.min(own, contrastRatio(y2, crossBoundY)) : own;
2424
2426
  };
2425
- if (shipRatio(placed.L) < sp.require.target) {
2427
+ const highlighterBoundY = ctx.opts?.crossHighlighterBoundY && sp.stop >= 10 && !textGround ? ctx.opts.crossHighlighterBoundY[mode] : void 0;
2428
+ const highlighterRatio = (L) => highlighterBoundY === void 0 ? Infinity : contrastRatio(shippedY(L, placed.C, placed.H), highlighterBoundY);
2429
+ const paperBoundY = ctx.opts?.crossPaperBoundY && sp.stop >= 8 && !textGround ? ctx.opts.crossPaperBoundY[mode] : void 0;
2430
+ const paperBar = sp.stop === 8 ? STOP_8_NONTEXT_CONTRAST : sp.stop === 9 ? PENCIL_9_CONTRAST : PEN_10_CONTRAST;
2431
+ const paperRatio = (L) => paperBoundY === void 0 ? Infinity : contrastRatio(shippedY(L, placed.C, placed.H), paperBoundY);
2432
+ const target = sp.require.target;
2433
+ const clears = (L) => shipRatio(L) >= target && highlighterRatio(L) >= PEN_10_CONTRAST && paperRatio(L) >= paperBar;
2434
+ if (!clears(placed.L)) {
2426
2435
  const away = shippedY(placed.L, placed.C, placed.H) < anchorShipY ? -1 : 1;
2427
2436
  let L2 = placed.L;
2428
- for (let i = 0; i < 100 && shipRatio(L2) < sp.require.target; i++) L2 += away * 1e-3;
2429
- if (shipRatio(L2) >= sp.require.target) {
2437
+ for (let i = 0; i < 100 && !clears(L2); i++) L2 += away * 1e-3;
2438
+ if (clears(L2)) {
2430
2439
  placed = { ...placed, L: L2 };
2431
2440
  clamped = true;
2432
2441
  }
@@ -2437,13 +2446,15 @@ function resolveRamp(hex, mode, spec, opts) {
2437
2446
  shipRatio(placed.L)
2438
2447
  );
2439
2448
  if (got < sp.require.target - 1e-3) unresolvable = `stop ${sp.stop}: contrast ${got.toFixed(2)} < required ${sp.require.target}`;
2449
+ else if (highlighterRatio(placed.L) < PEN_10_CONTRAST - 1e-3) unresolvable = `stop ${sp.stop}: contrast ${highlighterRatio(placed.L).toFixed(2)} < required ${PEN_10_CONTRAST} on the worst chromatic highlighter-20`;
2450
+ else if (paperRatio(placed.L) < paperBar - 1e-3) unresolvable = `stop ${sp.stop}: contrast ${paperRatio(placed.L).toFixed(2)} < required ${paperBar} on the worst chromatic paper-5`;
2440
2451
  } else if (sp.require?.metric === "apca") {
2441
2452
  const ga = apcaGroundOf(sp.require, sp.stop);
2442
2453
  const refA = apcaYAt(ga.L, ga.C, ga.H);
2443
2454
  const got = Math.abs(apcaLc(apcaYAt(placed.L, clampChromaToGamut(placed.L, placed.C, placed.H), placed.H), refA));
2444
2455
  if (got < sp.require.targetLc - APCA_TOL_LC) unresolvable = `stop ${sp.stop}: |Lc| ${got.toFixed(1)} < required ${sp.require.targetLc}`;
2445
2456
  } else if (sp.require?.metric === "min-separation") {
2446
- const ref = stops.find((s) => s.stop === (sp.require.against === "paper-99" ? 1 : sp.stop - 1));
2457
+ const ref = stops.find((s) => s.stop === (sp.require.against === "paper-1" ? 1 : sp.stop - 1));
2447
2458
  const rad2 = (h) => h * Math.PI / 180;
2448
2459
  const gC = clampChromaToGamut(placed.L, placed.C, placed.H);
2449
2460
  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);
@@ -2599,7 +2610,7 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2599
2610
  const dark = darkRamp.stops.filter((s) => s.stop >= 1).map(toStop).sort((a, b) => a.stop - b.stop);
2600
2611
  const p0Light = lightRamp.stops.find((s) => s.stop === 0);
2601
2612
  const p0Dark = darkRamp.stops.find((s) => s.stop === 0);
2602
- const inkPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2613
+ const penPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2603
2614
  const cta = makeStop(9, lightRamp.roles.cta.L, lightRamp.roles.cta.C, lightRamp.roles.cta.H);
2604
2615
  const ctaDark = makeStop(9, darkRamp.roles.cta.L, darkRamp.roles.cta.C, darkRamp.roles.cta.H);
2605
2616
  const roleStop = (r, stop) => makeStop(stop, r.L, r.C, r.H);
@@ -2623,8 +2634,8 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2623
2634
  identityHex: hex.toUpperCase(),
2624
2635
  paper0: p0Light ? makeStop(0, p0Light.L, p0Light.C, p0Light.H) : void 0,
2625
2636
  paper0Dark: p0Dark ? makeStop(0, p0Dark.L, p0Dark.C, p0Dark.H) : void 0,
2626
- ink0: inkPole(light, 0),
2627
- ink0Dark: inkPole(dark, 1)
2637
+ pen100: penPole(light, 0),
2638
+ pen100Dark: penPole(dark, 1)
2628
2639
  };
2629
2640
  }
2630
2641
  function generateNeutralScale(brandH, level = "default", contrastProfile) {
@@ -2636,7 +2647,11 @@ function generateNeutralScale(brandH, level = "default", contrastProfile) {
2636
2647
  const scale = generateScale(grayHex, "neutral", "light", {
2637
2648
  chromaCurve: curve,
2638
2649
  enforceOnFillContrast: true,
2639
- contrastProfile
2650
+ contrastProfile,
2651
+ // T13: the neutral's pens clear every chromatic family's highlighter-20 (the symmetric
2652
+ // pen guarantee); the chromatic ramps clear the neutral's through NEUTRAL_W80_WORST_SHIP_Y
2653
+ crossHighlighterBoundY: CHROMATIC_W80_WORST_SHIP_Y,
2654
+ crossPaperBoundY: CHROMATIC_P3_WORST_SHIP_Y
2640
2655
  });
2641
2656
  const asCta = (stop, src) => makeStop(stop, src.L, src.C, src.H);
2642
2657
  scale.cta = asCta(9, scale.light[3]);
@@ -2696,8 +2711,7 @@ function generateSubtleSecondary(hex, opts) {
2696
2711
 
2697
2712
  // src/engine/tokenDescriptions.ts
2698
2713
  var AA_LARGE = "AA large text and UI elements";
2699
- var AA_BODY = "AA standard body text & Level AAA large text";
2700
- var AAA_BODY = "AAA standard body text";
2714
+ var AA_BODY = "AA standard body text";
2701
2715
  var FAMILY = {
2702
2716
  neutral: "neutral",
2703
2717
  brandPrimary: "brand",
@@ -2733,7 +2747,7 @@ var COLOR_WORD = {
2733
2747
  [FAMILY.info]: " (blue)"
2734
2748
  };
2735
2749
  var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2736
- var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2750
+ var HIGHLIGHTER = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2737
2751
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2738
2752
  var OVERLAY = {
2739
2753
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2741,25 +2755,25 @@ var OVERLAY = {
2741
2755
  collides: true
2742
2756
  };
2743
2757
  var SCALE = {
2744
- "paper-99": PAPER,
2758
+ "paper-1": PAPER,
2745
2759
  "paper-99-overlay": OVERLAY,
2746
- "paper-97": PAPER,
2760
+ "paper-3": PAPER,
2747
2761
  "paper-97-overlay": OVERLAY,
2748
- "paper-95": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2762
+ "paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2749
2763
  "paper-95-overlay": OVERLAY,
2750
- "wash-92": WASH,
2751
- "wash-89": WASH,
2752
- "wash-85": WASH,
2753
- "wash-80": WASH,
2764
+ "highlighter-8": HIGHLIGHTER,
2765
+ "highlighter-11": HIGHLIGHTER,
2766
+ "highlighter-15": HIGHLIGHTER,
2767
+ "highlighter-20": HIGHLIGHTER,
2754
2768
  // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2755
- // landing on the wax rows is worth its ic-ON-s noise — the one surviving body carrier)
2756
- "wax-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2757
- "lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2758
- "ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2769
+ // landing on the crayon rows is worth its ic-ON-s noise — the one surviving body carrier)
2770
+ "crayon-26": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2771
+ "pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2772
+ "pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2759
2773
  // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2760
2774
  // words, and a body carrying either floods that token's picker search — the C50 law;
2761
2775
  // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2762
- "ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2776
+ "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2763
2777
  // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2764
2778
  // these bodies on purpose: it stopped being a token label, so it floods nothing,
2765
2779
  // and a designer's "cta" query still lands on these rows.
@@ -2788,7 +2802,7 @@ var LINK_INVERSE = (state, contrast) => ({
2788
2802
  var SYSTEM = {
2789
2803
  "system/abs-black": ABS,
2790
2804
  "system/abs-white": ABS,
2791
- // (the community system/ink-0 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2805
+ // (the community system/pen-100 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2792
2806
  // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2793
2807
  // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2794
2808
  "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
@@ -2804,7 +2818,7 @@ var SYSTEM = {
2804
2818
  "system/alpha/away-from-bg/08": OFFSET,
2805
2819
  "system/alpha/away-from-bg/16": OFFSET,
2806
2820
  // the inverse ladder: the offset rungs with the pole flipped per mode, for
2807
- // washes and edges over inverted backgrounds ("interaction" avoided in the
2821
+ // highlighters and edges over inverted backgrounds ("interaction" avoided in the
2808
2822
  // bodies: acti-ON would feed the "on" flood)
2809
2823
  "system/alpha/toward-bg/06": OFFSET_INVERSE,
2810
2824
  "system/alpha/toward-bg/08": OFFSET_INVERSE,
@@ -2821,10 +2835,10 @@ var SYSTEM = {
2821
2835
  "system/surface/high": PLANE("topmost plane \u2014 modals, dialogs"),
2822
2836
  "system/link/default/enabled": LINK("", AA_BODY),
2823
2837
  "system/link/default/hover": LINK(" pointer-over", AA_BODY),
2824
- "system/link/default/pressed": LINK(" pressed", AAA_BODY),
2838
+ "system/link/default/pressed": LINK(" pressed", AA_BODY),
2825
2839
  "system/link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2826
2840
  "system/link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2827
- "system/link/inverse/pressed": LINK_INVERSE(" pressed", AAA_BODY)
2841
+ "system/link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2828
2842
  };
2829
2843
  var PREFIXES = [
2830
2844
  // community spellings FIRST: the 2026-08-21 family rename made the ext word a
@@ -2871,15 +2885,15 @@ function checkCollision(brand, signalScale, signalDef, mode) {
2871
2885
  collides: hueGate && deltaE <= threshold
2872
2886
  };
2873
2887
  }
2874
- var HUE_COLLISION_WASH_DEG = 15;
2888
+ var HUE_COLLISION_HIGHLIGHTER_DEG = 15;
2875
2889
  var HUE_COLLISION_MIN_V = 0.5;
2876
2890
  var SECONDARY_NOTE_MIN_V = HUE_NOISE_C / VIVID_C;
2877
- var WASH_STOPS = [3, 4, 5, 6, 7];
2878
- function washProximity(brand, sig, mode) {
2891
+ var HIGHLIGHTER_STOPS = [3, 4, 5, 6, 7];
2892
+ function highlighterProximity(brand, sig, mode) {
2879
2893
  const b = mode === "light" ? brand.light : brand.dark;
2880
2894
  const s = mode === "light" ? sig.light : sig.dark;
2881
2895
  let dH = Infinity, dE = Infinity;
2882
- for (const k of WASH_STOPS) {
2896
+ for (const k of HIGHLIGHTER_STOPS) {
2883
2897
  const bs = b.find((x2) => x2.stop === k), ss = s.find((x2) => x2.stop === k);
2884
2898
  if (!bs || !ss) continue;
2885
2899
  dH = Math.min(dH, hueDistance(bs.H, ss.H));
@@ -2888,15 +2902,15 @@ function washProximity(brand, sig, mode) {
2888
2902
  return { dH, dE };
2889
2903
  }
2890
2904
  function checkHueCollision(brand, signalScale, signalDef, opts) {
2891
- const l = washProximity(brand, signalScale, "light");
2892
- const d = washProximity(brand, signalScale, "dark");
2905
+ const l = highlighterProximity(brand, signalScale, "light");
2906
+ const d = highlighterProximity(brand, signalScale, "dark");
2893
2907
  const vividness = Math.min(1, brand.brandC / VIVID_C);
2894
2908
  return {
2895
2909
  signal: signalDef.name,
2896
- dHueWash: { light: l.dH, dark: d.dH },
2897
- washDeltaE: { light: l.dE, dark: d.dE },
2910
+ dHueHighlighter: { light: l.dH, dark: d.dH },
2911
+ highlighterDeltaE: { light: l.dE, dark: d.dE },
2898
2912
  vividness,
2899
- collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_WASH_DEG
2913
+ collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_HIGHLIGHTER_DEG
2900
2914
  };
2901
2915
  }
2902
2916
  var YELLOW_SPLIT_H = 96;
@@ -3089,7 +3103,7 @@ function resolveBrand(hex, name, opts) {
3089
3103
  // (buildSignalScales/signalShift) — neutral alone stays outside; exact has enforce off.
3090
3104
  apcaClearance: opts?.apcaClearance ?? true,
3091
3105
  darkCtaFlatApp: opts?.darkCtaFlatApp,
3092
- inkGround: opts?.inkGround
3106
+ textGround: opts?.textGround
3093
3107
  };
3094
3108
  let scale = generateScale(hex, name, void 0, floor);
3095
3109
  let pending = [];
@@ -3172,7 +3186,7 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3172
3186
  const s = resolveBrand(linkHex, "link", { skipCollisionRules: true, contrastProfile }).scale;
3173
3187
  const at = (arr, n) => {
3174
3188
  const st = arr.find((x2) => x2.stop === n);
3175
- if (!st) throw new Error(`resolveLinkTrio: the link resolve has no ink stop ${n}`);
3189
+ if (!st) throw new Error(`resolveLinkTrio: the link resolve has no pen stop ${n}`);
3176
3190
  return st;
3177
3191
  };
3178
3192
  return {
@@ -3185,16 +3199,16 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3185
3199
  };
3186
3200
  }
3187
3201
  function resolveLinkInverseTrio(seedHex, contrastProfile) {
3188
- const ground = INK_30_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3202
+ const ground = PEN_70_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3189
3203
  const s = resolveBrand(seedHex, "link-inverse", {
3190
3204
  skipCollisionRules: true,
3191
3205
  contrastProfile,
3192
3206
  // the cross — ramp keyed, ground mode-swapped
3193
- inkGround: { light: ground.dark, dark: ground.light }
3207
+ textGround: { light: ground.dark, dark: ground.light }
3194
3208
  }).scale;
3195
3209
  const at = (arr, n) => {
3196
3210
  const st = arr.find((x2) => x2.stop === n);
3197
- if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no ink stop ${n}`);
3211
+ if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no pen stop ${n}`);
3198
3212
  return st;
3199
3213
  };
3200
3214
  return {
@@ -3208,12 +3222,12 @@ function resolveLinkInverseTrio(seedHex, contrastProfile) {
3208
3222
  };
3209
3223
  }
3210
3224
  function escapeCtaFamily(nScale, mode, contrastProfile) {
3211
- const strongInk = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3212
- if (!strongInk) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3213
- const mk = (stop, L) => makeStop(stop, L, strongInk.C, strongInk.H);
3214
- const cta = mk(9, strongInk.L);
3215
- const ctaHover = mk(10, stateFillL(strongInk.L, mode, 1));
3216
- const ctaPressed = mk(11, stateFillL(strongInk.L, mode, 2));
3225
+ const strongText = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3226
+ if (!strongText) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3227
+ const mk = (stop, L) => makeStop(stop, L, strongText.C, strongText.H);
3228
+ const cta = mk(9, strongText.L);
3229
+ const ctaHover = mk(10, stateFillL(strongText.L, mode, 1));
3230
+ const ctaPressed = mk(11, stateFillL(strongText.L, mode, 2));
3217
3231
  const onEnforce = contrastProfile !== "apca";
3218
3232
  const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
3219
3233
  const onFillIsWhite = onTextIsWhite(apcaY(cta.r, cta.g, cta.b), cta.L, cta.C, cta.H, onEnforce, onFloor);
@@ -3289,7 +3303,7 @@ function resolveTheme(input) {
3289
3303
  for (const def of SIGNALS2) {
3290
3304
  if (adoptedForSecondary.has(def.name)) continue;
3291
3305
  const h = checkHueCollision(scale2, effectiveOf(def.name), def, { minV: SECONDARY_NOTE_MIN_V });
3292
- if (h.collides) out.push(wording(def.name, Math.min(h.washDeltaE.light, h.washDeltaE.dark)));
3306
+ if (h.collides) out.push(wording(def.name, Math.min(h.highlighterDeltaE.light, h.highlighterDeltaE.dark)));
3293
3307
  }
3294
3308
  return out;
3295
3309
  };
@@ -3339,7 +3353,7 @@ function resolveTheme(input) {
3339
3353
  derived: true,
3340
3354
  notes: [
3341
3355
  `secondary derived from the brand color (default model, seed ${liftedHex})`,
3342
- ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3356
+ ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3343
3357
  ],
3344
3358
  distinctness: ctaDistinctness(primary.scale, scale2)
3345
3359
  },
@@ -3364,7 +3378,7 @@ function resolveTheme(input) {
3364
3378
  derived: false,
3365
3379
  notes: [
3366
3380
  `secondary keeps your color through the ramp; the cta is a tint of it (tint seed ${tintedHex})`,
3367
- ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3381
+ ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3368
3382
  ],
3369
3383
  distinctness: distinctness2
3370
3384
  },
@@ -3376,7 +3390,7 @@ function resolveTheme(input) {
3376
3390
  const scale = rSec.scale;
3377
3391
  const level = "standard";
3378
3392
  mergeSecondarySignals(scale, input.secondaryHex);
3379
- const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (wash \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3393
+ const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (highlighter \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3380
3394
  const distinctness = ctaDistinctness(primary.scale, scale);
3381
3395
  if (distinctness.close)
3382
3396
  notes.push(`secondary reads close to the primary (\u0394E ${Math.min(distinctness.light, distinctness.dark).toFixed(2)}) \u2014 consider a more distinct color`);
@@ -3435,7 +3449,7 @@ var alphaRootVars = (mode) => [
3435
3449
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3436
3450
  // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3437
3451
  // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
3438
- // called a "wash" — that word is the tinted band's). One spelling with the Figma
3452
+ // called a "highlighter" — that word is the tinted band's). One spelling with the Figma
3439
3453
  // row: --alpha-toward-bg-06 ↔ system/alpha/toward-bg/06.
3440
3454
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` --alpha-toward-bg-${offsetLeafName(r)}: ${offsetRgba(r, mode === "light" ? "dark" : "light")};`)
3441
3455
  ];
@@ -3462,7 +3476,7 @@ function brandKindBody(prefix, s, mode, page) {
3462
3476
  ` --${prefix}-stamp-fill-pressed: ${stopHex(f.ctaPressed)};`,
3463
3477
  ` --${prefix}-stamp-edge: var(${border ? offsetVarName(ctaBorderRung(prefix)) : TRANSPARENT_VAR});`,
3464
3478
  // the SOFT on-cta (owner 2026-08-04: "neutral cta on's should also be the alpha"): the
3465
- // neutral's cta is the scale-fed WASH-level fill, the system's other quiet cta, so its
3479
+ // neutral's cta is the scale-fed HIGHLIGHTER-level fill, the system's other quiet cta, so its
3466
3480
  // button text takes the pole AT ALPHA like the default-model secondary's — composited
3467
3481
  // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3468
3482
  // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
@@ -3492,16 +3506,16 @@ function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBor
3492
3506
  const p3Dark = brandKindP3Body(CSS_NEUTRAL, s, "dark");
3493
3507
  return [
3494
3508
  `${selector} {`,
3495
- ` --${PAPER_100}: ${p02(s.paper0, "#ffffff")};`,
3496
- ` --${INK_0}: ${p02(s.ink0, "#000000")};`,
3509
+ ` --${PAPER_0}: ${p02(s.paper0, "#ffffff")};`,
3510
+ ` --${PEN_100}: ${p02(s.pen100, "#000000")};`,
3497
3511
  // the neutral IS the page, so it is judged against its own paper stop
3498
3512
  // the neutral's overlays never read the visibility bar (the exempt family), so
3499
3513
  // the brandless chrome context passes 0
3500
3514
  ...brandKindBody(CSS_NEUTRAL, s, "light", nPage("light")),
3501
3515
  `}`,
3502
3516
  `${selector}[data-theme="dark"] {`,
3503
- ` --${PAPER_100}: ${p02(s.paper0Dark, "#000000")};`,
3504
- ` --${INK_0}: ${p02(s.ink0Dark, "#ffffff")};`,
3517
+ ` --${PAPER_0}: ${p02(s.paper0Dark, "#000000")};`,
3518
+ ` --${PEN_100}: ${p02(s.pen100Dark, "#ffffff")};`,
3505
3519
  ...brandKindBody(CSS_NEUTRAL, s, "dark", nPage("dark")),
3506
3520
  `}`,
3507
3521
  ...p3Light.length || p3Dark.length ? [
@@ -3590,8 +3604,8 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3590
3604
  const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3591
3605
  const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3592
3606
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
3593
- const lightAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${INK_0}: ${p0hex(nScale.ink0, "#000000")};`];
3594
- const darkAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${INK_0}: ${p0hex(nScale.ink0Dark, "#ffffff")};`];
3607
+ const lightAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${PEN_100}: ${p0hex(nScale.pen100, "#000000")};`];
3608
+ const darkAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${PEN_100}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
3595
3609
  const linkTrio = linkHex ? resolveLinkTrio(linkHex, contrastProfile) : null;
3596
3610
  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)};`] : [
3597
3611
  ` --link: var(--${CSS_BRAND}-${stopTokenName(9)});`,
@@ -3779,17 +3793,17 @@ function themeToFigma(r, input) {
3779
3793
  const build = (mode) => {
3780
3794
  const p02 = mode === "light" ? nScale.paper0 : nScale.paper0Dark;
3781
3795
  const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, neutralExtra(mode));
3782
- const i02 = mode === "light" ? nScale.ink0 : nScale.ink0Dark;
3783
- const spliceInk0 = (g3) => {
3796
+ const i02 = mode === "light" ? nScale.pen100 : nScale.pen100Dark;
3797
+ const splicePen100 = (g3) => {
3784
3798
  if (!i02) return g3;
3785
3799
  const out = {};
3786
3800
  for (const [k, v2] of Object.entries(g3)) {
3787
3801
  out[k] = v2;
3788
- if (k === stopTokenName(11)) out[INK_0] = colorFromStop(i02);
3802
+ if (k === stopTokenName(11)) out[PEN_100] = colorFromStop(i02);
3789
3803
  }
3790
3804
  return out;
3791
3805
  };
3792
- const neutralGroup = spliceInk0(p02 ? { [PAPER_100]: colorFromStop(p02), ...ramp } : ramp);
3806
+ const neutralGroup = splicePen100(p02 ? { [PAPER_0]: colorFromStop(p02), ...ramp } : ramp);
3793
3807
  const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, brandExtra(secondary, mode, CSS_FAMILY.brandSecondary));
3794
3808
  if (input.secondaryStyle === "outline" && input.secondary) {
3795
3809
  const s8 = secondary[mode].find((s) => s.stop === 8);
@@ -3827,12 +3841,12 @@ function themeToFigma(r, input) {
3827
3841
  putLeaf(g3, STAMP_ON, colorFromHex(esc.onFillIsWhite));
3828
3842
  }
3829
3843
  }
3830
- const scaleInkAt = (n) => {
3844
+ const scaleTextAt = (n) => {
3831
3845
  const s = scale[mode].find((x2) => x2.stop === n);
3832
- if (!s) throw new Error(`themeToFigma link: the brand scale has no ink stop ${n}`);
3846
+ if (!s) throw new Error(`themeToFigma link: the brand scale has no pen stop ${n}`);
3833
3847
  return s;
3834
3848
  };
3835
- 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)) };
3849
+ 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)) };
3836
3850
  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) };
3837
3851
  const g2 = {
3838
3852
  brand: brandGroup,
@@ -3948,11 +3962,11 @@ export {
3948
3962
  DEFAULT_LINK_HEX,
3949
3963
  DISABLED_OPACITY,
3950
3964
  EXT_KEY,
3951
- INK_0,
3952
3965
  MODE_SPECS,
3953
3966
  OFFSET_ALPHAS,
3954
3967
  OUTLINE_HOVER_ALPHA,
3955
- PAPER_100,
3968
+ PAPER_0,
3969
+ PEN_100,
3956
3970
  RESOLVER_ID,
3957
3971
  SCALE_STOP_COUNT,
3958
3972
  SCRIM_ALPHA,