okchroma 0.1.7 → 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.
@@ -24,11 +24,11 @@ __export(index_exports, {
24
24
  DEFAULT_LINK_HEX: () => DEFAULT_LINK_HEX,
25
25
  DISABLED_OPACITY: () => DISABLED_OPACITY,
26
26
  EXT_KEY: () => EXT_KEY,
27
- INK_0: () => INK_0,
28
27
  MODE_SPECS: () => MODE_SPECS,
29
28
  OFFSET_ALPHAS: () => OFFSET_ALPHAS,
30
29
  OUTLINE_HOVER_ALPHA: () => OUTLINE_HOVER_ALPHA,
31
- PAPER_100: () => PAPER_100,
30
+ PAPER_0: () => PAPER_0,
31
+ PEN_100: () => PEN_100,
32
32
  RESOLVER_ID: () => RESOLVER_ID,
33
33
  SCALE_STOP_COUNT: () => SCALE_STOP_COUNT,
34
34
  SCRIM_ALPHA: () => SCRIM_ALPHA,
@@ -282,12 +282,12 @@ var SCALE_C_LIGHT = {
282
282
  6: { base: 0.068, sat: 0.85 },
283
283
  7: { base: 0.086, sat: 0.78 },
284
284
  8: { base: 0.142, sat: 0.78 },
285
- 9: { inkMult: 0.95, inkMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
285
+ 9: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
286
286
  // ink-10 keeps the FIRST-text register (ink-9's row, C49): the retired hover law
287
287
  // evaluated ink-9's chroma register at the between L, so the same params reproduce it;
288
288
  // the damped strong register below belongs to the top text stop alone.
289
- 10: { inkMult: 0.95, inkMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
290
- 11: { inkMult: 0.5, inkMaxC: 0.08, chromaFloor: chromaFloorBase(11) }
289
+ 10: { textMult: 0.95, textMaxC: 0.15, chromaFloor: chromaFloorBase(10) },
290
+ 11: { textMult: 0.5, textMaxC: 0.08, chromaFloor: chromaFloorBase(11) }
291
291
  };
292
292
  var SCALE_C_DARK = {
293
293
  0: { sat: 0.4 },
@@ -299,9 +299,9 @@ var SCALE_C_DARK = {
299
299
  6: { sat: 0.76 },
300
300
  7: { sat: 0.8 },
301
301
  8: { sat: 0.84 },
302
- 9: { inkMult: 0.95, inkMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
303
- 10: { inkMult: 0.95, inkMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
304
- 11: { inkMult: 0.62, inkMaxC: 0.045, chromaFloor: chromaFloorBase(11) }
302
+ 9: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
303
+ 10: { textMult: 0.95, textMaxC: 0.12, chromaFloor: chromaFloorBase(10) },
304
+ 11: { textMult: 0.62, textMaxC: 0.045, chromaFloor: chromaFloorBase(11) }
305
305
  };
306
306
  var DARK_CTA_C = {
307
307
  brand: {
@@ -318,10 +318,10 @@ var DARK_CTA_C = {
318
318
  };
319
319
  var STOP_8_NONTEXT_CONTRAST = 3;
320
320
  var DARK_BAND_TOP_LIFT = 1.19;
321
- var INK_9_CONTRAST = 4.5;
322
- var INK_10_CONTRAST = 4.5;
323
- var INK_11_CONTRAST_FLOOR = 7;
324
- var INK_30_GROUND = {
321
+ var PENCIL_9_CONTRAST = 4.5;
322
+ var PEN_10_CONTRAST = 4.5;
323
+ var PEN_11_CONTRAST_FLOOR = 7;
324
+ var PEN_70_GROUND = {
325
325
  wcag: {
326
326
  light: { L: 0.3334966864733433, C: 0.07399966383476321, H: 104.26776675496032 },
327
327
  dark: { L: 0.907688606079988, C: 0.045, H: 293.66431934911327 }
@@ -397,7 +397,7 @@ var NEUTRAL_TINT_POINTS = {
397
397
  [0.95, 0.895],
398
398
  // paper-3 ─┘
399
399
  [0.924, 1],
400
- // wash-4 ─┐ peak: the mid-wash band, the neutral's most-branded moment
400
+ // wash-4 ─┐ peak: the mid-highlighter band, the neutral's most-branded moment
401
401
  [0.892, 1],
402
402
  // wash-5 ─┘
403
403
  [0.852, 0.842],
@@ -405,7 +405,7 @@ var NEUTRAL_TINT_POINTS = {
405
405
  [0.801, 0.653],
406
406
  // wash-7 │
407
407
  [0.738, 0.505],
408
- // wax-74
408
+ // crayon-26
409
409
  [0.6, 0.4],
410
410
  // knee (ex-scaffold) the drop-off: hue fades as you climb
411
411
  [0.56, 0.347],
@@ -431,7 +431,7 @@ var NEUTRAL_TINT_POINTS = {
431
431
  [0.42, 0.653],
432
432
  // wash-7
433
433
  [0.55, 0.505],
434
- // wax-74
434
+ // crayon-26
435
435
  [0.6, 0.4],
436
436
  // knee (ex-scaffold)
437
437
  [0.64, 0.347],
@@ -802,23 +802,23 @@ function perceptualDarkC(L, H2, nativeC, keep = KEEP_DARK, gamut = MASTER_GAMUT)
802
802
 
803
803
  // src/engine/tokenNames.ts
804
804
  var SHARED_NAMES = {
805
- 1: "paper-99",
806
- 2: "paper-97",
805
+ 1: "paper-1",
806
+ 2: "paper-3",
807
807
  // stop 3 renamed wash-3 → paper-3 (owner 2026-07-24, elevation round): it is a
808
808
  // PLANE in both themes (light sink / dark pop), so it belongs to the surface band.
809
- // NAME ONLY — generation is index-keyed and unchanged; the wash-collision machinery
810
- // (collision.ts WASH_STOPS) deliberately still spans stops 3–7. (The requirement
809
+ // NAME ONLY — generation is index-keyed and unchanged; the highlighter-collision machinery
810
+ // (collision.ts HIGHLIGHTER_STOPS) deliberately still spans stops 3–7. (The requirement
811
811
  // spec's `group` label followed the band to 'paper' on 2026-08-31 — labels only,
812
812
  // the spans never moved; see spec.ts groupOf.)
813
813
  // Stage B (owner 2026-08-07, names only): relabeled to the ROOT_L-derived digit,
814
- // paper-95 — same band, same index.
815
- 3: "paper-95",
816
- 4: "wash-92",
817
- 5: "wash-89",
818
- 6: "wash-85",
819
- 7: "wash-80",
820
- 8: "wax-74",
821
- // THE INK BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
814
+ // paper-5 — same band, same index.
815
+ 3: "paper-5",
816
+ 4: "highlighter-8",
817
+ 5: "highlighter-11",
818
+ 6: "highlighter-15",
819
+ 7: "highlighter-20",
820
+ 8: "crayon-26",
821
+ // THE PEN BAND (C49, owner 2026-08-05): the first text stop + emphasis fill (the
822
822
  // 2026-07-29 highlight collapse — the old highlight-9 and on-highlight are
823
823
  // deleted; this stop carries both jobs, its on-color a paper token), the between
824
824
  // text stop (a value the retired text-cta hover state used to generate bespokely,
@@ -827,9 +827,9 @@ var SHARED_NAMES = {
827
827
  // two pre-collapse stop INDICES (11, and the off-scale anchor's 12 — emitted as a
828
828
  // literal in cssRender, not through this table) with the between stop taking the
829
829
  // vacated index 10; the indices (9/10/11) are unchanged since.
830
- 9: "lead-53",
831
- 10: "ink-42",
832
- 11: "ink-30"
830
+ 9: "pencil-47",
831
+ 10: "pen-58",
832
+ 11: "pen-70"
833
833
  };
834
834
  var SCALE_STOP_COUNT = Object.keys(SHARED_NAMES).length;
835
835
  function stopTokenName(stop) {
@@ -837,8 +837,8 @@ function stopTokenName(stop) {
837
837
  if (!name) throw new Error(`stopTokenName: unexpected stop ${stop}`);
838
838
  return name;
839
839
  }
840
- var PAPER_100 = "paper-100";
841
- var INK_0 = "ink-0";
840
+ var PAPER_0 = "paper-0";
841
+ var PEN_100 = "pen-100";
842
842
  var STAMP_FILL = "stamp-fill";
843
843
  var STAMP_FILL_HOVER = "stamp-fill-hover";
844
844
  var STAMP_FILL_PRESSED = "stamp-fill-pressed";
@@ -875,7 +875,7 @@ var SYSTEM_LEAF = {
875
875
  // and the 2026-08-29 inverse ladder): away-from-bg is the page-polarity pole
876
876
  // (black in light, white in dark), toward-bg the flipped pole — STATE LAYERS for
877
877
  // INVERTED grounds, whose polarity is opposite the page's. (Never call toward-bg
878
- // a "wash": that word belongs to the tinted band stops.) The reversal lives in
878
+ // a "highlighter": that word belongs to the tinted band stops.) The reversal lives in
879
879
  // the engine: a consumer on an inverted ground names one token and never picks a
880
880
  // pole. Colored fills never ride these; their states stay engine-solved stops.
881
881
  // Rungs are two-digit percent, harmonizing with shadow-04/08/12.
@@ -891,10 +891,10 @@ var SYSTEM_LEAF = {
891
891
  }
892
892
  };
893
893
  var SURFACE_PLANE_LAW = {
894
- [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_100 },
894
+ [SYSTEM_LEAF.SURFACE.DIM]: { light: SHARED_NAMES[3], dark: PAPER_0 },
895
895
  [SYSTEM_LEAF.SURFACE.LOW]: { light: SHARED_NAMES[2], dark: SHARED_NAMES[1] },
896
896
  [SYSTEM_LEAF.SURFACE.MID]: { light: SHARED_NAMES[1], dark: SHARED_NAMES[2] },
897
- [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_100, dark: SHARED_NAMES[3] }
897
+ [SYSTEM_LEAF.SURFACE.HIGH]: { light: PAPER_0, dark: SHARED_NAMES[3] }
898
898
  };
899
899
  var TOKEN_ORDER = [
900
900
  ...Object.values(SHARED_NAMES),
@@ -910,26 +910,26 @@ function tokenOrder(name) {
910
910
  }
911
911
 
912
912
  // src/engine/requirements/spec.ts
913
- var inkLane = (g2) => g2 === "lead" || g2 === "ink";
914
- var groupOf = (stop) => (stop === 0 ? PAPER_100 : stopTokenName(stop)).split("-")[0];
913
+ var textLane = (g2) => g2 === "pencil" || g2 === "pen" || g2 === "lead" || g2 === "ink";
914
+ var groupOf = (stop) => (stop === 0 ? PAPER_0 : stopTokenName(stop)).split("-")[0];
915
915
  var PAPER0_DARK_ROOT_L = 0.16;
916
916
  var PL_LADDER = { hue: "warm-drift", L: "perceptual", chroma: "ladder" };
917
917
  var PL_TEXT = { hue: "warm-drift", L: "perceptual", chroma: "brand" };
918
918
  var P_LIFT = { hue: "warm-torsion", L: "perceptual-lift", chroma: "ladder" };
919
919
  var P_FIXED = { hue: "warm-torsion", L: "fixed", chroma: "ladder" };
920
920
  var P_TEXT = { hue: "warm-torsion", L: "perceptual", chroma: "brand" };
921
- var S8 = { metric: "wcag", against: "paper-95", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
922
- var T9 = { metric: "wcag", against: "paper-97", target: INK_9_CONTRAST, level: "AA" };
923
- var T10 = { metric: "wcag", against: "wash-80", target: INK_10_CONTRAST, level: "AA" };
924
- var T11 = { metric: "wcag", against: "paper-97", target: INK_11_CONTRAST_FLOOR, level: "AAA" };
921
+ var S8 = { metric: "wcag", against: "paper-5", target: STOP_8_NONTEXT_CONTRAST, level: "AA" };
922
+ var T9 = { metric: "wcag", against: "paper-3", target: PENCIL_9_CONTRAST, level: "AA" };
923
+ var T10 = { metric: "wcag", against: "highlighter-20", target: PEN_10_CONTRAST, level: "AA" };
924
+ var T11 = { metric: "wcag", against: "paper-3", target: PEN_11_CONTRAST_FLOOR, level: "AAA" };
925
925
  var ONS = { onFill: { metric: "apca-pole", enforce: true, ratioFloor: 4.5, coEnforceLc: 65 } };
926
926
  var LIGHT = {
927
927
  stops: [
928
- // paper-100 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
928
+ // paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — in light it genuinely is white (rootL 1.0, zero chroma)
929
929
  { 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 },
930
- // paper/wash/wax-74: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
930
+ // paper/highlighter/crayon-26: perceptual ladder/envelope blend on the geometric ROOT_L_LIGHT scaffold. Separation
931
931
  // falls out of the shape (see above) — no min-separation require. Only stop 8 carries a require: the WCAG
932
- // 3:1 vs the resolved paper-97 (paper-2) (re-solves automatically since it references paper-97 (paper-2)).
932
+ // 3:1 vs the resolved paper-3 (paper-2) (re-solves automatically since it references paper-3 (paper-2)).
933
933
  ...[1, 2, 3, 4, 5, 6, 7, 8].map((stop) => ({
934
934
  stop,
935
935
  rootL: ROOT_L_LIGHT[stop],
@@ -939,12 +939,12 @@ var LIGHT = {
939
939
  baseC: SCALE_C_LIGHT[stop].base,
940
940
  require: stop === 8 ? S8 : void 0
941
941
  })),
942
- // ink text: perceptual + contrast-required. lead-53 (ink-9) is ALSO the emphasis fill (the
943
- // highlight-9 collapse, owner 2026-07-29). ink-42 (ink-10) is the between text stop (C49) —
942
+ // pen text: perceptual + contrast-required. pencil-47 (ink-9) is ALSO the emphasis fill (the
943
+ // highlight-9 collapse, owner 2026-07-29). pen-58 (ink-10) is the between text stop (C49) —
944
944
  // a normal stop like its neighbors; the three together are the text-register cta.
945
- { 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 },
946
- { 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 },
947
- { 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 }
945
+ { 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 },
946
+ { 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 },
947
+ { 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 }
948
948
  ],
949
949
  roles: [
950
950
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: 0, chromaMult: 1 },
@@ -955,10 +955,10 @@ var LIGHT = {
955
955
  };
956
956
  var DARK = {
957
957
  stops: [
958
- // paper-100 (paper-0 pre-Stage-B): the resolved ladder extreme — one seam BELOW paper-99 (paper-1), deep and brand-tinted, never the
958
+ // paper-0 (paper-0 pre-Stage-B): the resolved ladder extreme — one seam BELOW paper-1 (paper-1), deep and brand-tinted, never the
959
959
  // absolute void (the old hard-coded #000000 was "too much"). Lift applies like the rest of the scale.
960
960
  { stop: 0, rootL: PAPER0_DARK_ROOT_L, group: groupOf(0), produce: P_LIFT, satFraction: SCALE_C_DARK[0].sat },
961
- // paper/wash 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
961
+ // paper/highlighter 1–7: perceptual on the dark scaffold. stop 8: FIXED at the hand-placed scaffold BUT with the
962
962
  // 3:1 non-text require DECLARED (the Stage-5 flip, owner-approved) — the blue-recede failure is prevented
963
963
  // BY RULE, not by patch. The require now genuinely PLACES the stop rather than catching a few low-luminance
964
964
  // hues: on the delta-carry path it solves from the sentinel every time, and the old claim that "most hues
@@ -972,11 +972,11 @@ var DARK = {
972
972
  satFraction: SCALE_C_DARK[stop].sat,
973
973
  require: stop === 8 ? S8 : void 0
974
974
  })),
975
- // ink text: perceptual + the contrast requires DECLARED in dark too (Stage-5 flip): the scaffold already
975
+ // pen text: perceptual + the contrast requires DECLARED in dark too (Stage-5 flip): the scaffold already
976
976
  // clears them for every hue (the gate proves it), so values don't move — but the guarantee is now a rule.
977
- { 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 },
978
- { 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 },
979
- { 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 }
977
+ { 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 },
978
+ { 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 },
979
+ { 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 }
980
980
  ],
981
981
  roles: [
982
982
  { role: "cta", produce: { hue: "constant", L: "anchor", chroma: "brand" }, floorL: DARK_CTA_MIN_L, chromaMult: 1 },
@@ -987,7 +987,7 @@ var DARK = {
987
987
  };
988
988
  var MODE_SPECS = { light: LIGHT, dark: DARK };
989
989
 
990
- // ../../../Users/emilygerrity/okchroma/node_modules/helmlab/dist/index.js
990
+ // node_modules/helmlab/dist/index.js
991
991
  function o0(n) {
992
992
  let t = new Float64Array(9);
993
993
  for (let e = 0; e < 3; e++) for (let s = 0; s < 3; s++) t[e * 3 + s] = n[e][s];
@@ -1903,7 +1903,7 @@ function p2Diff(a, b) {
1903
1903
  var DEFAULT_APCA_LC_MAP = { 3: 30, 4.5: 75, 6.5: 85, 7: 90 };
1904
1904
  function toApca(req, lcMap) {
1905
1905
  if (req.metric !== "wcag") return req;
1906
- if (req.against === "wash-80") return { metric: "apca", against: "paper-97", targetLc: 85 };
1906
+ if (req.against === "highlighter-20") return { metric: "apca", against: "paper-3", targetLc: 85 };
1907
1907
  const targetLc = lcMap[req.target];
1908
1908
  if (targetLc === void 0) throw new Error(`apca profile: no Lc mapping for wcag target ${req.target}`);
1909
1909
  return { metric: "apca", against: req.against, targetLc };
@@ -1917,7 +1917,7 @@ function withProfile(spec, profile, lcMap = DEFAULT_APCA_LC_MAP) {
1917
1917
  // (The stop-9 CARVE-OUT is gone with the stop, owner 2026-07-29. It existed because
1918
1918
  // highlight-9's require was a fill-vs-its-own-plane bar with no honest slot in a map
1919
1919
  // of TEXT bars — 3 → 30 non-text, 4.5 → 75 body, 7 → 90 — so the apca lane kept its
1920
- // own hl-9 placement instead. Today's stop 9 is an INK stop: 4.5 is a text bar, the
1920
+ // own hl-9 placement instead. Today's stop 9 is a PEN stop: 4.5 is a text bar, the
1921
1921
  // 75 slot is its correct translation, and it maps like every other require.)
1922
1922
  stops: spec.stops.map((s) => s.require ? { ...s, require: toApca(s.require, lcMap) } : s),
1923
1923
  ons: {
@@ -2079,16 +2079,16 @@ function separationClampLight(ctx, placed, chromaAt, ref, target) {
2079
2079
  return { L: pass, C: ceilingAt(pass), H: ctx.lightHueAt(pass) };
2080
2080
  }
2081
2081
  function placeLightText(ctx, rootL, cMult, maxLFor, deepen, maxC = Infinity) {
2082
- const inkC = Math.min(cMult * ctx.brandC, maxC);
2083
- const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL, inkC, ctx.lightHueAt(rootL)), ctx.lightHueAt(rootL));
2082
+ const textC = Math.min(cMult * ctx.brandC, maxC);
2083
+ const anchorL = perceptualRungL(rootL, clampChromaToGamut(rootL, textC, ctx.lightHueAt(rootL)), ctx.lightHueAt(rootL));
2084
2084
  let H2 = ctx.lightHueAt(anchorL);
2085
- let C2 = clampChromaToGamut(anchorL, inkC, H2);
2085
+ let C2 = clampChromaToGamut(anchorL, textC, H2);
2086
2086
  let L = Math.min(anchorL, maxLFor(C2, H2));
2087
2087
  H2 = ctx.lightHueAt(L);
2088
- C2 = clampChromaToGamut(L, inkC, H2);
2088
+ C2 = clampChromaToGamut(L, textC, H2);
2089
2089
  L = Math.min(anchorL, maxLFor(C2, H2));
2090
2090
  L = Math.min(L - deepen, maxLFor(C2, ctx.lightHueAt(L - deepen)));
2091
- return { L, C: ctx.cAt("light", L, inkC), H: ctx.lightHueAt(L) };
2091
+ return { L, C: ctx.cAt("light", L, textC), H: ctx.lightHueAt(L) };
2092
2092
  }
2093
2093
  function onFillIsWhiteLight(ctx, enforce, ratioFloor) {
2094
2094
  const fill9ApcaY = apcaYAt(ctx.scaleL, clampChromaToGamut(ctx.scaleL, ctx.cAt("light", ctx.scaleL, ctx.brandC), ctx.brandH), ctx.brandH);
@@ -2102,7 +2102,7 @@ function buildDarkContext(ctx, specFloorL = DARK_CTA_MIN_L) {
2102
2102
  return { dark9L, darkC9, darkHueAtL };
2103
2103
  }
2104
2104
  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));
2105
- var darkInkChromaAt = (ctx, dctx, floorBase, multiplier, maxC = Infinity) => (L) => ctx.cAt("dark", L, Math.min(applyChromaFloor(ctx.brandC, multiplier, floorBase, ctx.darkFloorStrength), maxC));
2105
+ var darkTextChromaAt = (ctx, dctx, floorBase, multiplier, maxC = Infinity) => (L) => ctx.cAt("dark", L, Math.min(applyChromaFloor(ctx.brandC, multiplier, floorBase, ctx.darkFloorStrength), maxC));
2106
2106
  function placeDark(dctx, rootL, chromaAt, lift = false) {
2107
2107
  let L = perceptualRungL(rootL, chromaAt(rootL), dctx.darkHueAtL(rootL));
2108
2108
  if (lift) L = Math.max(L, rootL);
@@ -2363,7 +2363,7 @@ function resolveRamp(hex, mode, spec, opts) {
2363
2363
  const seed = { hex, ...o };
2364
2364
  const ctx = buildContext(hex, opts);
2365
2365
  spec ?? (spec = MODE_SPECS[mode]);
2366
- if (ctx.opts?.inkGround) {
2366
+ if (ctx.opts?.textGround) {
2367
2367
  spec = {
2368
2368
  ...spec,
2369
2369
  stops: spec.stops.map((s) => s.stop === 10 && s.require?.metric === "wcag" ? { ...s, require: { ...s.require, target: 6.5 } } : s)
@@ -2385,12 +2385,12 @@ function resolveRamp(hex, mode, spec, opts) {
2385
2385
  if (!ref) throw new Error(`stop ${forWhom}: require against stop ${stopNum} but it is not resolved yet`);
2386
2386
  return ref;
2387
2387
  };
2388
- const AGAINST_STOP = { "paper-99": 1, "paper-97": 2, "paper-95": 3, "wash-80": 7 };
2388
+ const AGAINST_STOP = { "paper-1": 1, "paper-3": 2, "paper-5": 3, "highlighter-20": 7 };
2389
2389
  const declaredAnchor = (req) => req.metric === "min-separation" ? 1 : AGAINST_STOP[req.against] ?? 2;
2390
2390
  const wcagAnchorStop = (req, stop) => stop >= 9 && declaredAnchor(req) <= 3 ? 3 : declaredAnchor(req);
2391
- const inkGround = ctx.opts?.inkGround?.[mode];
2392
- const groundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(wcagAnchorStop(req, stop), stop);
2393
- const apcaGroundOf = (req, stop) => inkGround && stop >= 9 ? inkGround : refOf(declaredAnchor(req), stop);
2391
+ const textGround = ctx.opts?.textGround?.[mode];
2392
+ const groundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(wcagAnchorStop(req, stop), stop);
2393
+ const apcaGroundOf = (req, stop) => textGround && stop >= 9 ? textGround : refOf(declaredAnchor(req), stop);
2394
2394
  const NEUTRAL_P3_WORST_SHIP_Y = { light: 0.845015, dark: 0.014247 };
2395
2395
  const NEUTRAL_W80_WORST_SHIP_Y = { light: 0.506433, dark: 0.074262 };
2396
2396
  const maxLForOf = (req, forWhom, withMargin) => {
@@ -2408,14 +2408,14 @@ function resolveRamp(hex, mode, spec, opts) {
2408
2408
  }
2409
2409
  throw new Error(`stop ${forWhom}: ${req.metric} is not a contrast require`);
2410
2410
  };
2411
- const INK_DEEPEN = 0;
2411
+ const TEXT_DEEPEN = 0;
2412
2412
  for (const sp of spec.stops) {
2413
2413
  let placed;
2414
2414
  let clamped = false, unresolvable;
2415
2415
  if (mode === "light") {
2416
- if (inkLane(sp.group)) {
2417
- if (!sp.require) throw new Error(`light ink stop ${sp.stop} must declare a contrast require`);
2418
- placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), INK_DEEPEN, sp.inkMaxC);
2416
+ if (textLane(sp.group)) {
2417
+ if (!sp.require) throw new Error(`light pen stop ${sp.stop} must declare a contrast require`);
2418
+ placed = placeLightText(ctx, sp.rootL, sp.chromaMult ?? 1, maxLForOf(sp.require, sp.stop, false), TEXT_DEEPEN, sp.textMaxC);
2419
2419
  clamped = true;
2420
2420
  } else if (sp.produce.L === "fixed") {
2421
2421
  const chromaAt = lightScaleChromaAt(ctx, sp.baseC ?? 0, sp.satFraction ?? 1);
@@ -2427,7 +2427,7 @@ function resolveRamp(hex, mode, spec, opts) {
2427
2427
  placed = placeLightScale(ctx, sp.rootL, chromaAt, contrastReq ? maxLForOf(contrastReq, sp.stop, true) : void 0, targetChromaAt);
2428
2428
  clamped = !!contrastReq;
2429
2429
  if (sp.require?.metric === "min-separation") {
2430
- const refStop = sp.require.against === "paper-99" ? 1 : sp.stop - 1;
2430
+ const refStop = sp.require.against === "paper-1" ? 1 : sp.stop - 1;
2431
2431
  const ref = stops.find((s) => s.stop === refStop);
2432
2432
  if (!ref) throw new Error(`stop ${sp.stop}: min-separation against stop ${refStop} but it is not resolved yet`);
2433
2433
  const before = placed.L;
@@ -2437,8 +2437,8 @@ function resolveRamp(hex, mode, spec, opts) {
2437
2437
  }
2438
2438
  } else {
2439
2439
  const d = dctx;
2440
- const inkTwin = inkLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2441
- 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);
2440
+ const textTwin = textLane(sp.group) && ctx.opts?.deltaCarry && ctx.opts?.chromaCurve ? ctx.opts.deltaLightStops?.find((s) => s.stop === sp.stop) : void 0;
2441
+ 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);
2442
2442
  let spineH = null;
2443
2443
  const dl = ctx.opts?.deltaLightStops;
2444
2444
  const ls = dl && sp.stop >= 1 && sp.stop <= 8 ? dl.find((s) => s.stop === sp.stop) : void 0;
@@ -2458,7 +2458,7 @@ function resolveRamp(hex, mode, spec, opts) {
2458
2458
  }
2459
2459
  if (ctx.opts.deltaHKPlace) L = perceptualRungL(sp.rootL, ls.C, ls.H);
2460
2460
  if (ctx.opts.deltaLiftFloor) L = Math.max(L, sp.rootL);
2461
- if (ctx.opts.deltaChromaEq && !inkLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2461
+ if (ctx.opts.deltaChromaEq && !textLane(sp.group)) C2 = ctx.cAt("dark", L, perceptualDarkC(L, ls.H, ctx.brandC));
2462
2462
  placed = { L, C: C2, H: spineH ?? ls.H };
2463
2463
  } else if (sp.produce.L === "fixed") {
2464
2464
  placed = ls ? placeDarkDelta(d, sp.rootL, chromaAt, ls) : { L: sp.rootL, C: chromaAt(sp.rootL), H: d.darkHueAtL(sp.rootL) };
@@ -2494,7 +2494,7 @@ function resolveRamp(hex, mode, spec, opts) {
2494
2494
  if (sp.require?.metric === "wcag") {
2495
2495
  const anchor = groundOf(sp.require, sp.stop);
2496
2496
  const anchorShipY = shippedY(anchor.L, anchor.C, anchor.H);
2497
- const paperBound = sp.stop >= 8 && !(inkGround && sp.stop >= 9);
2497
+ const paperBound = sp.stop >= 8 && !(textGround && sp.stop >= 9);
2498
2498
  const crossBoundY = wcagAnchorStop(sp.require, sp.stop) === 7 ? NEUTRAL_W80_WORST_SHIP_Y[mode] : NEUTRAL_P3_WORST_SHIP_Y[mode];
2499
2499
  const shipRatio = (L) => {
2500
2500
  const y2 = shippedY(L, placed.C, placed.H);
@@ -2522,7 +2522,7 @@ function resolveRamp(hex, mode, spec, opts) {
2522
2522
  const got = Math.abs(apcaLc(apcaYAt(placed.L, clampChromaToGamut(placed.L, placed.C, placed.H), placed.H), refA));
2523
2523
  if (got < sp.require.targetLc - APCA_TOL_LC) unresolvable = `stop ${sp.stop}: |Lc| ${got.toFixed(1)} < required ${sp.require.targetLc}`;
2524
2524
  } else if (sp.require?.metric === "min-separation") {
2525
- const ref = stops.find((s) => s.stop === (sp.require.against === "paper-99" ? 1 : sp.stop - 1));
2525
+ const ref = stops.find((s) => s.stop === (sp.require.against === "paper-1" ? 1 : sp.stop - 1));
2526
2526
  const rad2 = (h) => h * Math.PI / 180;
2527
2527
  const gC = clampChromaToGamut(placed.L, placed.C, placed.H);
2528
2528
  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);
@@ -2678,7 +2678,7 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2678
2678
  const dark = darkRamp.stops.filter((s) => s.stop >= 1).map(toStop).sort((a, b) => a.stop - b.stop);
2679
2679
  const p0Light = lightRamp.stops.find((s) => s.stop === 0);
2680
2680
  const p0Dark = darkRamp.stops.find((s) => s.stop === 0);
2681
- const inkPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2681
+ const penPole = (arr, L) => arr.find((s) => s.stop === 11) ? makeStop(12, L, 0, arr.find((s) => s.stop === 11).H) : void 0;
2682
2682
  const cta = makeStop(9, lightRamp.roles.cta.L, lightRamp.roles.cta.C, lightRamp.roles.cta.H);
2683
2683
  const ctaDark = makeStop(9, darkRamp.roles.cta.L, darkRamp.roles.cta.C, darkRamp.roles.cta.H);
2684
2684
  const roleStop = (r, stop) => makeStop(stop, r.L, r.C, r.H);
@@ -2702,8 +2702,8 @@ function generateScale(hex, scaleName, forcedArchetype, opts) {
2702
2702
  identityHex: hex.toUpperCase(),
2703
2703
  paper0: p0Light ? makeStop(0, p0Light.L, p0Light.C, p0Light.H) : void 0,
2704
2704
  paper0Dark: p0Dark ? makeStop(0, p0Dark.L, p0Dark.C, p0Dark.H) : void 0,
2705
- ink0: inkPole(light, 0),
2706
- ink0Dark: inkPole(dark, 1)
2705
+ pen100: penPole(light, 0),
2706
+ pen100Dark: penPole(dark, 1)
2707
2707
  };
2708
2708
  }
2709
2709
  function generateNeutralScale(brandH, level = "default", contrastProfile) {
@@ -2775,8 +2775,7 @@ function generateSubtleSecondary(hex, opts) {
2775
2775
 
2776
2776
  // src/engine/tokenDescriptions.ts
2777
2777
  var AA_LARGE = "AA large text and UI elements";
2778
- var AA_BODY = "AA standard body text & Level AAA large text";
2779
- var AAA_BODY = "AAA standard body text";
2778
+ var AA_BODY = "AA standard body text";
2780
2779
  var FAMILY = {
2781
2780
  neutral: "neutral",
2782
2781
  brandPrimary: "brand",
@@ -2812,7 +2811,7 @@ var COLOR_WORD = {
2812
2811
  [FAMILY.info]: " (blue)"
2813
2812
  };
2814
2813
  var PAPER = { req: "backgrounds, inverted text", theming: (f) => TINT[f], collides: true };
2815
- var WASH = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2814
+ var HIGHLIGHTER = { req: "subtle interactive states, decorative borders, illos, signal hierarchy", theming: (f) => TINT[f], collides: true };
2816
2815
  var solved = (f) => `${TINT[f]}; re-solved to clear its floor`;
2817
2816
  var OVERLAY = {
2818
2817
  req: "translucent backgrounds that hold their reading on any paper",
@@ -2820,25 +2819,25 @@ var OVERLAY = {
2820
2819
  collides: true
2821
2820
  };
2822
2821
  var SCALE = {
2823
- "paper-99": PAPER,
2822
+ "paper-1": PAPER,
2824
2823
  "paper-99-overlay": OVERLAY,
2825
- "paper-97": PAPER,
2824
+ "paper-3": PAPER,
2826
2825
  "paper-97-overlay": OVERLAY,
2827
- "paper-95": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2826
+ "paper-5": { req: "backgrounds, inverted text", theming: (f) => `${TINT[f]}. Worst background text stops must clear.`, collides: true },
2828
2827
  "paper-95-overlay": OVERLAY,
2829
- "wash-92": WASH,
2830
- "wash-89": WASH,
2831
- "wash-85": WASH,
2832
- "wash-80": WASH,
2828
+ "highlighter-8": HIGHLIGHTER,
2829
+ "highlighter-11": HIGHLIGHTER,
2830
+ "highlighter-15": HIGHLIGHTER,
2831
+ "highlighter-20": HIGHLIGHTER,
2833
2832
  // ("icons" KEPT through the 2026-08-28 on-flood strip (owner): an "icon" query
2834
- // landing on the wax rows is worth its ic-ON-s noise — the one surviving body carrier)
2835
- "wax-74": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2836
- "lead-53": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2837
- "ink-42": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2833
+ // landing on the crayon rows is worth its ic-ON-s noise — the one surviving body carrier)
2834
+ "crayon-26": { req: "focus rings, icons, large text", contrast: AA_LARGE, theming: solved, collides: true },
2835
+ "pencil-47": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2836
+ "pen-58": { req: "regular text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2838
2837
  // ("high-emphasis" reworded 2026-08-12: the surface planes took low/high as label
2839
2838
  // words, and a body carrying either floods that token's picker search — the C50 law;
2840
2839
  // "strong-emphasis" reworded 2026-08-28: str-ON-g fed the "on" flood)
2841
- "ink-30": { req: "heavy-emphasis text, inverted backgrounds", contrast: AAA_BODY, theming: solved, collides: true },
2840
+ "pen-70": { req: "heavy-emphasis text, inverted backgrounds", contrast: AA_BODY, theming: solved, collides: true },
2842
2841
  // the stamp family (cta words → solid 2026-08-18, solid → stamp 2026-08-27). "CTA" stays in
2843
2842
  // these bodies on purpose: it stopped being a token label, so it floods nothing,
2844
2843
  // and a designer's "cta" query still lands on these rows.
@@ -2867,7 +2866,7 @@ var LINK_INVERSE = (state, contrast) => ({
2867
2866
  var SYSTEM = {
2868
2867
  "system/abs-black": ABS,
2869
2868
  "system/abs-white": ABS,
2870
- // (the community system/ink-0 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2869
+ // (the community system/pen-100 row RETIRED 2026-08-28 with its STATIC_UTILS entry:
2871
2870
  // the anchor is engine-resolved and rides the neutral in BOTH plugins now —
2872
2871
  // NEUTRAL_ONLY above. Old files' static rows orphan unwritten.)
2873
2872
  "system/abs-primary": { req: "identity seed reference", theming: "the theme\u2019s own input, as given" },
@@ -2883,7 +2882,7 @@ var SYSTEM = {
2883
2882
  "system/alpha/away-from-bg/08": OFFSET,
2884
2883
  "system/alpha/away-from-bg/16": OFFSET,
2885
2884
  // the inverse ladder: the offset rungs with the pole flipped per mode, for
2886
- // washes and edges over inverted backgrounds ("interaction" avoided in the
2885
+ // highlighters and edges over inverted backgrounds ("interaction" avoided in the
2887
2886
  // bodies: acti-ON would feed the "on" flood)
2888
2887
  "system/alpha/toward-bg/06": OFFSET_INVERSE,
2889
2888
  "system/alpha/toward-bg/08": OFFSET_INVERSE,
@@ -2900,10 +2899,10 @@ var SYSTEM = {
2900
2899
  "system/surface/high": PLANE("topmost plane \u2014 modals, dialogs"),
2901
2900
  "system/link/default/enabled": LINK("", AA_BODY),
2902
2901
  "system/link/default/hover": LINK(" pointer-over", AA_BODY),
2903
- "system/link/default/pressed": LINK(" pressed", AAA_BODY),
2902
+ "system/link/default/pressed": LINK(" pressed", AA_BODY),
2904
2903
  "system/link/inverse/enabled": LINK_INVERSE("", AA_BODY),
2905
2904
  "system/link/inverse/hover": LINK_INVERSE(" pointer-over", AA_BODY),
2906
- "system/link/inverse/pressed": LINK_INVERSE(" pressed", AAA_BODY)
2905
+ "system/link/inverse/pressed": LINK_INVERSE(" pressed", AA_BODY)
2907
2906
  };
2908
2907
  var PREFIXES = [
2909
2908
  // community spellings FIRST: the 2026-08-21 family rename made the ext word a
@@ -2950,15 +2949,15 @@ function checkCollision(brand, signalScale, signalDef, mode) {
2950
2949
  collides: hueGate && deltaE <= threshold
2951
2950
  };
2952
2951
  }
2953
- var HUE_COLLISION_WASH_DEG = 15;
2952
+ var HUE_COLLISION_HIGHLIGHTER_DEG = 15;
2954
2953
  var HUE_COLLISION_MIN_V = 0.5;
2955
2954
  var SECONDARY_NOTE_MIN_V = HUE_NOISE_C / VIVID_C;
2956
- var WASH_STOPS = [3, 4, 5, 6, 7];
2957
- function washProximity(brand, sig, mode) {
2955
+ var HIGHLIGHTER_STOPS = [3, 4, 5, 6, 7];
2956
+ function highlighterProximity(brand, sig, mode) {
2958
2957
  const b = mode === "light" ? brand.light : brand.dark;
2959
2958
  const s = mode === "light" ? sig.light : sig.dark;
2960
2959
  let dH = Infinity, dE = Infinity;
2961
- for (const k of WASH_STOPS) {
2960
+ for (const k of HIGHLIGHTER_STOPS) {
2962
2961
  const bs = b.find((x2) => x2.stop === k), ss = s.find((x2) => x2.stop === k);
2963
2962
  if (!bs || !ss) continue;
2964
2963
  dH = Math.min(dH, hueDistance(bs.H, ss.H));
@@ -2967,15 +2966,15 @@ function washProximity(brand, sig, mode) {
2967
2966
  return { dH, dE };
2968
2967
  }
2969
2968
  function checkHueCollision(brand, signalScale, signalDef, opts) {
2970
- const l = washProximity(brand, signalScale, "light");
2971
- const d = washProximity(brand, signalScale, "dark");
2969
+ const l = highlighterProximity(brand, signalScale, "light");
2970
+ const d = highlighterProximity(brand, signalScale, "dark");
2972
2971
  const vividness = Math.min(1, brand.brandC / VIVID_C);
2973
2972
  return {
2974
2973
  signal: signalDef.name,
2975
- dHueWash: { light: l.dH, dark: d.dH },
2976
- washDeltaE: { light: l.dE, dark: d.dE },
2974
+ dHueHighlighter: { light: l.dH, dark: d.dH },
2975
+ highlighterDeltaE: { light: l.dE, dark: d.dE },
2977
2976
  vividness,
2978
- collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_WASH_DEG
2977
+ collides: vividness >= (opts?.minV ?? HUE_COLLISION_MIN_V) && Math.min(l.dH, d.dH) <= HUE_COLLISION_HIGHLIGHTER_DEG
2979
2978
  };
2980
2979
  }
2981
2980
  var YELLOW_SPLIT_H = 96;
@@ -3168,7 +3167,7 @@ function resolveBrand(hex, name, opts) {
3168
3167
  // (buildSignalScales/signalShift) — neutral alone stays outside; exact has enforce off.
3169
3168
  apcaClearance: opts?.apcaClearance ?? true,
3170
3169
  darkCtaFlatApp: opts?.darkCtaFlatApp,
3171
- inkGround: opts?.inkGround
3170
+ textGround: opts?.textGround
3172
3171
  };
3173
3172
  let scale = generateScale(hex, name, void 0, floor);
3174
3173
  let pending = [];
@@ -3251,7 +3250,7 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3251
3250
  const s = resolveBrand(linkHex, "link", { skipCollisionRules: true, contrastProfile }).scale;
3252
3251
  const at = (arr, n) => {
3253
3252
  const st = arr.find((x2) => x2.stop === n);
3254
- if (!st) throw new Error(`resolveLinkTrio: the link resolve has no ink stop ${n}`);
3253
+ if (!st) throw new Error(`resolveLinkTrio: the link resolve has no pen stop ${n}`);
3255
3254
  return st;
3256
3255
  };
3257
3256
  return {
@@ -3264,16 +3263,16 @@ function resolveLinkTrio(linkHex, contrastProfile) {
3264
3263
  };
3265
3264
  }
3266
3265
  function resolveLinkInverseTrio(seedHex, contrastProfile) {
3267
- const ground = INK_30_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3266
+ const ground = PEN_70_GROUND[contrastProfile === "apca" ? "apca" : "wcag"];
3268
3267
  const s = resolveBrand(seedHex, "link-inverse", {
3269
3268
  skipCollisionRules: true,
3270
3269
  contrastProfile,
3271
3270
  // the cross — ramp keyed, ground mode-swapped
3272
- inkGround: { light: ground.dark, dark: ground.light }
3271
+ textGround: { light: ground.dark, dark: ground.light }
3273
3272
  }).scale;
3274
3273
  const at = (arr, n) => {
3275
3274
  const st = arr.find((x2) => x2.stop === n);
3276
- if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no ink stop ${n}`);
3275
+ if (!st) throw new Error(`resolveLinkInverseTrio: the link resolve has no pen stop ${n}`);
3277
3276
  return st;
3278
3277
  };
3279
3278
  return {
@@ -3287,12 +3286,12 @@ function resolveLinkInverseTrio(seedHex, contrastProfile) {
3287
3286
  };
3288
3287
  }
3289
3288
  function escapeCtaFamily(nScale, mode, contrastProfile) {
3290
- const strongInk = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3291
- if (!strongInk) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3292
- const mk = (stop, L) => makeStop(stop, L, strongInk.C, strongInk.H);
3293
- const cta = mk(9, strongInk.L);
3294
- const ctaHover = mk(10, stateFillL(strongInk.L, mode, 1));
3295
- const ctaPressed = mk(11, stateFillL(strongInk.L, mode, 2));
3289
+ const strongText = (mode === "light" ? nScale.light : nScale.dark).find((s) => s.stop === 11);
3290
+ if (!strongText) throw new Error("escapeCtaFamily: the neutral scale has no ink-11 stop");
3291
+ const mk = (stop, L) => makeStop(stop, L, strongText.C, strongText.H);
3292
+ const cta = mk(9, strongText.L);
3293
+ const ctaHover = mk(10, stateFillL(strongText.L, mode, 1));
3294
+ const ctaPressed = mk(11, stateFillL(strongText.L, mode, 2));
3296
3295
  const onEnforce = contrastProfile !== "apca";
3297
3296
  const onFloor = contrastProfile === "apca" ? void 0 : 4.5;
3298
3297
  const onFillIsWhite = onTextIsWhite(apcaY(cta.r, cta.g, cta.b), cta.L, cta.C, cta.H, onEnforce, onFloor);
@@ -3368,7 +3367,7 @@ function resolveTheme(input) {
3368
3367
  for (const def of SIGNALS2) {
3369
3368
  if (adoptedForSecondary.has(def.name)) continue;
3370
3369
  const h = checkHueCollision(scale2, effectiveOf(def.name), def, { minV: SECONDARY_NOTE_MIN_V });
3371
- if (h.collides) out.push(wording(def.name, Math.min(h.washDeltaE.light, h.washDeltaE.dark)));
3370
+ if (h.collides) out.push(wording(def.name, Math.min(h.highlighterDeltaE.light, h.highlighterDeltaE.dark)));
3372
3371
  }
3373
3372
  return out;
3374
3373
  };
@@ -3418,7 +3417,7 @@ function resolveTheme(input) {
3418
3417
  derived: true,
3419
3418
  notes: [
3420
3419
  `secondary derived from the brand color (default model, seed ${liftedHex})`,
3421
- ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3420
+ ...signalNotesFor(scale2, (name, dE) => `derived secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks the brand color`)
3422
3421
  ],
3423
3422
  distinctness: ctaDistinctness(primary.scale, scale2)
3424
3423
  },
@@ -3443,7 +3442,7 @@ function resolveTheme(input) {
3443
3442
  derived: false,
3444
3443
  notes: [
3445
3444
  `secondary keeps your color through the ramp; the cta is a tint of it (tint seed ${tintedHex})`,
3446
- ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (wash \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3445
+ ...signalNotesFor(scale2, (name, dE) => `secondary sits on the ${name} signal's hue (highlighter \u0394E ${dE.toFixed(3)}) \u2014 it tracks your color`)
3447
3446
  ],
3448
3447
  distinctness: distinctness2
3449
3448
  },
@@ -3455,7 +3454,7 @@ function resolveTheme(input) {
3455
3454
  const scale = rSec.scale;
3456
3455
  const level = "standard";
3457
3456
  mergeSecondarySignals(scale, input.secondaryHex);
3458
- const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (wash \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3457
+ const secNotes = signalNotesFor(scale, (name, dE) => `secondary reads close to the ${name} signal (highlighter \u0394E ${dE.toFixed(3)}) \u2014 your color ships untouched`);
3459
3458
  const distinctness = ctaDistinctness(primary.scale, scale);
3460
3459
  if (distinctness.close)
3461
3460
  notes.push(`secondary reads close to the primary (\u0394E ${Math.min(distinctness.light, distinctness.dark).toFixed(2)}) \u2014 consider a more distinct color`);
@@ -3514,7 +3513,7 @@ var alphaRootVars = (mode) => [
3514
3513
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` ${offsetVarName(r)}: ${offsetRgba(r, mode)};`),
3515
3514
  // the toward-bg ladder (the 2026-08-29 inverse ladder, renamed 2026-08-31): the
3516
3515
  // same rungs, pole flipped per mode — state layers for INVERTED grounds (never
3517
- // called a "wash" — that word is the tinted band's). One spelling with the Figma
3516
+ // called a "highlighter" — that word is the tinted band's). One spelling with the Figma
3518
3517
  // row: --alpha-toward-bg-06 ↔ system/alpha/toward-bg/06.
3519
3518
  ...Object.keys(OFFSET_ALPHAS).map(Number).sort((a, b) => a - b).map((r) => ` --alpha-toward-bg-${offsetLeafName(r)}: ${offsetRgba(r, mode === "light" ? "dark" : "light")};`)
3520
3519
  ];
@@ -3541,7 +3540,7 @@ function brandKindBody(prefix, s, mode, page) {
3541
3540
  ` --${prefix}-stamp-fill-pressed: ${stopHex(f.ctaPressed)};`,
3542
3541
  ` --${prefix}-stamp-edge: var(${border ? offsetVarName(ctaBorderRung(prefix)) : TRANSPARENT_VAR});`,
3543
3542
  // the SOFT on-cta (owner 2026-08-04: "neutral cta on's should also be the alpha"): the
3544
- // neutral's cta is the scale-fed WASH-level fill, the system's other quiet cta, so its
3543
+ // neutral's cta is the scale-fed HIGHLIGHTER-level fill, the system's other quiet cta, so its
3545
3544
  // button text takes the pole AT ALPHA like the default-model secondary's — composited
3546
3545
  // over whatever state the fill is in, so hover/pressed carry their own legibility. Same
3547
3546
  // register both families (SOFT_ON_CTA_ALPHA), so both alias the ONE system/alpha/ink
@@ -3571,16 +3570,16 @@ function neutralCss(selector, brandH, level = "default", contrastProfile, ctaBor
3571
3570
  const p3Dark = brandKindP3Body(CSS_NEUTRAL, s, "dark");
3572
3571
  return [
3573
3572
  `${selector} {`,
3574
- ` --${PAPER_100}: ${p02(s.paper0, "#ffffff")};`,
3575
- ` --${INK_0}: ${p02(s.ink0, "#000000")};`,
3573
+ ` --${PAPER_0}: ${p02(s.paper0, "#ffffff")};`,
3574
+ ` --${PEN_100}: ${p02(s.pen100, "#000000")};`,
3576
3575
  // the neutral IS the page, so it is judged against its own paper stop
3577
3576
  // the neutral's overlays never read the visibility bar (the exempt family), so
3578
3577
  // the brandless chrome context passes 0
3579
3578
  ...brandKindBody(CSS_NEUTRAL, s, "light", nPage("light")),
3580
3579
  `}`,
3581
3580
  `${selector}[data-theme="dark"] {`,
3582
- ` --${PAPER_100}: ${p02(s.paper0Dark, "#000000")};`,
3583
- ` --${INK_0}: ${p02(s.ink0Dark, "#ffffff")};`,
3581
+ ` --${PAPER_0}: ${p02(s.paper0Dark, "#000000")};`,
3582
+ ` --${PEN_100}: ${p02(s.pen100Dark, "#ffffff")};`,
3584
3583
  ...brandKindBody(CSS_NEUTRAL, s, "dark", nPage("dark")),
3585
3584
  `}`,
3586
3585
  ...p3Light.length || p3Dark.length ? [
@@ -3669,8 +3668,8 @@ function brandCss(slug, displayName, r, secondary, noteSuffix = "", neutralLevel
3669
3668
  const brandIdentity = ` --${CSS_BRAND}-identity: ${scale.identityHex};`;
3670
3669
  const secondaryIdentity = secondary ? ` --${CSS_SECONDARY}-identity: ${secondary.identityHex};` : ` --${CSS_SECONDARY}-identity: var(--${CSS_BRAND}-identity);`;
3671
3670
  const p0hex = (s, fallback) => s ? stopHex(s) : fallback;
3672
- const lightAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${INK_0}: ${p0hex(nScale.ink0, "#000000")};`];
3673
- const darkAnchors = [` --${PAPER_100}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${INK_0}: ${p0hex(nScale.ink0Dark, "#ffffff")};`];
3671
+ const lightAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0, "#ffffff")};`, ` --${PEN_100}: ${p0hex(nScale.pen100, "#000000")};`];
3672
+ const darkAnchors = [` --${PAPER_0}: ${p0hex(nScale.paper0Dark, "#000000")};`, ` --${PEN_100}: ${p0hex(nScale.pen100Dark, "#ffffff")};`];
3674
3673
  const linkTrio = linkHex ? resolveLinkTrio(linkHex, contrastProfile) : null;
3675
3674
  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)};`] : [
3676
3675
  ` --link: var(--${CSS_BRAND}-${stopTokenName(9)});`,
@@ -3858,17 +3857,17 @@ function themeToFigma(r, input) {
3858
3857
  const build = (mode) => {
3859
3858
  const p02 = mode === "light" ? nScale.paper0 : nScale.paper0Dark;
3860
3859
  const ramp = rampGroup(nScale[mode], mode === "light" ? nScale.onFillTextIsWhite : nScale.onFillTextIsWhiteDark, neutralExtra(mode));
3861
- const i02 = mode === "light" ? nScale.ink0 : nScale.ink0Dark;
3862
- const spliceInk0 = (g3) => {
3860
+ const i02 = mode === "light" ? nScale.pen100 : nScale.pen100Dark;
3861
+ const splicePen100 = (g3) => {
3863
3862
  if (!i02) return g3;
3864
3863
  const out = {};
3865
3864
  for (const [k, v2] of Object.entries(g3)) {
3866
3865
  out[k] = v2;
3867
- if (k === stopTokenName(11)) out[INK_0] = colorFromStop(i02);
3866
+ if (k === stopTokenName(11)) out[PEN_100] = colorFromStop(i02);
3868
3867
  }
3869
3868
  return out;
3870
3869
  };
3871
- const neutralGroup = spliceInk0(p02 ? { [PAPER_100]: colorFromStop(p02), ...ramp } : ramp);
3870
+ const neutralGroup = splicePen100(p02 ? { [PAPER_0]: colorFromStop(p02), ...ramp } : ramp);
3872
3871
  const secondaryGroup = rampGroup(secondary[mode], mode === "light" ? secondaryOnFillLight : secondaryOnFillDark, brandExtra(secondary, mode, CSS_FAMILY.brandSecondary));
3873
3872
  if (input.secondaryStyle === "outline" && input.secondary) {
3874
3873
  const s8 = secondary[mode].find((s) => s.stop === 8);
@@ -3906,12 +3905,12 @@ function themeToFigma(r, input) {
3906
3905
  putLeaf(g3, STAMP_ON, colorFromHex(esc.onFillIsWhite));
3907
3906
  }
3908
3907
  }
3909
- const scaleInkAt = (n) => {
3908
+ const scaleTextAt = (n) => {
3910
3909
  const s = scale[mode].find((x2) => x2.stop === n);
3911
- if (!s) throw new Error(`themeToFigma link: the brand scale has no ink stop ${n}`);
3910
+ if (!s) throw new Error(`themeToFigma link: the brand scale has no pen stop ${n}`);
3912
3911
  return s;
3913
3912
  };
3914
- 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)) };
3913
+ 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)) };
3915
3914
  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) };
3916
3915
  const g2 = {
3917
3916
  brand: brandGroup,