@cloudgrid-io/ui 0.27.2 → 0.29.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.
package/dist/tokens.css CHANGED
@@ -443,10 +443,230 @@
443
443
  value held on white, so the ground moved and the dot kept its contrast. The
444
444
  muted text rung comes with it, 3.27:1 -> 3.69:1 on white. */
445
445
  --cg-neutral-500: #858585;
446
+ /*
447
+ SOURCE: derived, 2026-09-18 (#232). The missing rung between neutral-500 and
448
+ neutral-700, and the only reason it exists is that --cg-text-muted has no value
449
+ on this ramp that clears the 4.5:1 body-text floor.
450
+
451
+ neutral-500 measures 3.69:1 on the card, 3.28:1 on the page ground and 3.29:1 on
452
+ the lavender wash. All three clear 3:1 for large text and none clears 4.5:1, so
453
+ every caption, every eyebrow and every tile hint in this library is under AA for
454
+ body copy. neutral-700 clears it everywhere (7.00:1 on the card) but it IS
455
+ --cg-text-secondary, so aliasing a "strong muted" onto it would not add a rung,
456
+ it would rename one.
457
+
458
+ Derived, with the sweep, so the next person does not repeat it:
459
+
460
+ #6e6e6e on the page ground : 4.53:1 clears by 0.03 — a margin a later
461
+ ground retune would erase in silence
462
+ #6c6c6c : 4.66:1
463
+ #6a6a6a : 4.80:1 <-- this
464
+ #686868 : 4.95:1
465
+
466
+ #6a6a6a on the card #ffffff : 5.41:1
467
+ on the page #f1f1f6 : 4.80:1
468
+ on raised #f4f6f8 : 4.99:1
469
+ on subtle #f2f1f7 : 4.82:1
470
+
471
+ The extra step past the first passing value is --cg-accent's practice, and for
472
+ its reason: 4.53:1 is a number that a page-ground retune turns into a failure
473
+ with nothing going red.
474
+
475
+ KNOWN, and not fixed here: on the violet-200 wash the frames draw (#d9d4fb) this
476
+ measures 3.80:1, still under the body floor. Nothing on a neutral ramp clears
477
+ 4.5:1 on that ground and stays muted; see --cg-violet-200's own note. */
478
+ --cg-neutral-600: #6a6a6a;
446
479
  /* SOURCE: legal / secondary text */
447
480
  --cg-neutral-700: #595959;
448
481
  /* SOURCE: ink */
449
482
  --cg-neutral-900: #222222;
483
+ /*
484
+ SOURCE: Figma variable color/neutral/300, read first-hand on the thegrid frame
485
+ (8364:4396, "On the grid & to the world" on the ink band).
486
+
487
+ THE NAME IS NOT AN ACCIDENT. Figma calls this family color/neutral/300 / 500 /
488
+ 600. This package's neutral ramp is already occupied at every one of those
489
+ numbers by a PURE grey (#acacac, #858585, #595959) that two published consumers
490
+ ship today, and these three are blue-greys: #828c98 is B152 against R130, which
491
+ is not a grey at all. Taking the Figma numbers would mean either changing three
492
+ shipped values (breaking) or having one ramp hold two hue families under one
493
+ name (silent). So the cool family takes its own root and the greys keep theirs.
494
+
495
+ 1.45:1 on the card and 1.29:1 on the page ground: this is a value for text on a
496
+ dark ground, never on a light one. */
497
+ --cg-slate-300: #d2d7de;
498
+ /*
499
+ SOURCE: Figma variable color/neutral/500, also carried under the legacy name
500
+ "N3 new". Read first-hand on four of the six canonical frames — thegrid
501
+ (8364:3966, footer links), teams (7172:7607), pricing (7779:9284), builders
502
+ (6916:8104, the terminal's "$"). It is the design's quiet-text tone and the most
503
+ frequently missing value in this package (ui#239).
504
+
505
+ WHAT IT MEASURES, because the frames spend it on words:
506
+
507
+ on the card #ffffff : 3.41:1
508
+ on the page ground #f1f1f6 : 3.03:1
509
+ on raised #f4f6f8 : 3.15:1
510
+ on the violet wash #d9d4fb : 2.40:1 <-- under even the 3:1 indicator bar
511
+ on ink #222222 : 4.66:1
512
+ on the purple panel #5a47f0 : 1.71:1 invisible
513
+
514
+ So it clears 3:1 for large text on the three light grounds, clears 4.5:1 on ink
515
+ alone, and fails everywhere the frames actually use it for 15px footer links.
516
+ It is shipped because the frames use it and this package is meant to be able to
517
+ draw them; it is deliberately NOT wired to a --cg-text-* semantic, because those
518
+ carry a contrast contract and this value cannot meet it. --cg-slate-600 is the
519
+ companion that can. See ui#232 for the same shape of problem on --cg-text-muted.
520
+
521
+ DECLARED AT :root ONLY. A brand ramp rung is not a semantic and does not flip. */
522
+ --cg-slate-500: #828c98;
523
+ /*
524
+ SOURCE: Figma variable color/neutral/600, read first-hand on thegrid
525
+ (8364:4327, the stats rows; 8364:4255, "your CREATIONS").
526
+
527
+ This is the accessible companion to --cg-slate-500 and the design supplied it
528
+ itself, which is why nothing had to be invented:
529
+
530
+ on the card #ffffff : 5.65:1
531
+ on the page ground #f1f1f6 : 5.02:1
532
+ on raised #f4f6f8 : 5.21:1
533
+ on subtle #f2f1f7 : 5.03:1
534
+ on the cyan tint #dffbfc : 5.20:1
535
+ on the violet wash #d9d4fb : 3.97:1 large text only
536
+ on the lime #b6de68 : 3.67:1 large text only
537
+ on ink #222222 : 2.82:1 do not
538
+ on the purple panel #5a47f0 : 1.03:1 invisible
539
+
540
+ So: safe for body copy on any of the light page and card grounds, and on the
541
+ cyan tint. NOT safe on the violet wash, on the lime, or on either dark surface.
542
+ Where the frames put slate-500 on a light ground and mean it as running text,
543
+ this is the value that keeps the design and passes. */
544
+ --cg-slate-600: #5f6874;
545
+ /*
546
+ SOURCE: Figma variable "stone", declared on the home frame (8204:2397). ui#239
547
+ asks for it by name.
548
+
549
+ HONEST PROVENANCE, because this is the one value here that is weaker than the
550
+ rest: it is a DECLARATION. The home frame declares it as a variable, and the
551
+ Figma quota ran out before the audit could read the ~90 nodes of that frame that
552
+ might paint with it. No node on any of the other five frames uses it. So its
553
+ role is recorded as unknown rather than guessed, and nothing in this package
554
+ consumes it yet.
555
+
556
+ It is a warm near-black, not a grey: R52 G51 B48 against --cg-ink's flat #222222.
557
+ 12.63:1 on the card and 11.22:1 on the page ground, so wherever it lands it is a
558
+ text-or-ink value and not a surface tint.
559
+
560
+ The rung number is by lightness against this package's own ramp, which ends at
561
+ neutral-900 #222222. Figma gives it no number. */
562
+ --cg-stone-800: #343330;
563
+
564
+ /* ---- hue-ramps ----
565
+ The three brand hues as RAMPS, not as single values (#239, 2026-09-18).
566
+
567
+ Until now this package carried one violet, one lime and one cyan, plus a single
568
+ --cg-primary-tint. The six canonical Figma frames carry a full scale of each,
569
+ declared as Figma variables under color/violet/*, color/lime/* and color/cyan/*,
570
+ and they spend those rungs on real surfaces: chips, role badges, avatar strokes,
571
+ step diagrams, the enterprise button. With no rung to reach for, a consumer
572
+ either hardcodes the hex, which CLAUDE.md forbids, or approximates with
573
+ color-mix(). cloudgrid-landing is doing the second right now — #133 renders
574
+ `color-mix(in srgb, var(--cg-primary) 22%, var(--cg-neutral-0))` to get within
575
+ 2/255 of --cg-violet-200 — and that is the drift this group removes.
576
+
577
+ RUNG NUMBERS ARE FIGMA'S, not this package's, wherever Figma names them. Two
578
+ rungs (violet-150, violet-400, cyan-400) are values observed applied on a node
579
+ with NO Figma variable behind them; each says so in its own note and is placed
580
+ by lightness between the bound rungs either side. Nothing here was invented.
581
+
582
+ WHY THE UTILITY NAMES DIVERGE FROM THE TOKEN NAMES. The violet ramp maps onto
583
+ --color-brand-100..900, because "brand" is this package's own name for the hue
584
+ (theme.css, rule 4) and Tailwind ships no `brand` scale to collide with. The
585
+ cyan and lime rungs map onto SUFFIXED names (cyan-tint, lime-edge) rather than
586
+ cyan-200 / lime-700, because Tailwind DOES ship those exact numbered names and
587
+ claiming them would silently repaint every `bg-cyan-200` in a consumer's own
588
+ markup. That is rule 4's failure aimed at Tailwind instead of shadcn, and it is
589
+ worse, because the consumer never opted in.
590
+
591
+ DECLARED AT :root ONLY, like every brand source. A ramp rung is not a semantic
592
+ and does not flip; the semantics that spend it do. */
593
+ /*
594
+ SOURCE: Figma color/violet/100. Applied on thegrid at 8364:4136 (the private-status chip), 8364:4158 (the Owner role badge) and the Add-people button. 1.20:1 on the card, so a ground and never a foreground. */
595
+ --cg-violet-100: #ebe8fd;
596
+ /*
597
+ SOURCE: observed applied, UNBOUND. teams 7172:7759 (the first flow circle) and builders 6919:8782 (the first step circle, "Detect"). No Figma variable carries it.
598
+
599
+ It is the value ui#239 asks for and the one cloudgrid-landing currently reaches with a color-mix of --cg-primary. Placed at 150 because it sits between the two bound rungs either side: violet-100 #ebe8fd and violet-200 #d9d4fb. A half-rung is not new here; the neutral ramp already carries neutral-25 between 0 and 50.
600
+
601
+ Measured as a ground: --cg-text-primary is 12.38:1 on it, --cg-slate-600 4.25:1, --cg-slate-500 2.56:1. */
602
+ --cg-violet-150: #e3e0fd;
603
+ /*
604
+ SOURCE: Figma color/violet/200, declared on the home and thegrid frames.
605
+
606
+ READ ui#239'S CORRECTION BEFORE USING --cg-offline FOR THIS. That issue's first table maps #D9D4FB onto --cg-offline, and the mapping only holds inside [data-cg-surface='panel']. At :root --cg-offline is var(--cg-neutral-500), a mid grey, so a consumer taking that token at face value on the page ground gets #858585 and nothing goes red. This rung is the value; --cg-offline is a status tone that happens to reach it on one surface.
607
+
608
+ The pairing the frames draw on it is a documented AA failure and the reason --cg-slate-600 exists: --cg-slate-500 on this ground measures 2.40:1, under even the 3:1 indicator bar. --cg-text-primary is 11.18:1, --cg-slate-600 3.97:1 (large text only), --cg-neutral-600 3.80:1. */
609
+ --cg-violet-200: #d9d4fb;
610
+ /*
611
+ SOURCE: Figma color/violet/300, declared on thegrid. Rendered there only inside flattened SVG illustration, so it is a declared rung with no CSS-reachable node behind it yet. Carried so the ramp a designer edits in Figma is the ramp this package ships. */
612
+ --cg-violet-300: #c8c2fa;
613
+ /*
614
+ SOURCE: observed applied, UNBOUND. builders 6919:8785, the second step circle.
615
+
616
+ Placed at 400 because it is the only observed violet with no bound rung within reach: it sits between color/violet/300 #c8c2fa and color/violet/500 #a69bf7.
617
+
618
+ Worth knowing before retuning: the four builders step circles are #E3E0FD / #BCB4F9 / #A49AF7 / #8072F3, which are --cg-primary composited on white at 17 / 40 / 55 / 77 percent. The last two land within 2 and 4 of bound rungs 500 and 700 and are taken as those; this one does not. */
619
+ --cg-violet-400: #bcb4f9;
620
+ /*
621
+ SOURCE: Figma color/violet/500, declared on thegrid. builders 6919:8788 paints #A49AF7, which is this value within 2/255 per channel and is treated as this rung rather than as a ninth violet. */
622
+ --cg-violet-500: #a69bf7;
623
+ /*
624
+ SOURCE: Figma color/violet/600, declared on thegrid; rendered only inside flattened SVG illustration. Carried for the same reason as violet-300. */
625
+ --cg-violet-600: #9488f5;
626
+ /*
627
+ SOURCE: Figma color/violet/700. Applied on thegrid 8364:4388, the stroke around an avatar chip in the Plug section. builders 6919:8791 paints #8072F3, within 4/255 per channel, and is taken as this rung.
628
+
629
+ 3.57:1 on the card: an indicator or a border, never a word. */
630
+ --cg-violet-700: #8476f4;
631
+ /*
632
+ Figma names --cg-primary color/violet/900. Deliberately an ALIAS rather than the hex written a second time: the brand hue has one home, at the top of this file, and a designer retuning it must not have to find two. */
633
+ --cg-violet-900: var(--cg-primary);
634
+ /*
635
+ SOURCE: Figma color/cyan/200, also carried under the legacy name "Teriary 20" (the typo is Figma's). Applied on three frames: thegrid 8364:4166 (the Editor role badge), teams 7172:7643 and pricing 7779:9320 (the footer's secondary buttons, with a --cg-tertiary hairline around them).
636
+
637
+ It IS a tint of the tertiary, and exactly: #60e9f0 composited on white at 20% is #dffbfc to the byte. Shipped as a value anyway rather than as a color-mix, because a consumer should not have to know the recipe to draw a button.
638
+
639
+ --cg-slate-600 measures 5.20:1 on it and --cg-text-primary 14.65:1. */
640
+ --cg-cyan-200: #dffbfc;
641
+ /*
642
+ SOURCE: Figma color/cyan/300, declared on thegrid; rendered only inside flattened SVG illustration. Carried so the ramp is whole. */
643
+ --cg-cyan-300: #cff8fa;
644
+ /*
645
+ SOURCE: observed applied, UNBOUND. thegrid 8364:4002 and 8364:4056 — the footer and enterprise buttons, drawn as this fill with a --cg-tertiary hairline. ui#239's second comment asks for it.
646
+
647
+ IT IS NOT A TINT OF --cg-tertiary, and that is worth the arithmetic, because every other rung in this family is. #60E9F0 has G 233, so reaching G 252 needs 86% white, and that mix gives R 233 — this value has R 177. Solving the same mix from the blue channel gives 0%. No single alpha produces it. So the color-mix() workaround available for the violet wash is not available here, which is precisely why the token is.
648
+
649
+ Placed at 400 by lightness, between bound rungs cyan-300 and cyan-900.
650
+
651
+ 5.07:1 on the purple panel and 13.82:1 on ink: as a fill it carries text on both dark surfaces. 1.15:1 on the card, so never a foreground on white. */
652
+ --cg-cyan-400: #b1fcff;
653
+ /*
654
+ Figma names --cg-tertiary color/cyan/900. An alias, for the reason violet-900 is one. */
655
+ --cg-cyan-900: var(--cg-tertiary);
656
+ /*
657
+ SOURCE: Figma color/lime/200, declared on thegrid; rendered only inside flattened SVG illustration. Carried so the ramp is whole. */
658
+ --cg-lime-200: #eef7dc;
659
+ /*
660
+ SOURCE: Figma color/lime/600, declared on thegrid; rendered only inside flattened SVG illustration. */
661
+ --cg-lime-600: #d0ea9e;
662
+ /*
663
+ SOURCE: Figma color/lime/700. Applied on thegrid 8364:4387, the stroke around the audience avatars in the Plug section.
664
+
665
+ 1.38:1 on the card and 4.22:1 on the purple panel: a border on a dark ground, not a line on a light one. */
666
+ --cg-lime-700: #c8e68e;
667
+ /*
668
+ Figma names --cg-secondary color/lime/900. An alias, for the reason violet-900 is one. */
669
+ --cg-lime-900: var(--cg-secondary);
450
670
 
451
671
  /* ---- semantic-surfaces ---- */
452
672
  --cg-surface-page: var(--cg-neutral-25);
@@ -476,6 +696,37 @@
476
696
  --cg-text-primary: var(--cg-neutral-900);
477
697
  --cg-text-secondary: var(--cg-neutral-700);
478
698
  --cg-text-muted: var(--cg-neutral-500);
699
+ /*
700
+ The muted tone for text that is real content: a tile hint, an eyebrow that
701
+ carries meaning, a caption a reader has to read. ui#232, 2026-09-18.
702
+
703
+ --cg-text-muted is under the 4.5:1 body-text floor on three of the four grounds
704
+ (3.69:1 card, 3.28:1 page, 3.44:1 panel, 5.92:1 ink), which means every caption
705
+ in this library is AA-legal as large text and illegal as body copy. #232 asked
706
+ for a stronger rung rather than a retune, on the reasoning #149 used for
707
+ state's -strong: a tone tuned to recede and a tone tuned to be read are two
708
+ jobs, and collapsing them retunes both.
709
+
710
+ It is NOT --cg-text-secondary under a second name. Secondary is neutral-700
711
+ (7.00:1 on the card) and reads as a second voice; this is neutral-600, a new
712
+ rung derived for the floor and nothing else. Its note carries the sweep.
713
+
714
+ base #6a6a6a card 5.41:1 page 4.80:1 raised 4.99:1 subtle 4.82:1
715
+ panel rgba(255,255,255,0.86) on #5a47f0 : 4.75:1
716
+ ink rgba(255,255,255,0.68) on #222222 : 8.02:1
717
+
718
+ ON THE PANEL THIS RUNG IS BRIGHTER THAN --cg-text-secondary, and that is not a
719
+ mistake. Measured, the panel's own secondary (0.82 white) is 4.47:1, which is
720
+ under the floor by 0.03. Nothing dimmer than 0.84 white clears 4.5:1 on that
721
+ ground, so the panel cannot deliver a "muted that reads as body copy" below its
722
+ own secondary. Same shape as the note further up about a panel chip's word:
723
+ where the surface cannot deliver the ratio, the surface has to move, not the
724
+ foreground. A designer who dislikes the inversion changes --cg-surface-panel,
725
+ not this.
726
+
727
+ DECORATIVE CAPTIONS MAY KEEP --cg-text-muted. This rung does not deprecate it;
728
+ #232's own done-when says so. */
729
+ --cg-text-muted-strong: var(--cg-neutral-600);
479
730
  --cg-text-placeholder: var(--cg-neutral-400);
480
731
  --cg-text-accent: var(--cg-primary);
481
732
  --cg-text-inverse: var(--cg-neutral-0);
@@ -523,16 +774,94 @@
523
774
  The mono face is untouched: it is used only inside prompt and terminal surfaces
524
775
  and the draft says nothing about it. */
525
776
  /*
526
- The display rung, now Manrope (#197). Identical to --cg-font-body today and
527
- deliberately still a separate token.
528
-
529
- A consumer that wants the licensed Futura back puts it in front of this stack in
530
- its own :root and changes nothing else. Note that the blocks reading this rung
531
- also set `uppercase` and --cg-display-tracking, which were tuned for a condensed
532
- 800; they still render, in Manrope, and whether the lockups keep the uppercase is
533
- a block decision rather than a token one (#203). */
534
- --cg-font-display: 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
535
- --cg-font-body: 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
777
+ The display face, and the one token in this file that names a font this package
778
+ does NOT ship. ui#240, 2026-09-18.
779
+
780
+ All six canonical Figma frames set every headline in TANKER: the hero (92px on
781
+ five frames, 120px on home), every section heading (56px), every card title.
782
+ Nothing else in the design uses it and it is used for nothing else.
783
+
784
+ THE BINARY CANNOT SHIP HERE, and the reason is a licence, not an oversight.
785
+ Tanker is under the ITF Free Font License v2.0, whose section 02 forbids making
786
+ the Font Software available "through another font website, font library,
787
+ marketplace, repository, download service, application or platform". This
788
+ package is public on registry.npmjs.org, installable by anyone with no
789
+ credentials, which is exactly what that clause names. Section 03 states the
790
+ remedy: "Any third party wishing to use the Font Software must obtain their own
791
+ copy directly from Fontshare and be independently bound by this License."
792
+
793
+ That is why Manrope is fine and Tanker is not. Manrope arrives as
794
+ @fontsource-variable/manrope under the Open Font License, which permits
795
+ redistribution. Fontshare publishes no fontsource package.
796
+
797
+ So the package owns the STACK and the consumer owns the FILE. Adding 'Tanker' at
798
+ the front is strictly additive in effect: a consumer that has not self-hosted it
799
+ falls straight through to Manrope and renders exactly what 0.27.2 rendered.
800
+ Before this, a consumer wanting the display face had to copy this whole fallback
801
+ chain verbatim into its own :root (cloudgrid-landing PR #132 did), and that copy
802
+ went stale the moment this package retired the condensed rung.
803
+
804
+ PENDING, and stated so rather than assumed: the 2026-09-10 re-skin (#197)
805
+ deliberately removed the display rung and set the whole system in Manrope.
806
+ Whether naming Tanker here reverses that decision is the founder's call, and it
807
+ is the open question at the end of #240. This change is written to be reversible
808
+ in one line if the answer is no.
809
+
810
+ NOT RETUNED, deliberately: --cg-display-line (1.06) and --cg-display-tracking
811
+ (0.012em) were tuned against Saira Semi Condensed 800. Every Tanker node in
812
+ every frame is leading-none with no tracking. See --cg-display-line-tight. */
813
+ --cg-font-display: 'Tanker', 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
814
+ /*
815
+ The body face. cloudgrid-landing#145, 2026-09-18.
816
+
817
+ Every body string in the canonical frames is SPACE GROTESK, and it was checked
818
+ on five of the six independently before it was believed: home 8204:2561 and
819
+ 8204:2563, thegrid 8364:4213, builders 6913:7654, teams 7172:7571, enterprise
820
+ 7037:779, pricing 7779:9261 — all 20px / 1.46, all the lede under a headline,
821
+ all in unrelated sections. The nav links and the card copy are the same face at
822
+ 15px. This is the design's text face, not a stray on one frame.
823
+
824
+ IT SHIPS HERE, unlike Tanker. @fontsource-variable/space-grotesk is OFL-1.1,
825
+ which permits redistribution, so it arrives as a dependency and an @import in
826
+ fonts.css exactly the way Manrope does. See fonts.css.
827
+
828
+ THIS IS A VALUE CHANGE, not an addition, and it is the only one in this release.
829
+ Manrope is kept in the chain immediately behind it, so a consumer whose pipeline
830
+ somehow does not resolve the new dependency renders precisely what it rendered
831
+ before. Blast radius: every string in every consumer that has not overridden
832
+ this token changes face. Metrics differ — Space Grotesk is wider at the same
833
+ size — so a consumer with a tightly measured layout should look at it before
834
+ taking the release. cloudgrid-landing is the consumer that asked for it.
835
+
836
+ MANROPE DID NOT LEAVE. The frames keep it, and only for two jobs: the eyebrow
837
+ and the button label. Those now read --cg-font-ui, which is this token's
838
+ previous value unchanged, so neither of them moves. */
839
+ --cg-font-body: 'Space Grotesk Variable', 'Space Grotesk', 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
840
+ /*
841
+ The face for the small fixed chrome of a page: the eyebrow above a heading, and
842
+ the label inside a button. 2026-09-18.
843
+
844
+ A third family stack is not a flourish. The canonical frames use exactly three
845
+ faces in three jobs — Tanker for display, Space Grotesk for everything a reader
846
+ reads, and MANROPE for the eyebrow and the button label — and they are
847
+ consistent about it. Every button label on all six frames is Manrope Bold 15
848
+ leading-none (home I8204:2580;732:46, thegrid I8364:4215;732:46, builders
849
+ I6913:7656;732:46, teams I7172:7573;732:46, enterprise I7037:781;732:46,
850
+ pricing I7779:9336;732:46). Every applied eyebrow is Manrope SemiBold 14, and
851
+ Figma binds it as a named text variable, "Eyebrow".
852
+
853
+ Its VALUE is --cg-font-body's previous value, byte for byte. That is what makes
854
+ the body-face change safe to read: the eyebrow and the button did not move, they
855
+ just stopped being spelled with the body token.
856
+
857
+ KNOWN INCONSISTENCY IN THE SOURCE, recorded so nobody "fixes" it from the wrong
858
+ end: on the home frame the four nav links (8204:2573) are Manrope Medium 15 and
859
+ "Sign In" beside them (8204:2579) is Space Grotesk Medium 15 — same slot, same
860
+ size, same weight, two faces. On thegrid the same split appears at 8364:4053
861
+ against 8364:4052/4054/4055. Five frames agree the nav is Space Grotesk, so the
862
+ Manrope instances read as a migration caught mid-flight rather than as intent,
863
+ and this token is NOT the nav's face. */
864
+ --cg-font-ui: 'Manrope Variable', 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
536
865
  --cg-font-mono: 'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
537
866
 
538
867
  /* ---- weights ----
@@ -560,14 +889,221 @@
560
889
  H1 display ramp: uppercase, condensed, Primary or white
561
890
 
562
891
  Stepped down one notch 2026-08-06 (operator: main headings read too
563
- dominant). The SOURCE values were xl 64 / lg 48 / md 42 / sm 32. */
892
+ dominant). The SOURCE values were xl 64 / lg 48 / md 42 / sm 32.
893
+
894
+ 2026-09-19 (#248): display-64 and display-48 added, named by pixel value — see
895
+ display-64's note. With the eight "SET FONT TO TANKER" placeholder nodes
896
+ excluded, ALL 68 remaining runs at 56 across all six frames are explicit
897
+ leading-none with zero tracking, as are all of 48, 36 and 32. #248's comment
898
+ reports 56 carrying two ratios and two trackings; that was one un-converted Anton
899
+ node (home 8216:1762) and three of its runs. This does NOT reopen the proposal to
900
+ move --cg-display-line and --cg-display-tracking: 26 (77 runs) genuinely carries
901
+ 1.0, 1.1 and Auto, so no single library default is right for it, and
902
+ --cg-display-line-tight already covers the cases that want 1. Both shipped values
903
+ stay exactly as they are. */
904
+ /*
905
+ SOURCE: home 8204:2556, the hero headline, Tanker at leading-none.
906
+
907
+ The largest rung in the system and the only node that asks for it. It is kept
908
+ separate from display-2xl rather than collapsed onto it because the two are 28px
909
+ apart and both are heroes: home is 120 and the other five frames are 92. */
910
+ --cg-display-3xl: 120px;
911
+ /*
912
+ SOURCE: the hero headline on five of the six canonical frames — thegrid
913
+ 8364:4211, builders 6913:7652, teams 7172:7569, enterprise 7037:777, pricing
914
+ 7779:9259. Tanker, leading-none, no tracking.
915
+
916
+ This is the ramp's real top. Before it, --cg-display-xl (56px) was the largest
917
+ value in the package and every hero in the design was 36px above it, so each
918
+ consumer clamped its own. thegrid also draws a 96px closing CTA (8364:3957) and
919
+ a 64px section heading (8364:4363); both are single nodes and are deliberately
920
+ not rungs. A value invented to complete a grid is the thing this file keeps
921
+ refusing to add. */
922
+ --cg-display-2xl: 92px;
923
+ /*
924
+ SOURCE: home 8204:3110 ("WHERE DOES IT LIVE?"), thegrid 8364:4363 ("Say what it
925
+ needs"). Two nodes, two frames, Tanker, no tracking. The widest hole in the ramp:
926
+ 36px separate --cg-display-xl 56 from --cg-display-2xl 92, and both consumers
927
+ were clamping their own value into it.
928
+
929
+ NAMED BY PIXEL VALUE, 2026-09-19 (#248). Every size added in this round falls
930
+ STRICTLY BETWEEN two shipped rungs, which is the second consecutive census to
931
+ hit that wall — the tanker-title-ramp note records the first. A t-shirt suffix
932
+ encodes ORDER, and order is what an insertion breaks; renaming a shipped rung is
933
+ breaking for two published consumers, and a third Tanker ramp would carry the
934
+ same face, leading and roles as the two that exist and therefore no information.
935
+ So the ramp prefix still means (face, leading, role) and the suffix becomes the
936
+ size. A px-named rung cannot be made wrong by a later insertion.
937
+
938
+ SIZE ONLY, AND DELIBERATELY.
939
+
940
+ A rung gets a --text-* utility only where the frames settled on a leading. Where
941
+ they did not, it ships as a custom property alone: theme.css's own rule is that
942
+ "a companion that invents a value is worse than an absent one", and
943
+ tokens.test.mjs requires every --text-* to carry a --line-height, so a utility
944
+ here could only exist by inventing one.
945
+
946
+ The two nodes do not agree on a leading and only one of them expresses an
947
+ opinion: thegrid sets 70px (ratio 1.094) and home leaves lineHeightUnit at
948
+ INTRINSIC_%, i.e. Figma's Auto, which is Tanker's own 1.35 default and not a
949
+ design decision at all. A leading shipped here would be wrong for one of its two
950
+ users. The same reasoning is why 96px (thegrid 8364:3957, lh 56 — a ratio of
951
+ 0.583, 96px type in a 56px line box) is recorded and still has no rung. */
952
+ --cg-display-64: 64px;
564
953
  --cg-display-xl: 56px;
954
+ /*
955
+ SOURCE: home 8216:1868, the "Copy this command INTO your aI tool to Plug in"
956
+ statement. Tanker, explicit leading-none (lineHeightUnit FONT_SIZE_% at 100), no
957
+ tracking.
958
+
959
+ ONE NODE ON ONE FRAME, stated plainly because the count in #248 reads as four:
960
+ the node carries four per-character colour runs and a run census counts each of
961
+ them. One node is not disqualifying here — --cg-display-3xl 120 is also a single
962
+ node on a single frame and 0.28.0 shipped it — but the evidence is one headline,
963
+ not four.
964
+
965
+ Named by pixel value; --cg-display-64's note has the reasoning (#248).
966
+
967
+ It carries display-line-tight and title-tracking rather than --cg-display-line
968
+ and --cg-display-tracking, the same as --cg-display-2xl and --cg-display-3xl:
969
+ the node is explicitly 1.0 / 0, and the two legacy values were tuned for a
970
+ condensed face this package no longer ships (#240). */
971
+ --cg-display-48: 48px;
565
972
  --cg-display-lg: 44px;
566
973
  /* SOURCE: onboarding H1 (was 42px) */
567
974
  --cg-display-md: 38px;
568
975
  --cg-display-sm: 30px;
569
976
  --cg-display-line: 1.06;
570
977
  --cg-display-tracking: 0.012em;
978
+ /*
979
+ The leading every Tanker node in every canonical frame actually uses: 1, i.e.
980
+ leading-none. Not one display node in six frames emits a letter-spacing either.
981
+
982
+ --cg-display-line stays 1.06 and --cg-display-tracking stays 0.012em. Both were
983
+ tuned against Saira Semi Condensed 800 (ui#240 records this), neither has been
984
+ retuned for Tanker, and both are shipped values that four display rungs and
985
+ every consumer's display utility already read. Retuning them is a one-line edit
986
+ and a visible change to text that exists today, so it is proposed rather than
987
+ made. This rung is what the new sizes are built on in the meantime. */
988
+ --cg-display-line-tight: 1;
989
+
990
+ /* ---- tanker-title-ramp ----
991
+ The display face at CARD scale, 2026-09-18.
992
+
993
+ Four rungs the canonical frames use constantly and this package could spell
994
+ none of. They are separated from h1-display-ramp rather than wedged into it
995
+ because the two ramps disagree everywhere they overlap, and the disagreement is
996
+ not resolvable by addition:
997
+
998
+ frames 120 96 92 64 56 40 38 32 30 26 20
999
+ package 56 44 38 30
1000
+
1001
+ 56, 38 and 30 coincide. 44 is used by nothing in any of the six frames. Every
1002
+ other size the frames ask for falls BETWEEN two shipped rungs — 40 between 44
1003
+ and 38, 26 between 30 and heading-lg's 22 — so there is no name left on the
1004
+ t-shirt ramp that is both free and in the right place, and renaming a shipped
1005
+ rung is a breaking change for two published consumers.
1006
+
1007
+ They are also not headings. --cg-heading-* is the Manrope ramp at 1.2 leading;
1008
+ this is the display FACE at leading-none, and --cg-title-sm and
1009
+ --cg-heading-md are both 20px precisely because a face and a leading are what
1010
+ separate them, not a size.
1011
+
1012
+ SIZES, with where they were read:
1013
+ 40 home 8204:2589, enterprise 7038:1244, pricing 7785:9809, thegrid 8364:4382
1014
+ 26 teams 7297:8606, builders 6916:8648, enterprise 7038:1192,
1015
+ pricing 7788:10264, thegrid 8364:3882
1016
+ 20 teams 7172:7760, builders 6919:8783, thegrid petal labels
1017
+
1018
+ pricing 7785:10059 draws one 32px Tanker number and thegrid draws 15px Tanker
1019
+ eyebrows. Single-frame, so recorded and not given a rung.
1020
+
1021
+ ---
1022
+
1023
+ 2026-09-19 (#248). Three more rungs — title-36, title-32, title-24 — and the
1024
+ t-shirt suffix stops here. See --cg-display-64's note for why they are named by
1025
+ pixel value and the group note above for why this ramp is separate from
1026
+ h1-display-ramp in the first place.
1027
+
1028
+ RECORDED AND DELIBERATELY NOT GIVEN A RUNG:
1029
+
1030
+ 28px. #248 proposes it on two home nodes. Both are placeholders the designer
1031
+ has not converted: 8204:3066 "lead - SET FONT TO TANKER" (lineHeight 92.6%,
1032
+ letterSpacing 0.24) and 8216:1784 "txt:RIGHT HERE - SET FONT TO TANKER"
1033
+ (106%, 0.42). They keep the previous face's metrics — a third node in the same
1034
+ set, 8216:1762, is still literally set in Anton — so there is no settled 28px
1035
+ anything in the six frames. Six home nodes and two thegrid nodes carry that
1036
+ "SET FONT TO TANKER" marker; every Tanker node in the frames that emits a
1037
+ letter-spacing is one of them.
1038
+
1039
+ 32px on Inter (builders 7172:8458, the "$ grid plug" pill). Off-system in the
1040
+ design itself; #243 recorded it.
1041
+
1042
+ ON AUTO LEADING. Figma's lineHeightUnit distinguishes a decision from an
1043
+ absence: FONT_SIZE_% and PIXELS mean the designer set a leading, INTRINSIC_%
1044
+ means Auto and the number in the file is the FACE's default, which for Tanker is
1045
+ 1.35. Every 1.35 in a Tanker census is INTRINSIC_%. A rung built on one would
1046
+ encode Tanker's metrics as a brand decision. */
1047
+ /*
1048
+ Four frames. The governance card title, the plan price, the sub-heading under a section statement. */
1049
+ --cg-title-lg: 40px;
1050
+ /*
1051
+ SOURCE: enterprise 7039:5124, 7039:5119, 7039:5129 — the three audience column
1052
+ headings ("Platform and security teams", "Innovation Leaders", "Business
1053
+ functions"). Three nodes, one frame, all explicit leading-none, no tracking.
1054
+
1055
+ One frame, and 2px from --cg-display-md 38. It is a rung rather than an
1056
+ approximation of 38 because it is the card/column role, not the section-heading
1057
+ role, and because rounding a measured 36 onto 38 is the kind of "close enough"
1058
+ that #243 had to unpick when ui#239's table called this ramp's 26 a "24px card
1059
+ title".
1060
+
1061
+ Named by pixel value; --cg-display-64's note has the reasoning (#248). */
1062
+ --cg-title-36: 36px;
1063
+ /*
1064
+ SOURCE: home 8204:2590 ("Learn how the grid can help you"), pricing 7785:10059 /
1065
+ 10068 / 10073 / 10076 / 10081 / 10084 (the six plan prices). Seven nodes, two
1066
+ frames, all explicit leading-none, no tracking.
1067
+
1068
+ THE DISPLAY FACE HALF OF 32. The frames also draw 32 on the UI face, ten times,
1069
+ and that is --cg-stat-size on the Manrope ramp, not this. Two rungs at one size
1070
+ is the ramp rule working as intended, exactly as --cg-title-sm and
1071
+ --cg-heading-md are both 20px: a rung is a face, a leading and a role, not a
1072
+ number.
1073
+
1074
+ #243 recorded pricing's 32 as "one 32px Tanker number, single-frame, so recorded
1075
+ and not given a rung". The rebuilt extract finds seven nodes on two frames.
1076
+
1077
+ Named by pixel value; --cg-display-64's note has the reasoning (#248). */
1078
+ --cg-title-32: 32px;
1079
+ /*
1080
+ Five frames. The card title and the FAQ question — the most common display size in the design after the section heading.
1081
+
1082
+ ui#239's type table calls this rung "24px card title". Measured on five frames it is 26, not 24, and nothing in any frame is 24. The table was written from a consumer's approximation rather than from the node. */
1083
+ --cg-title-md: 26px;
1084
+ /*
1085
+ SOURCE: home 8204:2813, 8204:2753 / 2758 / 2763; enterprise 7039:5084, 7039:5088,
1086
+ 7039:5093, 7039:5097, 7395:666. Twelve nodes, two frames. Nine set an explicit
1087
+ leading-none; two leave lineHeightUnit at INTRINSIC_% (Figma's Auto, no
1088
+ decision); one, home 8216:1908, sets 30px. No node carries tracking.
1089
+
1090
+ TWO PIXELS FROM --cg-title-md 26, AND BOTH ARE REAL. 26 is 77 runs on six frames
1091
+ and 24 is 12 on two; they coexist in the same design and in some cases in the
1092
+ same section. Note that #239's type table called the 26 rung "24px card title"
1093
+ from a consumer's approximation — this rung is the actual 24, measured, and does
1094
+ not retroactively make that table right.
1095
+
1096
+ Named by pixel value; --cg-display-64's note has the reasoning (#248). */
1097
+ --cg-title-24: 24px;
1098
+ /*
1099
+ Three frames. The label inside a step circle or a diagram petal. Same size as --cg-heading-md and a different face and leading; see the group note. */
1100
+ --cg-title-sm: 20px;
1101
+ /*
1102
+ leading-none, aliased rather than repeated so a retune of the display leading reaches both ramps. An alias compiles to var(), so it survives a scope flip. */
1103
+ --cg-title-line: var(--cg-display-line-tight);
1104
+ /*
1105
+ No Tanker node in any of the six frames emits a letter-spacing. Spelled out rather than omitted so a utility built on this ramp cannot inherit --cg-display-tracking's 0.012em by accident. */
1106
+ --cg-title-tracking: 0em;
571
1107
 
572
1108
  /* ---- manrope-ramp ---- */
573
1109
  /*
@@ -589,12 +1125,74 @@
589
1125
  extrapolation, and a heading is short enough that 1.2 does not cost a line its
590
1126
  descenders. */
591
1127
  --cg-heading-line: 1.2;
1128
+ /*
1129
+ SOURCE: the lede paragraph on all six canonical frames — home 8204:2561,
1130
+ thegrid 8364:4213, builders 6913:7654, teams 7172:7571, enterprise 7037:779,
1131
+ pricing 7779:9261. Space Grotesk regular, line-height 1.46, no tracking.
1132
+
1133
+ The single most-used text size in the whole design and the one this package
1134
+ could not spell. --cg-body-lg is 19px and --cg-heading-md is 20px, so a
1135
+ consumer building a lede had to pick between a body token one pixel short and a
1136
+ heading token that is the right size with the wrong role and the wrong leading
1137
+ (1.2 against 1.46). cloudgrid-landing#145 records sections doing the first.
1138
+
1139
+ It reads --cg-body-line, so it is a body rung and not a fifth display rung.
1140
+
1141
+ pricing 7785:9832 draws one 18px bold plan name. Single node, one frame, 1px
1142
+ off --cg-body-lg: recorded, not given a rung. */
1143
+ --cg-body-xl: 20px;
592
1144
  /* SOURCE: H1 subline */
593
1145
  --cg-body-lg: 19px;
1146
+ /*
1147
+ SOURCE: pricing 7785:9832 / 9858 / 9872, the three plan names — Space Grotesk
1148
+ Bold 18 at line-height 1.46, which is --cg-body-line exactly. Also thegrid
1149
+ 8364:4352 / 4355 / 4358 and 8364:4385, Manrope 18 at 1.5.
1150
+
1151
+ A BODY RUNG, NOT A HEADING ONE. #248 asks which ramp 18 belongs on, since it
1152
+ sits between --cg-heading-sm 17 and --cg-heading-md 20. Every node at 18 that
1153
+ expresses a leading at all reads 1.46 or 1.5; nothing is near
1154
+ --cg-heading-line's 1.2. home 8204:3090 and 8204:3105 draw Tanker at 18 and both
1155
+ are INTRINSIC_% (Auto), so they express no opinion and do not make this a
1156
+ display rung either.
1157
+
1158
+ #243 recorded pricing's 18 as "one 18px bold plan name. Single node, one frame,
1159
+ 1px off --cg-body-lg". It is three nodes, and the 1px is the point: a consumer
1160
+ otherwise rounds it onto --cg-body-lg 19 and the plan-name row drifts by a pixel
1161
+ against the Figma it is being checked against.
1162
+
1163
+ Named by pixel value; --cg-display-64's note has the reasoning (#248). */
1164
+ --cg-body-18: 18px;
594
1165
  /* SOURCE: default UI text */
595
1166
  --cg-body-md: 16px;
596
1167
  /* SOURCE: card body, legal */
597
1168
  --cg-body-sm: 15px;
1169
+ /*
1170
+ SOURCE: home 8216:1768, 8216:1769, 8216:1771 — the three lines of the prompt-and-
1171
+ reply mock. Manrope Regular 14. Three runs, one frame.
1172
+
1173
+ READ THIS BEFORE TREATING 14 AS A GAP. #248 counts nineteen 14px runs on four
1174
+ frames and calls it one of its two strongest cases. SIXTEEN OF THE NINETEEN ARE
1175
+ THE EYEBROW — Manrope 600, leading 1.2, tracking 1.4px, uppercase — which 0.28.0
1176
+ already ships whole as --cg-eyebrow-size / -line / -tracking / -weight, and
1177
+ three of the nodes the issue cites (teams 7172:8463, builders 6916:8053,
1178
+ enterprise 7038:1184) are the very nodes in that token's own docblock. 14 was
1179
+ already closed; the four frames are the eyebrow's four frames.
1180
+
1181
+ So this rung exists for the residue, and the residue is thin. It ships anyway
1182
+ because without it the only 14 in the package is --cg-eyebrow-size, and a
1183
+ consumer who wants plain 14px text will reach for it and sooner or later reach
1184
+ for text-eyebrow with it, inheriting a weight and a tracking nobody asked for.
1185
+ --cg-mono-size is also 14 and is the mono face.
1186
+
1187
+ SIZE ONLY.
1188
+
1189
+ A rung gets a --text-* utility only where the frames settled on a leading. Where
1190
+ they did not, it ships as a custom property alone: theme.css's own rule is that
1191
+ "a companion that invents a value is worse than an absent one", and
1192
+ tokens.test.mjs requires every --text-* to carry a --line-height, so a utility
1193
+ here could only exist by inventing one. The three runs read 1.295 and 1.55; there is nothing
1194
+ to ship. */
1195
+ --cg-body-14: 14px;
598
1196
  /* SOURCE: footer, helper */
599
1197
  --cg-caption: 13px;
600
1198
  /*
@@ -609,6 +1207,78 @@
609
1207
  --cg-mono-line: 1.7;
610
1208
  --cg-label-size: 15px;
611
1209
  --cg-label-weight: var(--cg-weight-medium);
1210
+ /*
1211
+ SOURCE: Figma's own bound text variable, "Eyebrow" —
1212
+ Font(family: "Manrope", style: SemiBold, size: 14, weight: 600, lineHeight: 1.2,
1213
+ letterSpacing: 10). Applied first-hand on teams 7172:8463, builders 6916:8053
1214
+ and enterprise 7038:1184, each rendering tracking-[1.4px] uppercase.
1215
+
1216
+ The only rung in this package derived from a Figma TEXT variable rather than
1217
+ from a measured node, and it agrees with the nodes, which is why it is trusted.
1218
+
1219
+ It is equidistant from the two tokens a consumer would otherwise reach for:
1220
+ --cg-caption is 13px and --cg-label-size is 15px, and Eyebrow currently builds
1221
+ on the second. One pixel is not the point — the eyebrow also carries a weight,
1222
+ a leading and a tracking that neither of those tokens has, and spelling three
1223
+ of the four inline is how a component drifts.
1224
+
1225
+ KNOWN: thegrid draws its eyebrows differently — Tanker regular 15px, leading
1226
+ 16px, tracking 1.1px (8364:3905, 8364:4409) — and both of pricing's eyebrows are
1227
+ switched off in the file. So this is the eyebrow of three frames, not six, and
1228
+ thegrid's is an open question for the designer rather than a second rung here. */
1229
+ --cg-eyebrow-size: 14px;
1230
+ /*
1231
+ From the Eyebrow variable. The same number as --cg-heading-line, spelled out rather than aliased: an eyebrow is not a heading, and a heading retune must not silently move it. */
1232
+ --cg-eyebrow-line: 1.2;
1233
+ /*
1234
+ From the Eyebrow variable, whose letterSpacing is 10 — Figma's percent, not pixels. At 14px that is 1.4px, which is what all three frames render. Stored in em so the ratio survives a size change. */
1235
+ --cg-eyebrow-tracking: 0.1em;
1236
+ /*
1237
+ 600, from the Eyebrow variable's weight: 600.
1238
+
1239
+ Aliased onto --cg-weight-bold rather than written as a number, because 600 is
1240
+ already this package's "bold" (the weights group's own note explains why that
1241
+ name and not "semibold"). Two spellings of one weight is how a ramp starts
1242
+ disagreeing with itself.
1243
+
1244
+ Note this is NOT --cg-label-weight, which is 500. The eyebrow and the label are
1245
+ different rungs; see the group note. */
1246
+ --cg-eyebrow-weight: var(--cg-weight-bold);
1247
+ /*
1248
+ The big figure in a stat block: the UI face at leading-none.
1249
+
1250
+ SOURCE: home 8204:2769 / 2774 / 2779 / 2784 / 2789 and enterprise 7038:4564 /
1251
+ 4569 / 4574 / 4579 / 4584 — "2", "184", "4", "8", "$0". Ten nodes, two frames,
1252
+ Manrope Medium 32, every one of them explicit leading-none with no tracking.
1253
+
1254
+ THE UI-FACE HALF OF 32; --cg-title-32 is the display-face half. This is a role
1255
+ the Manrope ramp could not spell at all: --cg-heading-* is 1.2 and --cg-body-* is
1256
+ 1.46, and nothing on this face was at 1.
1257
+
1258
+ It is a role set rather than a bare size, on the --cg-eyebrow-* pattern, for the
1259
+ reason that block gives: the figure carries a leading and a weight as well as a
1260
+ size, and spelling two of the three inline is how a component drifts.
1261
+
1262
+ KNOWN DIVERGENCE, not fixed here: StatTile renders its value as
1263
+ `font-display text-display-sm` — Tanker 30 at --cg-display-line. The frames say
1264
+ Manrope Medium 32 at leading-none. Repointing it changes what every StatTile
1265
+ renders today, so it is a separate change with its own changeset.
1266
+
1267
+ The same five figures appear again at 18.149px inside scaled product mocks on
1268
+ home, thegrid and enterprise. That is the per-mock scale factor #248 documents,
1269
+ not a second rung. */
1270
+ --cg-stat-size: 32px;
1271
+ /*
1272
+ leading-none, from all ten nodes. Spelled as a number rather than aliased onto
1273
+ --cg-display-line-tight: that alias exists so a retune of the DISPLAY leading
1274
+ reaches the title ramp too, and the stat figure is on the UI face and must not
1275
+ move with it. */
1276
+ --cg-stat-line: 1;
1277
+ /*
1278
+ 500, from all ten nodes. Manrope Medium, not the eyebrow's 600 and not
1279
+ --cg-label-weight's 500 by coincidence — aliased onto the weight rung so it
1280
+ tracks the brand's Medium. */
1281
+ --cg-stat-weight: var(--cg-weight-medium);
612
1282
 
613
1283
  /* ---- spacing ----
614
1284
  spacing: 4px base */
@@ -657,8 +1327,44 @@
657
1327
  --cg-radius-lg: 14px;
658
1328
  /* SOURCE: option card */
659
1329
  --cg-radius-xl: 16px;
1330
+ /*
1331
+ SOURCE: the corner of every section slab and every card in the design, read on
1332
+ all six canonical frames — home 8204:2440, thegrid 8364:3928, builders
1333
+ 6913:8019, teams 7172:7567, enterprise 7037:775, pricing 7785:10051.
1334
+
1335
+ It is the most-used radius in the whole design and the ramp had no name for it.
1336
+ --cg-radius-xl is 16 and --cg-radius-2xl is 24, and both are shipped, so 20 has
1337
+ no free t-shirt name in the right place. Renaming a rung two published
1338
+ consumers already read is a breaking change for a corner.
1339
+
1340
+ So it takes a ROLE name, which the corners group already does once for
1341
+ --cg-radius-pill. The role is exact and worth stating: in these frames 16 is
1342
+ what a CONTROL is rounded to (every button on every frame is 16) and 20 is what
1343
+ a CONTAINER is rounded to. The design draws that distinction consistently, which
1344
+ is more information than a number between two numbers.
1345
+
1346
+ NOT --cg-radius-panel, deliberately: "panel" in this package is the purple
1347
+ surface (--cg-surface-panel, data-cg-surface="panel"), and a radius named after
1348
+ it would read as that surface's corner rather than as every container's. */
1349
+ --cg-radius-container: 20px;
660
1350
  /* SOURCE: full-bleed purple panel */
661
1351
  --cg-radius-2xl: 24px;
1352
+ /*
1353
+ SOURCE: home 8204:2557 and 8204:2569, the two 92px lime image cards in the hero.
1354
+
1355
+ One frame, two nodes, and it is here rather than recorded-and-skipped for one
1356
+ reason: it extends the t-shirt ramp at the TOP, where there is a free name and
1357
+ no ambiguity, instead of asking for one between two shipped rungs.
1358
+
1359
+ WHAT IS NOT HERE. The brief this work came from expected a rung between
1360
+ --cg-radius-2xl and --cg-radius-pill for a frame nesting 108 / 93 / 80px
1361
+ corners. No radius above 60 and below the pill appears on any of the six frames
1362
+ first-hand. builders draws 60px on three decorative glow rectangles
1363
+ (7172:8460-8462) and thegrid draws 22 / 18 / 15 / 11 / 9 inside a flattened
1364
+ product-UI mock. None of those is a rung; all are recorded here so the next
1365
+ audit does not re-find them. The home frame is only ~10% audited (the Figma
1366
+ quota ran out), so if that expectation has a node behind it, it is there. */
1367
+ --cg-radius-3xl: 32px;
662
1368
  --cg-radius-pill: 999px;
663
1369
  --cg-border-width: 1px;
664
1370
  --cg-border-width-strong: 1.5px;
@@ -844,6 +1550,12 @@
844
1550
  --cg-text-primary: #ffffff;
845
1551
  --cg-text-secondary: rgba(255, 255, 255, 0.82);
846
1552
  --cg-text-muted: rgba(255, 255, 255, 0.66);
1553
+ /*
1554
+ 4.75:1 on --cg-surface-panel. See the base note: on this ground nothing dimmer
1555
+ than 0.84 white clears the 4.5:1 floor, so this rung is necessarily brighter
1556
+ than the panel's own --cg-text-secondary (0.82, 4.47:1). The surface is what
1557
+ would have to move to change that, not this value. */
1558
+ --cg-text-muted-strong: rgba(255, 255, 255, 0.86);
847
1559
  --cg-text-accent: #ffffff;
848
1560
 
849
1561
  /* ---- semantic-lines ---- */
@@ -945,6 +1657,14 @@
945
1657
  --cg-text-primary: #ffffff;
946
1658
  --cg-text-secondary: rgba(255, 255, 255, 0.78);
947
1659
  --cg-text-muted: rgba(255, 255, 255, 0.56);
1660
+ /*
1661
+ 8.02:1 on --cg-surface-code. Ink is the one ground where --cg-text-muted
1662
+ already clears the body floor (5.92:1), so this rung exists there for
1663
+ consistency of meaning rather than to fix a ratio: a component that reaches for
1664
+ text-faint-strong must resolve to something on all three surfaces, and
1665
+ resolving to the base #6a6a6a inside a dark scope would be the CLAUDE.md rule-3
1666
+ failure. It sits between the ink scope's muted (0.56) and its secondary (0.78). */
1667
+ --cg-text-muted-strong: rgba(255, 255, 255, 0.68);
948
1668
  --cg-text-accent: var(--cg-secondary);
949
1669
 
950
1670
  /* ---- semantic-surfaces ---- */