/* =============================================================================
   ft-theme.tokens.css — THE REFERENCE LAYER
   Fasttera CRM · Frappe 16 · written 2026-09-16
   VERSION 2026-09-26-s · colour + THE DIMENSION LAYER · space · size · type · elevation · motion · the nav contract
                          + rev263 · ft-popup on tokens (desk S005, 2026-09-24)
                          + T-1 · parity with the designer's chain: 19 roles take HIS declarations (desk S010, owner ruling 2026-09-24)
                          + rev267 · the table A–Z (grey head, OR-T1) · rev267b · THE 16 HELD ROLES ENDED · rev267c · the owner's
                            main-nav geometry 236/52/0/16@22/8/14 (reversible: the ORDER-2026-09-17 set is kept as a comment)
                          + THE TWO TYPE SLOTS and THE RAIL WIDTH
   🔴 Your reply names the VERSION line it built against.

   🔴 THIS FILE HAS ONE OWNER AND IT IS THIS REPOSITORY (RULING-2026-09-18-THE-TOKEN-FILE-HAS-ONE-OWNER).
   The desk sends ORDERED LINES, never a whole file, and the VERSION moves here when they land. Its own
   -e copy is marked superseded. The designer generates his reference layer from the SERVED file.
   Two owners of one artefact is the defect this file spent a week being — the edits simply never
   collided, and luck is not a process.

   -f adds three lines, all previously ruled and all carried as fallbacks until now:
     --ft-font-latin · --ft-font-arabic   the two TYPE SLOTS (ruled 2026-09-16, MR 199's sibling).
                                          They were declared in ft-fonts.css; that file now consumes
                                          them and declares neither, so there is one owner.
     --ft-rail-width                       256px (ruled 2026-09-18 from 24 measured labels).

   Implements the owner's ruling of 2026-09-15:

     "coding make the colors as references not as color value, then we make the
      reference as color. all systems does that. primary color=#xxxxxx … if we
      want to change one colour we change only the reference and from the theme
      colour settings."

   THE ONE RULE THIS FILE ENFORCES
   -------------------------------
   A hex or rgb() literal may appear in TIER 1 and nowhere else in the system.
   Tier 2 and tier 3 are var() only. Component CSS is var() only.
   If you are typing a '#' outside the SOURCE block below, stop — the colour you
   want either already exists or belongs in tier 1.

   WHY THIS FILE EXISTS
   --------------------
   Measured on the live port, 2026-09-16:
     124 --ft* tokens declared · 68 of them colours · 0 referencing anything
     1,187 colour sites in our own CSS · 100 painted through a token · 7.8%
     27 of 124 tokens used anywhere · 97 declared and never referenced
     HubSpot's teal #00A4BD baked into 6 tokens, with 2 different hover values

   SCOPE — DO NOT MOVE THIS BLOCK
   ------------------------------
   These tokens are declared on :root and must stay there.
   Dialogs in this product mount on <body>, OUTSIDE .ftset-host (driven and
   confirmed 2026-09-16, and #ft-overlay-root is empty and unused). Scoping the
   theme to .ftset-host would silently unpaint every dialog, menu and toast,
   with nothing reporting it.
   ============================================================================= */


/* =============================================================================
   TIER 1 · SOURCE — THE ONLY LITERALS IN THE SYSTEM
   ============================================================================= */

:root {

  /* ---------------------------------------------------------------------------
     ✅ RULED BY THE OWNER, 2026-09-16:  "our is the navy."

     THE BRAND IS #0A2344.  This is the only line in the system that defines it.
     #00A4BD is HubSpot's teal. It is NOT our brand. It survives below as an
     ACCENT because it does real work in six places and reads well against navy —
     but it never again answers to the name "primary".

     Nothing else in this file, and nothing in any component stylesheet, may
     declare a brand colour. Change the brand here and the product repaints.
     --------------------------------------------------------------------------- */

  --ft-source-brand:            #0A2344;    /* Fasttera navy — THE BRAND */
  --ft-source-brand-secondary:  #B40C0E;    /* Fasttera red */
  /* 🔴 S107 4 (owner): the red's INTERACTIVE STEPS, added because the scale had none and Cancel may not borrow
     the destructive ones. DERIVED, not chosen — the same HSL lightness deltas that relate --ft-action-primary's
     light hover/pressed to its base (+0.0529 / -0.0392), applied to #B40C0E. Tier-3 editable, like every source.
     Measured with white ink: base 7.00:1 · hover 5.73:1 · pressed 8.17:1, all clearing 4.5:1. */
  --ft-source-brand-secondary-hover:   #CD0E10;
  --ft-source-brand-secondary-pressed: #A10B0D;
  --ft-source-accent:           #00A4BD;    /* the teal — an ACCENT, never the brand.
                                               ⚠️ NON-TEXT ONLY. White on it is 2.98:1,
                                               which fails AA large text by 0.02. */
  --ft-source-accent-strong:    #007990;    /* the accent for surfaces that CARRY TEXT.
                                               White on it is 5.08:1 — passes AA.
                                               Found by the designer, rev100. */

  /* neutrals — the spine. Every grey in the product resolves to one of these. */
  --ft-source-ink:              #141414;
  --ft-source-ink-2:            #1F2937;
  --ft-source-ink-3:            #475569;
  --ft-source-ink-4:            #64748B;
  --ft-source-ink-5:            #94A3B8;
  --ft-source-line:             #E5E7EB;   /* 252 uses — the most-painted colour we own */
  --ft-source-line-field:       #CBD5E1;   /* 80 uses — input borders. A DIFFERENT line to the above. */
  --ft-source-line-soft:        #F1F5F9;
  --ft-source-surface:          #F8FAFC;
  --ft-source-surface-2:        #F5F8FA;
  --ft-source-surface-3:        #F3F4F6;   /* 11 uses — the neutral tint */
  --ft-source-paper:            #FFFFFF;

  /* semantic sources */
  --ft-source-success:          #10B981;
  --ft-source-warning:          #F59E0B;
  --ft-source-danger:           #EF4444;
  --ft-source-info:             #3B82F6;
  --ft-source-azure:            #1F5FB0;   /* named in ft-tokens.js; painted 17× as a literal */

  /* semantic INK — the colour of text ON a semantic tint.
     This family was missing from the first draft and the conversion map found
     it: 28 sites paint one of these four and none of them is derivable from
     the semantic source above. A dark amber on a pale amber is a designed
     pair, not a computed one. */
  --ft-source-success-ink:      #166534;
  --ft-source-warning-ink:      #92400E;   /* 23 uses — the largest of the four */
  --ft-source-danger-ink:       #991B1B;
  --ft-source-info-ink:         #1E40AF;

  /* channel marks — brand colours OWNED BY OTHER COMPANIES.
     These are the one legitimate family of literals outside the spine: they are
     not ours to theme, and a theme switch must NOT move them. */
  --ft-channel-facebook:        #1877F2;
  --ft-channel-linkedin:        #0A66C2;
  --ft-channel-instagram:       #E1306C;
  --ft-channel-whatsapp:        #25D366;
  --ft-channel-tiktok:          #000000;
  --ft-channel-email:           #3B82F6;
  --ft-channel-internal:        #6B7280;

  /* campaign colours — HubSpot's campaign picker, measured by the desk 2026-10-04, in HubSpot's order and its
     lower-case spelling. DATA colours, like the channel marks: a campaign stores the chosen CODE, so a theme switch
     must NOT move them and changing a value here never recolours a saved campaign. Read by
     campaigns.get_campaign_palette (the campaign form's quick picks); no campaign code spells a hex. A Tier-3 override
     of this list comes later. */
  --ft-campaign-1:              #956309;
  --ft-campaign-2:              #00823a;
  --ft-campaign-3:              #00d0e4;
  --ft-campaign-4:              #f26522;
  --ft-campaign-5:              #0200ff;
  --ft-campaign-6:              #ff0000;
  --ft-campaign-7:              #7d53e9;
  --ft-campaign-8:              #333333;
  --ft-campaign-9:              #ea90b1;

  /* derivation constants — the ramp rule, one place.
     Replaces the two conflicting hovers (#0089A0 and #0093a8) measured today. */
  --ft-mix-hover:               14%;
  --ft-mix-active:              26%;
  --ft-mix-tint:                90%;
}


/* =============================================================================
   TIER 2 · ROLE — never a literal. What a colour IS.
   ============================================================================= */

:root {

  /* brand */
  --ft-brand:                   var(--ft-source-brand);
  --ft-brand-secondary:         var(--ft-source-brand-secondary);
  --ft-brand-secondary-hover:   var(--ft-source-brand-secondary-hover);
  --ft-brand-secondary-pressed: var(--ft-source-brand-secondary-pressed);

  /* 🔴 TWO ACCENTS, AND THE SPLIT IS A CONTRAST RULING, NOT A PREFERENCE.
     --ft-accent         #00A4BD  focus rings · fills · hairlines · tints · the gradient
                                  — anything that does NOT carry text.
     --ft-accent-strong  #007990  any accent surface WITH TEXT ON IT.
     White reads 2.98 on the first and 5.08 on the second. Putting ink on
     --ft-accent is a WCAG failure and the linter should say so. */
  --ft-accent:                  var(--ft-source-accent);
  --ft-accent-strong:           var(--ft-source-accent-strong);

  /* derived states — ONE rule, not one value per component.
     color-mix is supported in every browser Frappe 16 targets. Where a build
     must not use it, generate these four at build time from tier 1 — never by
     hand, and never twice. */
  --ft-brand-hover:   color-mix(in srgb, var(--ft-brand)  calc(100% - var(--ft-mix-hover)),  black);
  --ft-brand-active:  color-mix(in srgb, var(--ft-brand)  calc(100% - var(--ft-mix-active)), black);
  --ft-brand-tint:    color-mix(in srgb, var(--ft-brand)  calc(100% - var(--ft-mix-tint)),   white);
  --ft-accent-hover:  color-mix(in srgb, var(--ft-accent) calc(100% - var(--ft-mix-hover)),  black);
  --ft-accent-tint:   color-mix(in srgb, var(--ft-accent) calc(100% - var(--ft-mix-tint)),   white);

  /* GROUND — a colour under something */
  --ft-ground-page:             var(--ft-source-paper);
  --ft-ground-surface:          var(--ft-source-surface);
  --ft-ground-surface-2:        var(--ft-source-surface-2);
  --ft-ground-raised:           var(--ft-source-paper);
  --ft-ground-brand:            var(--ft-brand);
  --ft-ground-accent:           var(--ft-accent-strong);   /* text-bearing — 5.08 with white */
  --ft-ground-accent-flat:      var(--ft-accent);          /* no text — fills and rules only */

  /* ═══ --ft-control-checked · ONE TOKEN FOR EVERY CHECKBOX (owner, hotfix 2 Oct) ══════════════════════════════
     The checked AND indeterminate fill and border of every checkbox we render — the declared FtCheck, the
     hand-rolled boxes, and every native `input[type=checkbox]` we mark `data-ft-check` (painted by
     css/ft-checkbox.css). It resolves to the ground the declared checkbox already paints, so the declared controls
     do not move; the designer will set a lighter brand-blue step here later, as this ONE line.
     ⚖️ No component paints a checkbox from anything else — shared/__tests__/checkbox-paint-token.spec.js fails the
     build on a colour that is not this token. */
  /* RELEASE 3c (rev328e §2, owner): the designer's value arrives as the ONE line — the azure step of the brand blue,
     lighter than the navy it resolved to; the tick and the indeterminate bar are --ft-control-checked-ink, white. */
  --ft-control-checked:         #1F5FB0;
  --ft-control-checked-ink:     #FFFFFF;

  /* ═══ RELEASE 3c (rev328e §1, owner) · THE TOAST: success green, error the STATUS red (not the destructive action
     red #B40C0E), white ink on both — 6.02:1 and 7.46:1. Read by Settings' save toast (DirtyBar.vue). */
  --ft-toast-success-ground:    #19714A;
  --ft-toast-error-ground:      #A3231F;
  --ft-toast-ink:               #FFFFFF;
  --ft-toast-enter-ms:          220ms;
  --ft-toast-exit-ms:           180ms;

  /* ═══ 🔴 --ft-ground-inverse · LANDED 2026-09-21 BECAUSE rev197 CANNOT WORK WITHOUT IT ═══════
     NOT an ordered line and not a mint — the desk's own sheet publishes this name in two cuts
     (SEND-…-rev177 and -rev181 ft-tokens-fasttera.css), light #0E1A2B / dark #EDEFF3, and their
     rev197 note quotes contrast "17.48:1 light and 15.18:1 dark". Those two numbers reproduce
     ONLY against a layer where this token resolves; ours did not define it at all.
     ⚠️ WHAT THAT COST, MEASURED: rev197 moves the strip's count badge off `--ft-ink-strong`
     (an INK used as a GROUND, which inverts in dark) onto `var(--ft-ground-inverse, #0E1A2B)`
     + `var(--ft-ink-inverse, #FFFFFF)`. Undefined, the ground paints its literal fallback
     #0E1A2B, and a literal cannot flip. `--ft-ink-inverse` DOES flip: it is
     `var(--ft-source-paper)`, which the dark block below sets to #0E1A2B. So on the dark page
     the badge would have been #0E1A2B on #0E1A2B — 1.00:1, the count invisible. The exact
     failure rev197 was sent to cure, arriving through the half of the pair nobody defined.
     ⬜ Declared in BOTH halves rather than derived: no tier-1 source flips in this phase
     (--ft-source-paper runs the other way), which is why the designer's sheet hardcodes both
     too. It joins --ft-ground-disabled / --ft-ink-disabled as an exempt pair in the dark block.
     ⬜ Raised with the desk for confirmation. The value is theirs either way. */
  --ft-ground-inverse:          #0E1A2B;   /* with --ft-ink-inverse #FFFFFF — 17.48:1 */

  /* INK — a colour on something.
     NOTE: --ft-ink-on-brand and --ft-ground-page are the same pixel, #FFFFFF,
     and a DIFFERENT JOB. That is why they are two tokens and not one.
     (MR 140 — a colour's job is not one word. White is held by five tokens in
     the current system and that ambiguity is what blocks 162 of the conversion
     sites until the designer answers the job table.) */
  --ft-ink-strong:              var(--ft-source-ink);
  --ft-ink:                     var(--ft-source-ink-2);
  --ft-ink-muted:               var(--ft-source-ink-3);
  --ft-ink-subtle:              var(--ft-source-ink-4);
  --ft-ink-faint:               var(--ft-source-ink-5);

  /* ═══ ORDERED LINE · desk 2026-09-21 · rev197 PART 3 §2 ═══════════════════════════════════
     `--ft-ink-recede` was REFERENCED by the pack and DEFINED by nobody. It carried a line in
     PAINTS_ITS_FALLBACK for exactly that reason; the desk drove it live and a bare
     `var(--ft-ink-recede)` resolved to the browser's default grey (#525252), not to anything
     either side meant. Every reader that painted correctly did so through the fallback.
     ⬜ A LITERAL, NOT A SCALE STEP: #5A6473 is not --ft-source-ink-3 (#475569) or -4 (#64748B),
     and snapping it to one would change the seven sites the desk measured.
     ⚠️ ITS DARK HALF LIVES SOMEWHERE ELSE. Its two role-siblings --ft-ink-chrome and
     --ft-ink-chrome-muted are declared in ft-tokens-brand-delta-189.css, which carries a dark
     block; that file is desk-delivered and hash-pinned, so this line cannot join them there.
     When the dark pass opens (the parked audit guard), this token needs a dark value and it
     will be found in a different file from its siblings. Raised, not worked around. */
  --ft-ink-recede:              #5A6473;
  --ft-ink-on-brand:            var(--ft-source-paper);   /* on #0A2344 — 15.71 */
  --ft-ink-on-accent:           var(--ft-source-paper);   /* ONLY ever on --ft-accent-strong — 5.08 */
  --ft-ink-inverse:             var(--ft-source-paper);

  /* ═══ ORDERED LINE · desk rev207 · 2026-09-22 ═══════════════════════════════════════════════
     `--ft-ink-brand` — the brand INK, and the vocabulary owed it. The User row's object icon is
     brand navy and there was no JOB for that colour, which is what pushed rev206 into two wrong
     answers in a row. Light 15.71:1; the dark half below is 8.34:1 with b* negative, so it still
     reads navy rather than drifting to a neutral blue-grey.
     ⚠️ NOT `--ft-brand`, WHICH ALREADY EXISTS AND IS A GROUND. Same hex today, different job:
     one is the colour of the brand surface, this is the colour of type ON a page. That the two
     agree in light is a coincidence of this palette — the dark halves part company, which is
     exactly why the ruling minted a second name instead of reusing the first. */
  --ft-ink-brand:               #0A2344;   /* 15.71:1 on white */
  /* 2026-09-23 (VERSION -m) · ink.text — the RECEDE ink as a JOB (desk rev219: 4.6:1 on the page;
     secondary type, not a glyph colour). Same value as --ft-ink-recede today; a second NAME because
     they are two jobs and may part company in dark. Minted on the desk's order; leaves the
     PAINTS_ITS_FALLBACK register in the same commit. */
  --ft-ink-text:                #5A6473;
  /* ink.premium — the brass accent AS INK (desk order 2026-09-23; CLAUDE.md "Premium accent: brass") */
  --ft-ink-premium:             #B8862F;

  /* EDGE — a colour that separates */
  --ft-edge:                    var(--ft-source-line);
  --ft-edge-field:              var(--ft-source-line-field);   /* 80 uses — inputs */
  --ft-edge-soft:               var(--ft-source-line-soft);
  --ft-edge-strong:             var(--ft-source-ink-5);
  --ft-edge-focus:              var(--ft-accent);

  /* ── 🔴 THE FOCUS RING IS A PAIR, BY MEASUREMENT. rev 2026-09-16-d ───────────
     `--ft-edge-focus` above is KEPT so nothing breaks. It is also NOT SUFFICIENT
     on its own, and the numbers say so rather than a preference:

       --ft-edge-focus  #00A4BD  on the white page      2.98 : 1   ✗ FAILS
                                 on the canvas #FBFCFD  2.90 : 1   ✗ FAILS
                                 on the navy button     5.27 : 1   ✓

     WCAG 2.4.11 sets a 3:1 floor for a focus indicator. So the current single
     token is BELOW the floor on every light surface in the product — which is
     most of it — and passes only on navy.

     ⚖️ AND NO SINGLE VALUE CAN FIX IT. The designer measured the same wall from
     the other side: azure #1F5FB0 is 6.32 on the page and 2.49 on navy. One
     colour passes light and fails dark; the other passes dark and fails light.

       --ft-edge-focus-on-light  #1F5FB0   6.32 on page · 5.95 on canvas   ✓
       --ft-edge-focus-on-dark   #FFFFFF  15.71 on navy · 1.00 on page     ✓ on dark only

     THE CONSUMER PICKS BY GROUND, not by component. A dark ground declares
     itself and the ring flips — see the rule at the foot of this file. */
  --ft-edge-focus-on-light:     #1F5FB0;
  --ft-edge-focus-on-dark:      #FFFFFF;

  /* dimensions, because 1px fails 2.4.11 at most sizes and the offset is what
     keeps the ring clear of the glyph it surrounds */
  --ft-ring-width:              2px;
  --ft-ring-offset:             2px;

  /* STATUS — role level, not component level */
  --ft-status-success:          var(--ft-source-success);
  --ft-status-warning:          var(--ft-source-warning);
  --ft-status-danger:           var(--ft-source-danger);
  --ft-status-info:             var(--ft-source-info);
  --ft-status-neutral:          var(--ft-source-ink-4);

  --ft-status-success-tint: color-mix(in srgb, var(--ft-status-success) 10%, white);
  --ft-status-warning-tint: color-mix(in srgb, var(--ft-status-warning) 10%, white);
  --ft-status-danger-tint:  color-mix(in srgb, var(--ft-status-danger)  10%, white);
  --ft-status-info-tint:    color-mix(in srgb, var(--ft-status-info)    10%, white);

  /* the ink that sits ON those tints — authored, not computed */
  --ft-status-success-ink:      var(--ft-source-success-ink);
  --ft-status-warning-ink:      var(--ft-source-warning-ink);
  --ft-status-danger-ink:       var(--ft-source-danger-ink);
  --ft-status-info-ink:         var(--ft-source-info-ink);
  --ft-status-neutral-tint:     var(--ft-source-surface-3);

  /* THE DISABLED PAIR — measured, exempt, and DELIBERATE.
     ratio 1.96 · WCAG 1.4.3 exempt (inactive component) · doNotFix.
     Raising this contrast would make disabled controls look enabled.
     The failure IS the fix. Do not "improve" these two values. */
  --ft-ground-disabled:         #EEF0F3;
  --ft-ink-disabled:            #A6AEBA;

  /* EFFECTS — these are where a literal hides. They are tier 2, not tier 1. */
  --ft-shadow-focus:   0 0 0 3px color-mix(in srgb, var(--ft-accent) 35%, transparent);
  --ft-shadow-sm:      0 1px 2px 0 color-mix(in srgb, var(--ft-source-ink) 5%, transparent);
  --ft-shadow-md:      0 4px 6px -1px color-mix(in srgb, var(--ft-source-ink) 7%, transparent);
  --ft-shadow-lg:      0 10px 15px -3px color-mix(in srgb, var(--ft-source-ink) 8%, transparent);
  --ft-gradient-brand: linear-gradient(135deg, var(--ft-brand) 0%, var(--ft-accent) 100%);
  --ft-overlay:        color-mix(in srgb, var(--ft-source-ink) 30%, transparent);
}


/* =============================================================================
   TIER 3 · COMPONENT — never a literal, never tier 1. What a colour is FOR.
   ============================================================================= */

:root {

  /* button */
  --ft-button-ground:           var(--ft-ground-brand);
  --ft-button-ground-hover:     var(--ft-brand-hover);
  --ft-button-ground-active:    var(--ft-brand-active);
  --ft-button-ink:              var(--ft-ink-on-brand);
  --ft-button-edge:             transparent;

  /* table — the component the contract is written for */
  /* REMOVED 2026-10-05 (desk S206): `--ft-table-head-ground: var(--ft-ground-page)` — the WHITE header, declared
     here and then re-declared as the OR-T1 grey further down this same file, where the later declaration won.
     It had been dead for a year and read as the truth by anyone who grepped for the first match. The grey one
     (owner's OR-T1 ruling, 2026-09-24) is now the ONLY declaration, and ft-table-no-zebra.spec.js pins that there
     is exactly one. */
  /* 2026-09-23 · the desk measured the served header at #64748B (= --ft-ink-subtle) against the ruled
     #5A6473; the head ink now reads the ink.text JOB so token and paint agree */
  --ft-table-head-ink:          var(--ft-ink-text);
  --ft-table-head-edge:         var(--ft-edge);
  --ft-table-row-ground:        var(--ft-ground-page);
  /* REMOVED 2026-10-05 (desk S206): `--ft-table-row-ground-alt: var(--ft-ground-surface)` — the zebra's colour.
     It exists in the designer's material (his base sheet inbox/tokens-e.css rev265, and his editable-token
     registry rev328n) but is tagged there as `derived`, with the reason "Reads --ft-ground-surface. Edit that
     token instead." Nothing in the pack paints with it; we were its only consumer. Read --ft-ground-surface. */
  --ft-table-row-ground-hover:  var(--ft-ground-surface-2);
  --ft-table-row-ground-selected: var(--ft-accent-tint);
  --ft-table-row-edge:          var(--ft-edge-soft);
  --ft-table-sort-ink:          var(--ft-ink-strong);
  /* ⚠️ rev207 · REPOINTED AT THE RULED SCALE, and the numbers moved. These were 56/72, measured
     on HubSpot before the density ruling existed. rev207 names three steps — compact 40 ·
     standard 48 · comfortable 56 — and derives them from the CONTROL scale, so a row height stops
     being its own private number. The default is now STANDARD, which is what `FTTable.ROW_H`
     resolves to, so the token layer and the tier agree instead of racing.
     🔴 THIS IS VISIBLE ON `UniversalDataTable` (Users & Teams), which reads `--ft-table-row-h`
     directly: its rows go 56 → 48. That is the ruled default arriving on the surface the desk
     flagged for the port, not a side effect to be discovered later.
     ⬜ The three views that pass `:row-height="72"` are UNAFFECTED — an inline custom property
     beats this declaration — so nothing was re-measured behind their authors' backs. */
  --ft-table-row-h:             var(--ft-control-6);   /* STANDARD 48 — the ruled default */
  /* ⚠️ 72 IS THE PORT'S ROOMY VARIANT, NOT THE RULING'S COMFORTABLE STEP (56), AND CI SETTLED
     THAT. This was repointed at --ft-control-7 in the same commit that intaked rev207, on the
     claim that nothing visible depended on it. Three specs failed immediately: three Data
     Model views pass :row-height="72" from their own sheets and are drawn against
     `.ft-table--comfortable`, which reads this token. The number stays until those sheets
     are re-measured onto the named scale — raised with the desk, not decided here. */
  --ft-table-row-h-comfortable: 56px;   /* owner D-W5-0034 (d), 11 Oct: HubSpot Comfortable (rev365), replacing the port's 72 */
  /* ⬜ WHAT THE OLD COMMENT SAID, KEPT BECAUSE IT IS STILL TRUE OF ONE HALF: "There is NO dense
     variant on HubSpot. If we want one it is ours to invent and must be declared as ours." rev207
     INVENTED it and declared it — `compact` 40 (--ft-control-5) — so the sentence is answered
     rather than deleted. The other half is superseded: 72 was this layer's comfortable, and the
     ruling's is 56. Recorded, not silently overwritten. */

  /* dialog */
  --ft-dialog-ground:           var(--ft-ground-raised);
  --ft-dialog-ink:              var(--ft-ink);
  --ft-dialog-edge:             var(--ft-edge);
  --ft-dialog-scrim:            var(--ft-overlay);

  /* nav */
  --ft-nav-ground:              var(--ft-ground-page);
  --ft-nav-ink:                 var(--ft-ink-muted);
  --ft-nav-ink-current:         var(--ft-brand);
  --ft-nav-edge:                var(--ft-edge);

  /* the accent bar — one rule, eleven hues.
     Replaces eleven hand-written `border-left: 4px solid <literal>` rules, which
     are also eleven of the twenty RTL conversions. See THE-RTL-TWENTY. */
  --ft-accent-bar:              var(--ft-status-info);

  /* chat */
  --ft-bubble-ground-in:        #F1F2F4;  /* ⚠ tier-1 candidate, awaiting the job table */
  --ft-bubble-ground-out:       var(--ft-accent-tint);
  --ft-bubble-ink:              var(--ft-source-ink-2);


  /* ═══════════════════════════════════════════════════════════════════════════
     THEME MODULE · STEP 1 — NAMES ONLY.  Owner ruled 2026-09-16.

     Every token below points at an EXISTING tier-2 role. Not one new value and
     not one new decision is made here. Values land in Step 2, after the
     designer's job table and the owner's three sentences — because four jobs in
     the vocabulary still hold more than one value and one of them governs 1,533
     sites. Minting a value against that would mint it twice. (MR 145.)

     The names exist NOW so that the tab strip and the rail are BUILT against
     them once, instead of built and then renamed.
     ═══════════════════════════════════════════════════════════════════════════ */

  /* TABS — ONE LOOK, THREE BEHAVIOURS.  Owner ruled 2026-09-16.
     nav tabs (<a> + aria-current="page") · view tabs (role=tab, roving, closable)
     · inner tabs (role=tab, roving).  They share every token below.
     The ✕ and the count badge are CAPABILITIES, not styles: a tab has them or it
     does not, and that never changes which component it is. */
  --ft-tab-ground:              transparent;
  --ft-tab-ground-hover:        var(--ft-ground-surface);
  --ft-tab-ground-current:      var(--ft-ground-page);
  --ft-tab-ink:                 var(--ft-ink-subtle);
  --ft-tab-ink-current:         var(--ft-ink-strong);
  --ft-tab-edge:                var(--ft-edge);
  --ft-tab-radius:              8px;
  --ft-tab-h:                   36px;
  --ft-tab-gap:                 4px;

  /* the one line that keeps "changes the page" legible from "filters the table"
     when both strips sit on one screen. Set --ft-tab-current-rule to 0 to make
     them perfectly identical — a preference, not a defect, and the owner's. */
  --ft-tab-current-rule:        2px;
  --ft-tab-current-rule-ink:    var(--ft-brand);
  --ft-tab-view-current-rule:   0;

  /* tab capabilities */
  --ft-tab-count-ground:        var(--ft-status-neutral-tint);
  --ft-tab-count-ink:           var(--ft-ink-subtle);
  --ft-tab-close-ink:           var(--ft-ink-faint);
  --ft-tab-close-ink-hover:     var(--ft-ink);

  /* THE RAIL — the sidebar.  Owner ruled 2026-09-16: CRM Record's navRailEl is
     THE SOURCE; CRM Index consumes it; Workflows is read before it is folded in.
     🔴 A NAME MAY NOT BE A COLOUR. `--ft-navy-900` in a rail is a
        find-and-replace; `--ft-rail-ground` is a theme. */
  --ft-rail-ground:             var(--ft-ground-page);
  --ft-rail-edge:               var(--ft-edge);
  --ft-rail-item-ink:           var(--ft-ink-muted);
  /* ═══ 🔴 RULED 2026-09-22 · the dead twin of rev201's active-nav fix ═══════════════════════
     rev201 moved `app-shell.js`'s active nav ink off `--ft-brand-primary` onto
     `--ft-accent-strong`, because a foreground and its background must not come from one family:
     invisible in light (both navy), 1.3:1 in dark where they lift together. THIS token is the
     same derivation in the CSS layer — the half rev201's fix cannot reach.
     ⬜ Read by nobody today (no `var(--ft-rail-item-ink-current)` anywhere), so it was not a live
     defect. The desk ruled POINT IT, don't retire it: a token that means "the active nav row's
     ink" must be correct the day a surface wires it, and no-delete/refactor says the name stays.
     ⚠️ ITS DARK HALF IS NOT FIXED AND CANNOT BE FROM HERE. `ft-tokens-brand-delta-189.css` line
     124 redeclares this name as `var(--ft-brand-primary)` inside its dark block; that file is
     desk-delivered and hash-pinned, and it loads AFTER this one, so it wins in dark. It needs an
     ordered line — deliberately NOT chased now, per the same send's ruling that dark work pauses
     until a real dark switch exists. Recorded so the remaining half is not mistaken for done. */
  --ft-rail-item-ink-current:   var(--ft-accent-strong);
  --ft-rail-item-ground-current: var(--ft-ground-surface);
  --ft-rail-item-ground-open:   var(--ft-ground-surface-2);
  --ft-rail-icon:               var(--ft-ink-subtle);
  --ft-rail-w:                  240px;
  --ft-rail-w-collapsed:        56px;
  --ft-rail-item-h:             36px;


  /* ═══════════════════════════════════════════════════════════════════════════
     THE VIEWS · one token set, four behaviours.  2026-09-16.

     Every geometry value below was READ OFF A RUNNING HUBSPOT SCREEN and is
     recorded in THE-VIEW-CONTRACTS-2026-09-16.md with the surface it came from.
     Every colour is a var() of an existing tier-2 role — no view introduces a
     value of its own.

     🔴 TWO VIEWS HAVE NO SOURCE AND ARE MARKED AS OURS, NOT ATTRIBUTED:
        the CALENDAR  — /calendar/ renders 424 nodes of chrome, 0 day cells
        the DENSE table variant — HubSpot has 40/56/72 and no dense
     ═══════════════════════════════════════════════════════════════════════════ */

  /* ROW DENSITY — three, measured, and a view picks one. It does not invent a fourth.
     40 = the CRM list/index · 56 = the settings table · 72 = properties */
  --ft-row-h-compact:           40px;
  --ft-row-h:                   56px;
  --ft-row-h-roomy:             72px;

  /* shared across every view that shows records */
  --ft-view-ground:             var(--ft-ground-page);
  --ft-view-ground-sunken:      var(--ft-ground-surface);
  --ft-view-edge:               var(--ft-edge);
  --ft-view-ink:                var(--ft-ink);
  --ft-view-ink-muted:          var(--ft-ink-subtle);
  --ft-view-ground-hover:       var(--ft-ground-surface-2);
  --ft-view-ground-selected:    var(--ft-accent-tint);
  --ft-view-focus-ring:         var(--ft-shadow-focus);
  --ft-view-gutter:             16px;
  --ft-view-radius:             8px;

  /* the COUNT badge — a board column, a view tab, a filter chip all use this one.
     Measured: HubSpot shows the count as a loose number beside the label on all
     three. Ours is part of the accessible name, and this is only its paint. */
  --ft-count-ground:            var(--ft-status-neutral-tint);
  --ft-count-ink:               var(--ft-ink-subtle);
  --ft-count-radius:            10px;

  /* the EMPTY STATE — one rule for every view.
     Measured: the table's, the properties table's and the dashboard's are ALL
     bare <div>s with no role and no aria-live. Ours is role="status". */
  --ft-empty-ink:               var(--ft-ink-subtle);
  --ft-empty-ground:            transparent;
  --ft-empty-pad:               48px;

  /* BOARD · measured on the Deals pipeline */
  --ft-board-ground:            var(--ft-ground-surface);
  --ft-board-col-w:             280px;   /* measured */
  --ft-board-col-ground:        var(--ft-ground-page);
  --ft-board-col-edge:          var(--ft-edge);
  --ft-board-col-head-ink:      var(--ft-ink);
  --ft-board-col-gap:           12px;
  --ft-board-card-w:            264px;   /* measured */
  --ft-board-card-ground:       var(--ft-ground-page);
  --ft-board-card-edge:         var(--ft-edge);
  --ft-board-card-radius:       var(--ft-view-radius);
  --ft-board-card-shadow:       var(--ft-shadow-sm);
  --ft-board-card-shadow-drag:  var(--ft-shadow-lg);
  --ft-board-card-gap:          8px;
  --ft-board-drop-ground:       var(--ft-accent-tint);   /* the drop target, which HubSpot does not paint */
  --ft-board-drop-edge:         var(--ft-accent);

  /* REPORT · measured on the CRM Data Overview dashboard */
  --ft-report-ground:           var(--ft-ground-surface);
  --ft-report-card-w:           389px;   /* measured */
  --ft-report-card-h:           190px;   /* measured */
  --ft-report-card-ground:      var(--ft-ground-page);
  --ft-report-card-edge:        var(--ft-edge);
  --ft-report-card-radius:      var(--ft-view-radius);
  --ft-report-card-shadow:      var(--ft-shadow-sm);
  --ft-report-card-gap:         16px;
  --ft-report-title-ink:        var(--ft-ink-strong);
  --ft-report-meta-ink:         var(--ft-ink-subtle);

  /* CHART · ⚠️ OURS. The portal had no chart data, so nothing here was measured.
     The series ramp is deliberately NOT the status colours — a series is not a
     state, and reusing danger-red for "series 3" is how a chart lies. */
  --ft-chart-series-1:          var(--ft-brand);
  --ft-chart-series-2:          var(--ft-accent);
  --ft-chart-series-3:          var(--ft-source-azure);
  --ft-chart-series-4:          var(--ft-source-warning-ink);
  --ft-chart-series-5:          var(--ft-source-ink-3);
  /* ═══ ORDERED LINE · desk rev207 (carrying rev238 G5) · owner ruling 2026-09-25 (S011) ════════════════════════════
     The designer's reason, verbatim from his delta: "the chart ramp had five series; Audit Log card 1 draws five
     categories and series-3 (azure) is indistinguishable from series-2 (accent) at bar size. A sixth series is
     declared as the brand's premium brass." Landed as he wrote it — the literal is his, and #B8862F is the same value
     our --ft-ink-premium carries, so pointing this at that token would be a RE-POINT nobody ordered. Found missing by
     the T-1 parity spec's first CI run: three of rev207's four lines had landed and this one never did. */
  --ft-chart-series-6:          #B8862F;
  --ft-chart-grid:              var(--ft-edge-soft);
  --ft-chart-axis-ink:          var(--ft-ink-subtle);
  --ft-chart-label-ink:         var(--ft-ink-muted);

  /* CALENDAR · ⚠️ OURS ENTIRELY. HubSpot has no calendar view on this portal.
     Names only, values provisional, and this block is the one place in the file
     where a value was not read off a screen. It is marked so it cannot be
     quoted as measured. */
  --ft-cal-ground:              var(--ft-ground-page);
  --ft-cal-edge:                var(--ft-edge);
  --ft-cal-head-ink:            var(--ft-ink-subtle);
  --ft-cal-day-h:               var(--ft-row-h-roomy);
  --ft-cal-day-ground:          var(--ft-ground-page);
  --ft-cal-day-ground-other:    var(--ft-ground-surface);   /* outside the month */
  --ft-cal-today-ground:        var(--ft-accent-tint);
  --ft-cal-today-ink:           var(--ft-brand);
  --ft-cal-event-ground:        var(--ft-accent-tint);
  --ft-cal-event-ink:           var(--ft-ink);
  --ft-cal-event-h:             22px;
}


/* =============================================================================
   DARK — redefine TIER 1 ONLY. Tier 2 and 3 follow for free.
   This is the whole point of the three tiers: dark mode is ~15 lines, not 68.
   ============================================================================= */

/* ═══ 🔴 rev201's LESSON, APPLIED IN THE MIRROR · 2026-09-22 ═══════════════════════════════
   The desk's sheet matched `[data-theme="dark"]` alone while the port's switch writes
   `data-ft-theme`, so under our attribute their grounds never flipped — 166 white surfaces.
   THIS FILE HAS THE SAME SPLIT, THE OTHER WAY ROUND: it matched `data-ft-theme` only, while
   every served brand delta (182 · 183 · 189) already matches BOTH, by rev182's stated decision.
   ⚠️ So a page that set `data-theme="dark"` — the pack's own THEME page does — got the deltas
   inverted and tier 1 left in light: a HALF-inverted palette, which is worse than either whole
   one and reads as a pile of unrelated ink bugs. One selector closes it.
   ⬜ NOTHING IN THIS REPOSITORY WRITES EITHER ATTRIBUTE YET — grepped, not assumed. Dark has no
   producer on our surface; it is reachable only by hand, which is how it was verified. This is
   a latent trap being closed before the switch exists, not a live defect. */
:root[data-ft-theme="dark"],
:root[data-theme="dark"] {
  --ft-source-paper:            #0E1A2B;
  --ft-source-surface:          #142235;
  --ft-source-surface-2:        #1B2C42;
  --ft-source-ink:              #F8FAFC;
  --ft-source-ink-2:            #E2E8F0;
  --ft-source-ink-3:            #CBD5E1;
  --ft-source-ink-4:            #94A3B8;
  --ft-source-ink-5:            #64748B;
  --ft-source-line:             #24384F;
  --ft-source-line-soft:        #1B2C42;

  /* the two exempt values invert as a PAIR or not at all —
     their ratio is the design, not their values */
  --ft-ground-disabled:         #1B2537;
  --ft-ink-disabled:            #566173;

  /* ⬜ a THIRD exempt value, for the same reason and not a new kind of thing. Its partner
     --ft-ink-inverse resolves through --ft-source-paper and so flips to #0E1A2B here; the
     ground has to flip the other way or the pair collapses to 1.00:1. The desk's sheet says
     #EDEFF3 and drove it at 15.18:1 — reproduced exactly against this ink. */
  --ft-ground-inverse:          #EDEFF3;   /* with --ft-ink-inverse #0E1A2B — 15.18:1 */

  /* rev207 — the brand ink's dark half. 8.34:1 on the dark page, b* negative so it stays NAVY;
     a neutral lift would have kept the contrast and lost the brand, which is the whole reason
     this is its own token and not a reuse of --ft-brand. */
  --ft-ink-brand:               #8FB4E8;
  /* 2026-09-23 · ink.text dark value NOT YET ORDERED — carries the light value until the desk rules it */
  --ft-ink-text:                #5A6473;
  /* ink.premium dark — desk review 2026-09-23: a warmer, higher-luminance gold for dark surfaces */
  --ft-ink-premium:             #DDB65C;
}


/* =============================================================================
   🔴 PROVEN LIVE, AND IT BLINDS OUR OWN INSTRUMENTS — READ THIS

   Driven in the port's own browser, 2026-09-16:

     CSS.supports('color','color-mix(in srgb, red 50%, blue)')   ->  true
     --t-hover: color-mix(in srgb, var(--t-brand) calc(100% - var(--t-mix)), black)
       resolves. Nested var() inside color-mix inside calc() works.
     The focus shadow with an alpha mix resolves.

   BUT the computed value comes back as:

     color(srgb 0.0337255 0.118039 0.229333)        <- NOT #081E3A
     color(srgb 0 0.643137 0.741176 / 0.35)         <- NOT rgba(0,164,189,.35)

   Every colour scanner we own — mine and the designer's ft-tokens.js — matches
   on  /#[0-9a-f]{3,8}|rgba?\(|hsla?\(/  and will silently MISS every
   color-mix output. The moment this file lands, a census that reports
   "0 literals" will be reporting on itself.

   BEFORE this file is merged, both scanners add  color\(  to their pattern and
   must print a NON-ZERO on a page known to contain one.
   (MR 159 — a zero is a claim about the instrument until the instrument has
    printed a non-zero on the same run.)
   ============================================================================= */


/* =============================================================================
   RTL — direction is a token too.
   All 20 physical-direction declarations in the product are listed in
   THE-RTL-TWENTY-2026-09-16.md. New component CSS uses logical properties only:
     padding-inline / margin-inline / inset-inline / border-inline
     text-align: start | end        (never left | right)
   and the dialog must NOT carry a hardcoded dir="ltr".
   ============================================================================= */

/* ═══════════════════════════════════════════════════════════════════════════
   THE FOCUS RING — THE RULE THAT CONSUMES THE PAIR.        rev 2026-09-16-d
   Appended, not substituted. Nothing above is removed.

   :focus-visible, not :focus — a mouse click must not paint a ring.
   The inert exclusions are in the selector so a declared-dead control cannot
   pick up a live state by accident.
   ═══════════════════════════════════════════════════════════════════════════ */

:focus-visible {
  outline: var(--ft-ring-width) solid var(--ft-edge-focus-on-light);
  outline-offset: var(--ft-ring-offset);
}

/* a dark ground DECLARES itself and the ring flips. Mark any navy/dark surface
   with data-ft-ground="dark" or .ft-on-dark — do not guess from the component. */
[data-ft-ground="dark"] :focus-visible,
.ft-on-dark :focus-visible {
  outline-color: var(--ft-edge-focus-on-dark);
}

/* inert controls take no ring */
[disabled] :focus-visible,
[aria-disabled="true"] :focus-visible { outline: none; }


/* =============================================================================
   TIER 1 · THE DIMENSION SOURCE — SPACE · SIZE · TYPE · ELEVATION · MOTION · LAYER
   Added 2026-09-16 (VERSION -e). Nothing above is changed or removed.

   Approved by the owner in RULING-2026-09-16-THE-THREE-SCALES, with Amendment 1
   (border · icon · control, found by the sidebar) and Amendment 2 (control-7,
   found by the table). FITTED TO THE MEASURED PRODUCT, not to a 4px grid:
   6 · 10 · 14 · 18 · 22 are 39% of all spacing, so a 4px grid was refused.

   🔴 PREFERRED vs LEGACY is marked per step. PREFERRED is a TIGHTENING TARGET,
   not the six most-used values — stated because the first version of this scale
   did not say so and a new component was pushed onto a legacy step.
   ============================================================================= */

:root {

  /* ── SPACE · a 2px rhythm. px, not rem: density is OUR decision. ─────────── */
  --ft-space-1:   2px;    /* legacy  */   --ft-space-9:   18px;  /* legacy · 213 uses */
  --ft-space-2:   4px;    /* PREFERRED */ --ft-space-10:  20px;  /* legacy  */
  --ft-space-3:   6px;    /* legacy · 653 uses */
  --ft-space-4:   8px;    /* PREFERRED · 674 uses — the most-used spacing value */
  --ft-space-5:  10px;    /* legacy · 494 uses */
  --ft-space-11:  22px;   /* legacy  */
  --ft-space-6:  12px;    /* PREFERRED · 421 uses */
  --ft-space-12:  24px;   /* PREFERRED */
  --ft-space-7:  14px;    /* legacy · 381 uses */
  --ft-space-13:  32px;   /* PREFERRED */
  --ft-space-8:  16px;    /* PREFERRED · 296 uses */
  --ft-space-14:  40px;   /* legacy  */
  --ft-space-15:  48px;   /* legacy  */

  /* ── SIZE · radius ───────────────────────────────────────────────────────── */
  --ft-radius-1:    4px;
  --ft-radius-2:    6px;
  --ft-radius-3:    8px;     /* 497 uses — the most-painted radius we own */
  --ft-radius-4:   12px;
  --ft-radius-pill: 999px;   /* 235 uses. A sentinel, not a length. Do not "fix". */

  /* ── SIZE · border width.  1px is 96% of every border in the product. ────── */
  --ft-border-1:  1px;
  --ft-border-2:  2px;
  --ft-border-3:  3px;   /* A3-T · D-A3-T-0012 §2: the selected list row's inset accent bar (pack: Announcements :308) */
  /* ⚠️ 1.5px (45 uses) and 2.5px (3) are SUB-PIXEL BORDERS — the same defect
     family as the half-pixel type. They snap to 1px and 2px. */

  /* ── SIZE · icon box ─────────────────────────────────────────────────────── */
  --ft-icon-1: 12px;  --ft-icon-3: 16px;  /* 116 uses — dominant */  --ft-icon-5: 20px;
  --ft-icon-2: 14px;  --ft-icon-4: 18px;                             --ft-icon-6: 24px;
  /* 🔴 AN ICON SIZE CANNOT BE A REFERENCE AT THE CALL SITE THE WAY A COLOUR CAN.
     <svg width> is a PRESENTATION ATTRIBUTE: it sits BELOW the cascade, so a
     var() there is a default, not a value, and loses to any stylesheet rule.
     Reference these from CSS only. Found by the designer building the tabs. */

  /* ── SIZE · control height ───────────────────────────────────────────────── */
  --ft-control-1: 24px;  --ft-control-4: 36px;  /* 81  */  --ft-control-7: 56px;
  --ft-control-2: 28px;  --ft-control-5: 40px;  /* 134 — most used */
  --ft-control-3: 32px;  --ft-control-6: 48px;
  /* --ft-control-7 exists because the table row IS 56px. Snapping it to 48 would
     have shrunk every row in the product by 8px. MR 216. */

  /* ── SIZE · reading width (A3-T · D-A3-T-0046 §1, B213 / B224, W8 ASK 5) ──────
     The measure of a running-text column: the 60ch the Theme already typed for the empty
     state's message. A part's own narrower cap (a gate's 44ch, a breadcrumb's 22ch) stays its own. */
  --ft-reading-width: 60ch;

  /* ── SIZE · tile grid (A3-T · D-A3-T-0024 §2; the accepted tree's rev662z-zb item 21 tile tokens, values verbatim) ──
     One source for channel/app tile grids. --ft-tile-min / --ft-tile-max: a tile in a tile grid is 156–180 px wide
     (Inbox & Help Desk › Channels: repeat(auto-fill, minmax(min(100%, min), max))). --ft-tile-min-h: a tile is at least
     140 px tall (the same grid). --ft-tile-min-chooser: a chooser card is at least 168 px wide (Connect Channel:
     repeat(auto-fit, minmax(min, 1fr))). Theme-neutral (no dark value). In the reference layer, NOT the authority sheet
     (ft-tokens-fasttera.css stays the Theme FINAL's file byte for byte — test_theme_d102_wiring). */
  --ft-tile-min: 156px;  --ft-tile-max: 180px;  --ft-tile-min-h: 140px;  --ft-tile-min-chooser: 168px;

  /* ── SIZE · table-cell measure (A3-T · r7 intake, TaskT3 R-TaskT3-0245; the pack's rev660 O5 bounds, values verbatim) ──
     A table's long-text cells are bounded, not left to take the sentence's full width: a SENTENCE cell (a lock reason, a
     note) is 160–220 px, a RULE cell 140–200 px, an ITEM (the row's name) at least 180 px. A page writes
     `minInlineSize: var(--ft-table-cell-sentence-min)` etc. — never the literal. Theme-neutral (no dark value). In the
     reference layer, NOT the authority sheet (ft-tokens-fasttera.css stays the Theme FINAL's file byte for byte). */
  --ft-table-cell-sentence-min: 160px;  --ft-table-cell-sentence-max: 220px;
  --ft-table-cell-rule-min: 140px;  --ft-table-cell-rule-max: 200px;  --ft-table-cell-item-min: 180px;

  /* ── STATE INDICATOR — a weight, not a border ────────────────────────────── */
  --ft-indicator-weight: 3px;   /* the active-tab underline. Named separately so
                                   that changing how emphatic an active tab looks
                                   does not move every 3px rule in the product. */

  /* ── TYPE · THE TWO SLOTS. The SCRIPT selects the face; the fallback chain never should.
        Ruled 2026-09-16; landed here 2026-09-18. A mixed stack lets the browser pick per glyph, so an
        Arabic sentence carrying a Latin product name renders in two faces at two optical sizes on one
        line. `:lang(ar)` selects the Arabic slot in ft-fonts.css, which also self-hosts its four
        weights — and which no longer declares these two names, because this file does. ───────────── */
  --ft-font-latin:   InterVariable, Inter, system-ui, -apple-system, sans-serif;
  /* ORDERED LINE · desk rev208 — Plex FIRST, Noto retained. rev208 ruled IBM Plex Sans Arabic
     the default Arabic face; until this commit nothing served it, so the stack fell through to
     Noto with no error anywhere. The bytes are hosted now (ft-fonts.css). Noto is not removed:
     it is the face that paints if Plex is ever unavailable, which is what a stack is for. */
  --ft-font-arabic:  'IBM Plex Sans Arabic', 'Noto Sans Arabic', system-ui, sans-serif;
  /* MONOSPACE · W5, desk 7 Oct — the one mono role (codes, keys, URLs, counts, a readout). Pages name this token,
     never a family. Desk ruling (option 2): it LEADS with the pack's ruled mono face, IBM Plex Mono (registry: «the
     desk, 2026-09-16»), self-hosted in ft-fonts.css (latin 400 · 500); then system faces. Arabic in a mono place has
     no glyphs in Plex Mono and falls through the list — a mono stack never names an Arabic face (the two-slot rule). */
  --ft-font-mono:    'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* ── TYPE · in rem, EXACT at a 16px root. The user may scale this. ───────── */
  --ft-text-1: 0.6875rem;  /* 11px */
  --ft-text-2: 0.75rem;    /* 12px */
  --ft-text-3: 0.8125rem;  /* 13px — the body size, 1,266 uses */
  --ft-text-4: 0.875rem;   /* 14px */
  --ft-text-5: 1rem;       /* 16px */
  --ft-text-6: 1.25rem;    /* 20px */
  --ft-text-7: 1.5rem;     /* 24px */

  /* 🔴 S107 5 (owner) · THE ROW TITLE'S WEIGHT, AS A TOKEN — "the notification titles are too faint".
     Measured on live: the type title in every All-notifications row rendered at 300, LIGHTER than its own 400
     description, so the title read weaker than the text under it. It goes to 500 (medium).
     ⚖️ A TOKEN AND NOT A LITERAL, per S058: a weight is a type decision and Tier-3 owns it. This is the first
     weight token in the file, which is why it is named for its ROLE (a row's title) rather than for its value —
     `--ft-weight-500` would have to be edited everywhere the day the role's weight changes.
     ⚠️ Arabic is the reason this mattered enough to rule on: at 300 it is the hardest of the two scripts to
     read, and the desk asked for it to be checked in Arabic specifically. */
  --ft-weight-row-title: 500;
  /* ⚠️ 12.5 · 13.5 · 11.5 · 10.5px are 36% of all font sizes — 1,528 uses.
     Sub-pixel type renders differently across browsers, OSes and zoom levels.
     12.5→13 · 13.5→14 · 11.5→12 · 10.5→11. Half a pixel. */

  /* ── TIER 1 · TYPE — the five LINE steps. Ruled 2026-09-18, landed 2026-09-19.
     Every one a multiple of 4, so a line box always lands on the spacing grid and a
     row height is COMPUTABLE rather than measured. ── */
  --ft-line-1:  16px;   /* on --ft-text-1/2  (11 · 12px)  labels · badges · captions     */
  --ft-line-2:  20px;   /* on --ft-text-3/4  (13 · 14px)  DENSE UI — rows, nav, controls */
  --ft-line-3:  24px;   /* on --ft-text-4/5  (14 · 16px)  body, and anything that WRAPS   */
  --ft-line-4:  28px;   /* on --ft-text-6    (20px)       headings                       */
  --ft-line-5:  32px;   /* on --ft-text-7    (24px)       large headings                 */

  /* ── TIER 2 · the ROLES. A component references a role, never a step — that is the
     whole point of the tier, and it is how a step can change without a sweep. ── */
  --ft-line-dense:    var(--ft-line-2);
  --ft-line-body:     var(--ft-line-3);
  --ft-line-heading:  var(--ft-line-4);

  /* ── TIER 3 · the table cell. BOTH lines — `cell-type` is fallback-carried today
     exactly as `cell-line` is, and missing it makes the designer's ghosts() print 1 on
     a commit that otherwise looks landed. (The desk's own order was written naming nine
     and the instrument found a tenth; the lesson is in that footnote, not in this one.)
     20px, NOT HubSpot's 24 — driven: rev 139 renders 41 · 49 · 57 and 20px, identical to
     rev 137. This DECLARES what it already renders rather than changing it. ── */
  --ft-table-cell-line:  var(--ft-line-dense);
  --ft-table-cell-type:  var(--ft-text-4);    /* 14px — what it carries today */

  /* ── LAYER · the z-index ladder. No bare number anywhere. ────────────────── */
  --ft-z-base: 0;  --ft-z-sticky: 100;  --ft-z-dropdown: 200;
  --ft-z-overlay: 300;  --ft-z-modal: 400;
  /* 🔴 HOTFIX 2 Oct (live on 8a24daa) · THE TOAST IS THE TOP RUNG — of EVERYTHING this app paints, not of this
     designer ladder. At 500 the pop-up arrived (the sound played) and painted UNDER `.ftset-host` (fixed, z 1040):
     elementsFromPoint at the toast hit the Settings page. Our surfaces go far above this ladder — the cover 1040,
     the bar 1090, the overlay root 1100, the dialogs and drawers at 10060–10100, Ctrl-K 99999, the compose drawer
     and the bot widget at 2147483000 — so the toast sits above all of them and below the int32 ceiling
     (2147483647). A notification that arrives over a dialog must be seen over it.
     ⚖️ Pinned by shared/__tests__/toast-top-rung.spec.js, which READS every z-index in the app: the next surface
     that picks a bigger number fails CI instead of hiding the pop-up live. Only NotifToast reads this token. */
  --ft-z-toast: 2147483600;

  /* ── MOTION ──────────────────────────────────────────────────────────────── */
  --ft-motion-fast: 120ms;   /* hover · focus · colour */
  --ft-motion-base: 200ms;   /* open · close · expand  */
  --ft-motion-slow: 320ms;   /* page and panel          */
  --ft-ease-standard: cubic-bezier(0.2, 0, 0.2, 1);
  --ft-ease-enter:    cubic-bezier(0, 0, 0.2, 1);
  --ft-ease-exit:     cubic-bezier(0.4, 0, 1, 1);
}


/* =============================================================================
   TIER 2 · DIMENSION ROLES — what a measurement IS.
   A density mode later redefines THESE, not the 22,355 call sites.
   ============================================================================= */

:root {
  --ft-inset-control:     var(--ft-space-4);    /* padding inside a button, input, nav item */
  --ft-inset-container:   var(--ft-space-8);    /* padding inside a card, panel, section    */
  --ft-gap-tight:         var(--ft-space-2);
  --ft-gap-default:       var(--ft-space-4);
  --ft-gap-loose:         var(--ft-space-8);
  --ft-stack-section:     var(--ft-space-12);   /* between sections on a page */

  --ft-radius-control:    var(--ft-radius-3);
  --ft-radius-container:  var(--ft-radius-4);
  --ft-hairline:          var(--ft-border-1);

  --ft-height-control:    var(--ft-control-3);  /* a standard button or input */
  --ft-height-row:        var(--ft-control-7);  /* a table row                */
  --ft-height-nav-item:   var(--ft-control-4);  /* a sidebar item — 36px = 20 + 8 + 8. ORDER 2026-09-17 (0ef50ba7 §1,
                                                   RULING THE-RAIL-IS-CRAMPED §4): was --ft-control-2 (28px), sampled
                                                   from the cramped rail (MR 235). Applied by the developer as ordered;
                                                   the desk folds it into the next VERSION.                            */

  --ft-type-body:         var(--ft-text-4);  /* THE THEME RELEASE (W2d, 29 Oct): rev612 D108 (1) body 14/24 — was text-3 */
  --ft-type-label:        var(--ft-text-2);
  --ft-type-heading:      var(--ft-text-6);
  --ft-type-meta:         var(--ft-text-3);  /* THE THEME RELEASE (W2d, W5 R-W5-0243): rev612's meta type, 13 */

  /* THE THEME RELEASE (W2d, desk D-W2d 29 Oct, from W5 R-W5-0240): the rows W5's parts read, at rev612's values
     (ft-tokens-fasttera.css, which loads after this sheet and already serves them — so no served look changes). */
  --ft-lock-opacity:                .5;                                       /* rev601 R4 · ONE locked opacity */
  --ft-action-destructive-disabled: var(--ft-ground-destructive);    /* rev612 F10 · the locked destructive keeps its red; rev658-final drops the fallback (D-W7-0040) */
  --ft-section-heading-size:        1.25rem;                                  /* 20 */
  --ft-section-heading-weight:      500;
  --ft-section-heading-lh:          28px;                                     /* rev601 · section line 28 */
}


/* =============================================================================
   TIER 3 · THE NAV — the component contract the specimen page describes.
   Every surface that renders a sidebar consumes THESE and declares no literal.
   ============================================================================= */

:root {
  --ft-nav-item-height:   var(--ft-height-nav-item);
  --ft-nav-item-inset:    var(--ft-space-6);      /* 12px inline. ORDER 2026-09-17 §1: was --ft-inset-control (8px)   */
  --ft-nav-item-radius:   var(--ft-radius-control);
  --ft-nav-item-gap:      var(--ft-gap-default);  /* the gap INSIDE an item, icon to label (the specimen's `gap`)      */
  --ft-nav-item-stack:    var(--ft-space-1);      /* 2px BETWEEN items. ORDER 2026-09-17 §1 wrote this value against
                                                     --ft-nav-item-gap ("currently 0"); that token is the icon↔label gap
                                                     and is 8px, so the 2px inter-item gap gets its own name (MR 154,
                                                     noted in the reply). The desk owns the name.                     */
  --ft-nav-item-type:     var(--ft-text-4);       /* 14px. ORDER 2026-09-17 §1: its OWN step — --ft-type-body stays
                                                     --ft-text-3 (1,266 uses)                                          */
  --ft-nav-icon:          var(--ft-icon-3);
  --ft-nav-section-type:  var(--ft-type-label);
  --ft-nav-section-gap:   var(--ft-stack-section);
  --ft-nav-rule:          var(--ft-hairline);   /* the 1px gutter — a BORDER, not a gap */

  /* THE RAIL — ruled 2026-09-18 from 24 measured labels, and it is HubSpot's number rather than the
     computed one. DEFENDED against English (longest label 180.3px, and that one is a content decision:
     `Recording & Transcription` is 50px longer than anything else and was sizing the column for all
     twenty-four); ASSERTED against Arabic, whose longest label does not exist yet because 213 strings
     are unauthored. Re-measure when the Arabic lands.
     ⚠️ The asymmetry in that sentence is the point: half of this number answers a question we can only
     half ask, and slack is not waste while half the content is unwritten. */
  --ft-rail-width:        256px;

  /* ═══ ORDERED LINE · desk rev201 · 2026-09-22 ═════════════════════════════════════════════
     `--ft-rail-row-pad` is the rail row's BLOCK padding, and it is a token for one reason: only
     CSS can ask how tall the window is. rev198 fixed `flex: '0 0 256px'` — a basis that under a
     column parent is a HEIGHT CAP, which hid 9 of 15 hubs — and that left the honest remainder:
     15 rows at 38px need 592px and a 540px window has 486 after the bar. The ruling is compress
     first, scroll only as a last resort.
     ⬜ THE STEPS ARE DERIVED FROM THE CONTENT, not picked for roundness: 9px → 38px rows (592),
     5px → 30 (490), 3px → 26 (430); 780 and 660 are where each next step becomes necessary with
     the 54px bar subtracted. 26px is the FLOOR — below it the row stops being a target, so under
     ~620px the rail scrolls, which is the last resort the ruling allows.
     ⚠️ `app-shell.js` reads it as `var(--ft-rail-row-pad, 9px)` from an inline style, which cannot
     ask a media query. Undefined, the fallback pins it at 9px and the compression never happens —
     the rail would simply overflow again, silently and only on short windows. */
  --ft-rail-row-pad:      9px;

  /* ═══ ORDERED LINE · desk rev207 · the TENANT table density ════════════════════════════════
     ⚠️ IT IS A NAME, NOT A LENGTH, and the desk was explicit about why: `FTTable` resolves
     `standard` to `--ft-control-6`, so a tenant spelled `48px` here would become a FOURTH place a
     row height is decided. rev207 exists because three files already held three answers.
     ⬜ This is the CLIENT-side default only. The multi-tenant default is a server setting with the
     mandatory branch field and server-side RBAC — designed, not built, and put to the owner in the
     reply on this commit. A browser value cannot be a tenant value. */
  --ft-table-density:     standard;
}

@media (max-height: 780px) { :root { --ft-rail-row-pad: 5px; } }
@media (max-height: 660px) { :root { --ft-rail-row-pad: 3px; } }

/* ═══ ORDERED LINES · desk S005 · designer rev263 · ft-popup on tokens · 2026-09-24 ═══════════════════════════════
   Appended verbatim from ft-tokens-fasttera.css § rev263 (sha256 of the delivered ft-popup.js 8fcdc3ad44169ccd…).
   The listbox / menu paint moved out of ft-popup.js listStyle()/optionStyle() into these nine names; the values are
   the component's own literals, moved, not changed (light theme renders pixel-identical; the designer's proof page
   compares 165 computed values, 0 differences in LTR and RTL). Four point at existing THEME roles; five hold the old
   values. The one declared difference: in dark, the active option takes the dark ramp's raised surface. */
:root {
  --ft-popup-ground: var(--ft-ground-page);           /* was #fff */
  --ft-popup-edge: var(--ft-edge-field);              /* was #E2E6ED */
  --ft-popup-ink: var(--ft-ink);                      /* was #41506A — unselected option */
  --ft-popup-ink-selected: var(--ft-ink-brand, #0A2344); /* was #0A2344 — selected option */
  --ft-popup-option-active: #F0F3F8;                  /* was #F0F3F8 — the active (keyboard / hover) option */
  --ft-popup-shadow: rgba(20, 20, 20, 0.16) 0 10px 30px; /* was rgba(20,20,20,.16) 0 10px 30px */
  --ft-popup-radius: 10px;                            /* was 10px */
  --ft-popup-option-radius: 6px;                      /* was 6px */
  --ft-popup-option-type: 12.5px;                     /* was 12.5px */
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-popup-option-active: var(--ft-ground-surface-2); /* the light #F0F3F8 had no dark value; the dark ramp's raised surface */
  --ft-control-checked: #4A86D6;   /* RELEASE 3c (rev328e §2): #1F5FB0 would be 2.77:1 on the dark page */
}

/* ═══ ORDERED LINES · T-1 · parity with the designer's chain · VERSION 2026-09-24-o · desk S010 §3, owner ruling 2026-09-24 ═══
   THE THEME IS THE DESIGNER'S. The parity spec (shared/__tests__/ft-token-parity.spec.js) resolves every role both
   chains declare — his: inbox/tokens-e.css → ft-tokens-fasttera.css → ft-tokens-brand-delta-207.css (rev265 cut) —
   ours: this file + the four deltas — and found 66 drifting roles, all downstream of 12 root declarations made here on
   2026-09-16 from the SOURCE ramp, before his BRAND layer (ft-tokens-fasttera.css §① the reconciled aliases, §② the
   names the frozen set does not carry) re-pointed them at the measured values. Only rev182 · 183 · 187 · 189 · 207
   ever reached this file as lines; his brand layer never did. These are HIS declarations, verbatim, for the roots
   that no desk-ordered delta and no owner ruling contradicts. The rest (--ft-ink-strong · --ft-edge-strong ·
   --ft-nav-ink · --ft-rail-item-ink · --ft-status-danger · --ft-source-accent(-strong) · --ft-rail-w · the rail's
   36/12/14 · --ft-table-row-h) stay as served and are HELD in the spec with the conflict each waits on (D005).
   ⚠️ The fallback literals ride inside his lines as delivered (a `var(name, #hex)` where the name resolves reads the
   name; where it does not, the fallback IS his value) — provenance over the one-literal rule, said here so the next
   reader does not "clean" them into a second producer. */
:root {
  --ft-ink:               var(--ft-ink-body, #41506A);                  /* was var(--ft-source-ink-2) #1F2937 — T-1's first read */
  --ft-ink-subtle:        var(--ft-ink-meta, #5A6473);                  /* was var(--ft-source-ink-4) #64748B */
  --ft-ink-faint:         #8A94A3;                                      /* was var(--ft-source-ink-5) #94A3B8 */
  --ft-edge-field:        var(--ft-edge-border, #E2E6ED);               /* was var(--ft-source-line-field) #CBD5E1 — T-1's second read */
  --ft-edge-soft:         var(--ft-edge-hairline, #EEF1F5);             /* was var(--ft-source-line-soft) #F1F5F9 */
  --ft-ground-surface:    var(--ft-ground-sunken, #F4F6F9);             /* was var(--ft-source-surface) #F8FAFC */
  --ft-ground-surface-2:   #EDEFF3;                                     /* was var(--ft-source-surface-2) #F5F8FA */
  --ft-brand-tint:        var(--ft-ground-tint-navy, #EAF0F8);          /* was color-mix(brand 10%, white) */
  --ft-brand-hover:       var(--ft-ground-brand-hover, #0F2E5A);        /* was color-mix(brand 86%, black) */
  --ft-brand-active:      var(--ft-ground-brand-deep, #071A33);         /* was color-mix(brand 74%, black) */
  --ft-status-danger-ink:  var(--ft-ink-status-negative, #A3231F);      /* was var(--ft-source-danger-ink) #991B1B */
  --ft-status-success-ink: var(--ft-ink-status-positive, #19714A);      /* was var(--ft-source-success-ink) #166534 */
  --ft-status-info-ink:    var(--ft-ink-interactive, #1F5FB0);          /* was var(--ft-source-info-ink) #1E40AF */
  --ft-status-danger-tint:  var(--ft-ground-status-negative, #FBEBEA);  /* was color-mix(danger 10%, white) */
  --ft-status-success-tint: var(--ft-ground-status-positive, #E7F3EC);  /* was color-mix(success 10%, white) */
  --ft-status-warning-tint: var(--ft-ground-status-warning, #F7EFDD);   /* was color-mix(warning 10%, white) */
  --ft-status-info-tint:   #EEF3FA;                                     /* was color-mix(info 10%, white) */
  --ft-rail-item-ink-current:   var(--ft-brand);                        /* tokens-e line 341; was var(--ft-accent-strong) #17497F */
  --ft-table-cell-line: 24px;      /* ft-table — cell line height */    /* was var(--ft-line-dense) 20px */
}

/* ═══ ORDERED LINES · rev267 · ft-table A–Z (OR-T1 grey header, owner 2026-09-24) · taken VERBATIM from the designer's
   ft-tokens-fasttera.css (rev267c cut, in the design-pack fixture). Append-only; the two head-ground roles are
   RE-POINTED here. ⚠️ `html:root`, NOT `:root`, and the reason is measured: hooks.py serves four brand deltas AFTER
   this file, so a plain `:root` line here loses to any of them that names the same role. rev267c hit it on the nav
   label (delta-187 → 16px) and answered with `html:root`; the same selector carries these lines for the same reason. */
html:root {
  --ft-ground-surface-3: #F3F4F6;                              /* the neutral tint (owner ruling OR-T1) */
  --ft-table-head-ground: var(--ft-ground-raised);             /* R39 (owner 12 Oct) white, supersedes OR-T1's grey; rev612 (W2d re-cut, 29 Oct) */
  --ft-table-head-ground-hover: var(--ft-ground-surface-2);    /* one step darker on the neutral spine, sortable headers only */
  --ft-table-pager-current-edge: var(--ft-edge-strong);        /* the current page number is outlined */
  --ft-table-head-h-index: 40px; --ft-table-head-h-settings: 44px;
  --ft-table-row-h-index: 40px; --ft-table-row-h-list: 47px; --ft-table-row-h-settings: 72px; --ft-table-row-h-two-line: 72px;
  /* THE THEME RELEASE (W2d, 29 Oct, W5 R-W5-0243): Settings rows 72 = rev612 (was 56); the pack sheet already served 72 */
}
/* ═══ ORDERED LINES · rev267b · THE 16 HELD ROLES, ENDED (developer T-1 parity D005 §1; desk VERDICT rev267) ·
   VERBATIM. One line per role, each naming the ruling it keeps. The owner's later nav rulings (rev267c, below) win
   over this block's 256 / 36 / 12, which is why they are written again there.
   ⚠️ `html:root` for the same measured reason as the block above: with a plain `:root`, delta-189's
   `--ft-edge-strong: var(--ft-source-ink-5)` (#94A3B8) beat this line and the served edge stayed dark. */
html:root {
  --ft-ink-strong: #141414;                         /* H1  · keeps the rev189 retraction (sheet #0E1A2B was the stale line) */
  --ft-tab-ink-current: var(--ft-ink-strong);       /* H1  · follows ink-strong */
  --ft-report-title-ink: var(--ft-ink-strong);      /* H1  · follows ink-strong */
  --ft-table-sort-ink: var(--ft-ink-strong);        /* H1  · follows ink-strong */
  --ft-edge-strong: #D7DCE4;                        /* H2  · designer's call: the light edge. Table row rules, the head edge and the rev267 pager
                                                             outline read it, and rev267 was accepted with it; #94A3B8 would draw dark lines under every row */
  --ft-ink-chrome-muted: #52627C;                   /* H3/H4 · rev189 */
  --ft-nav-ink: var(--ft-ink-chrome-muted);         /* H3  · rev189 */
  --ft-rail-item-ink: var(--ft-ink-chrome-muted);   /* H4  · rev189 */
  --ft-status-danger: #B40C0E;                      /* H5  · rev182 = the brand destructive red */
  --ft-source-accent: initial;                      /* H6  · retired by rev182 (the generic teal) */
  --ft-source-accent-strong: initial;               /* H7  · retired by rev183 */
  --ft-rail-w: var(--ft-rail-width);                /* H8  · one rail width; the alias follows it */
  --ft-rail-width: 256px;                           /* H8  · superseded three lines below by the owner's 236 */
  --ft-nav-item-height: 36px;                       /* H9  · owner ORDER 2026-09-17 — superseded below */
  --ft-height-nav-item: 36px;                       /* H10 · owner ORDER 2026-09-17 — superseded below */
  --ft-nav-item-inset: 12px;                        /* H16 · owner ORDER 2026-09-17 — superseded below */
  --ft-table-row-h: var(--ft-table-row-h-index, 40px); /* owner D-W5-0034 (c), 11 Oct: HubSpot's index default (rev365/rev359 C2), replacing H12's 48 */
  --ft-chart-series-6: #B8862F;                     /* rev207 · confirmed ORDERED (brass); already landed here at -p, kept in his block's order */
}
/* ══ rev267c · OWNER RULINGS 2026-09-25: the main left nav takes HubSpot's measured geometry. Reversible in one step:
   the ORDER 2026-09-17 set is kept below as a comment. Colours, hover and current state are unchanged. Append-only.
   `html:root` (not `:root`) so a brand delta that loads later (delta-187 sets the label to 16) cannot re-open H11. */
html:root {
  --ft-rail-width: 236px;                /* owner 2026-09-25 · HubSpot measured (was 256) */
  /* ═══ V-10 / rev312 NT-5 — THE NARROWED RAIL. Contract 63 §3 has four states and only ONE width was
     ever measured, so SettingsShell.vue said in a comment that the second number "IS STILL MISSING AND IS
     NOT INVENTED HERE" and kept 236 at 1024. rev312 supplies it: 204 = 236 − 32, chosen so the longest
     rail label wraps to two lines and never truncates.
     ⚙️ `--ft-rail-now` is the width a rail READS. The media rule lives here, once, rather than in each of
     the pages that has a rail — the designer's own sheet does it the same way.
     ⚠️ `html:root`, like every line in this block: a brand delta that loads later would otherwise re-open
     it (the reason H11 needed the same treatment). */
  --ft-rail-width-narrow: 204px;         /* rev312 NT-5 */
  --ft-rail-now: var(--ft-rail-width, 236px);
  --ft-rail-w: var(--ft-rail-width);     /* follows */
  --ft-nav-item-height: 52px;            /* owner 2026-09-25 (was 36) */
  --ft-height-nav-item: 52px;            /* owner 2026-09-25 (the source the sheet line reads) */
  --ft-nav-item-inset: 0px;              /* owner 2026-09-25 · the row spans the rail (was 12) */
  --ft-nav-icon-size: 16px;              /* owner 2026-09-25 */
  --ft-nav-icon-inset: 22px;             /* owner 2026-09-25 · icon at inline-start 22 */
  --ft-nav-icon-gap: 8px;                /* owner 2026-09-25 · icon to label */
  --ft-nav-item-type: 14px;              /* H11 · owner ruling 2026-09-25 = the ORDER value (ends H11) */
  --ft-nav-item-radius: 0px;             /* owner 2026-09-25 · a row that spans the rail has square ends */
}
/* THE ORDER 2026-09-17 SET (documented; to bring it back, paste this block after the one above):
:root { --ft-rail-width: 256px; --ft-nav-item-height: 36px; --ft-height-nav-item: 36px; --ft-nav-item-inset: 12px; --ft-nav-item-type: 16px; --ft-nav-item-radius: 9px; --ft-nav-icon-size: 18px; --ft-nav-icon-inset: 11px; --ft-nav-icon-gap: 11px; }
*/
/* ═══ ORDERED LINES · rev264 / rev265, TWO NAMES EARLY, and the reason. The ported row menu (rev267c's engine) paints
   its box with `--ft-shell-menu-radius` and `--ft-shell-menu-shadow-soft`; undefined, the menu renders square and
   flat. Their rev PRs (rev264 · rev265) land the rest of the --ft-shell-* set; these two arrive with the menu that
   reads them, verbatim from his sheet, and they leave the PENDING register in this commit. */
:root {
  --ft-shell-menu-radius: 6px;                                   /* HubSpot 6px (ours was 12) */
  --ft-shell-menu-shadow-soft: 0 24px 48px rgba(0, 0, 0, 0.08);  /* HubSpot "0 24px 48px at 0.08"; the base colour was not given, black assumed */
}

/* ═══ S013 · EVERY MENU = HubSpot (owner ruling 2026-09-25, afternoon) · VERSION 2026-09-25-r ════════════════════════════
   The desk measured HubSpot (portal 149391748, 1920) and our live site side by side (S013 §1) and the owner ruled: all three
   menus take HubSpot's geometry, EN and mirrored in AR. The designer adds these roles to the THEME in a small pack after
   rev271; UNTIL IT LANDS every line below is DESK-MEASURED 2026-09-25, AWAITING THEME, in a naming of ours
   (--ft-nav-* the main rail · --ft-fly-* the hub flyout · --ft-setnav-* the Settings rail). When his pack lands his lines
   replace these verbatim. Reversible like ORDER-2026-09-17; the register (ft-token-delta-approved.json `measured`) carries
   one line per name. ONE OWNER DEPARTURE, dated: the main-rail label is 15px/500 ("a little bigger"); 14 stays in
   --ft-nav-item-type as the fallback. Three values are NOT the desk's and say so (the pill's insets, the separator's
   upper gap): my read of HubSpot, to be replaced by his line. Append-only. */
:root {
  /* ① the main rail — rows span the rail from x 0 (--ft-nav-item-inset 0 above); the icon at 22, the label at 46 = 22 + 16 + 8 */
  --ft-nav-icon: var(--ft-nav-icon-size);            /* the row's icon box reads the owner's 16 (was --ft-icon-3, also 16) */
  --ft-nav-hub-label-type: 15px;                     /* OWNER 2026-09-25 ② · the one departure; fallback 14 = --ft-nav-item-type */
  --ft-nav-hub-label-weight: 500;
  --ft-nav-pill-radius: var(--ft-radius-pill);       /* desk-measured: hover and current are a PILL inset inside the row */
  --ft-nav-pill-inset-block: 8px;                    /* NOT desk-measured — my read of the pill (52 − 2×8 = 36 tall); awaiting THEME */
  --ft-nav-pill-inset-inline: 8px;                   /* idem */
  --ft-nav-divider-gap: 12px;                        /* desk-measured: after Bookmarks the next row starts 25 below = 12 + 1 + 12 */
  /* ② the hub flyout (opened from CRM, Marketing, …) */
  --ft-fly-panel-width: 212px;
  --ft-fly-pad-block: 12px;
  --ft-fly-pad-inline: 8px;
  --ft-fly-radius: 16px;
  --ft-fly-row-height: 40px;
  --ft-fly-row-gap: 4px;                             /* pitch 44 = 40 + 4 · ONE pitch between every pair of rows (owner shot 1) */
  --ft-fly-text-inset: 8px;                          /* text 16 from the panel edge = 8 (panel padding) + 8 */
  --ft-fly-type: 14px;
  --ft-fly-row-radius: var(--ft-shell-menu-radius);  /* a flyout row is a menu row: HubSpot's 6 (rev264 line) */
  --ft-fly-sep-gap: 8px;                             /* the group separator: 13 above the next row = 1 (rule) + 4 (gap) + 8; the
                                                        same 8 above it is NOT desk-measured */
  /* ③ the Settings rail — the designer's rowStyle() rows (THEME specimen #spec-rail-row, ft-settings-rail.js rev270),
        with the desk's heights and insets beside his lines */
  /* rev329 (desk, 4 Oct): the all-controls-32px standard reaches the shell. The designer's rail rows were
     38px — HubSpot's pitch — and are 32px now. ⚠️ THIS TOKEN, never `--ft-nav-item-height`: that one is the
     APP nav's and is 52px by the owner's ruling of 2026-09-25. Two names, so one rail cannot move the other. */
  /* THEME 2 · part 73 (D-W2d, 30 Oct): his FINAL row, rev612-FINAL ft-settings-rail rowStyle() — rows are 38 again
     (rev404s N1/N2, the desk's reference DOM, 14 Oct), which supersedes rev329's 32 for this rail. Each value is HIS,
     named here so FtNav declares no literal: his `--ft-rail-*` roles (the pack sheet's) behind the rail's own names. */
  --ft-setnav-row-height: var(--ft-rail-row-h, 38px);
  --ft-setnav-line-height: 24px;                          /* rev657 G2-59 (D-W2d-0019): a rail row is set on a 24px line */
  --ft-setnav-edge: 4px solid transparent;                /* rev657 rev649: the 4px start edge; the current bar is the sheet's ::before */
  --ft-setnav-current: var(--ft-current);                 /* rev657 rowStyle(): the current row's ground — the pack sheet's --ft-current */
  --ft-nav-current: var(--ft-current);                    /* D-W2d-0020 part 57b · the hub rail's current row (rev658-final): --ft-selected, #E6EEFA light · #1B3354 dark */
  --ft-weight-regular: 400;                               /* rev657 rowStyle(): the row's weight (N1: never bold), as the pack sheet declares it */
  --ft-weight-bold: 700;                                  /* rev657 deco(): the Beta chip's weight, as the pack sheet declares it */
  --ft-setnav-row-pad-block: 4px;
  --ft-setnav-ink: var(--ft-rail-ink);
  --ft-setnav-active-ground: var(--ft-rail-active-ground);
  --ft-setnav-tag-pad: 2px 7px;                      /* his Beta chip on a rail row (deco(): text-0, 700, the pill) */
  --ft-setnav-text-inset: 12px;                      /* ruling 3: our group icons sit at the text inset, never flush */
  --ft-setnav-heading-type: 16px;
  --ft-setnav-heading-weight: 600;
  --ft-setnav-heading-height: 28px;
  --ft-setnav-group-gap: 32px;                       /* between a group's last item and the next heading */
}

/* ═══ S014 O-1 (owner 2026-09-25) · THE --ft-shell-* SET, VERBATIM FROM THE DESIGNER'S SHEET (ft-tokens-fasttera.css,
   cut rev277). The rev277 app-shell.js reads 29 of these names (settings-pack-tokens-resolve's ghost test named every
   one); undefined they would paint their fallbacks silently. THE THEME IS HIS: the three blocks below are copied line
   for line from his sheet (rev264 · rev265 · rev271b), comments included, and nothing of ours is changed by them.
   Two names (--ft-shell-menu-radius · --ft-shell-menu-shadow-soft) were served early above (the ported row menu) at the
   same values; his rev265 block restates them. The --ft-fly-* lines of S013 stay for now: his --ft-shell-flyout-text-inset
   (16, "from the list start") and our --ft-fly-text-inset (8, inside the 8 panel padding) are two readings of one 16, to
   be reconciled when the flyout swaps to his names (D009 §B). Append-only. */
/* ══ rev264 · app-shell — the global top bar, left rail, account menu and hub flyouts, moved out of app-shell.js.
   Values are app-shell's own literals, moved, not changed: the light theme renders identically (popup-diff-rev264.html).
   ⚖️ THE BAR IS THEME-INVARIANT. It is navy in both themes, so every on-bar value is a literal here and does not follow
   dark. The menu / flyout / rail values point at existing THEME roles where one exists and so follow the dark theme.
   Where app-shell read var(--role, #fallback) and the role has no light value on every shell page, the fallback moves
   into a --ft-shell-* pointer below. No existing token is defined or changed here. */
:root {
  --ft-shell-bar: #0A2344;                      /* bar + logo tile */
  --ft-shell-on-bar: #FFFFFF;                   /* type and glyphs on the bar */
  --ft-shell-on-bar-muted: rgba(255, 255, 255, 0.82); /* utility glyphs, trial pill */
  --ft-shell-on-bar-caret: rgba(255, 255, 255, 0.8);  /* account caret */
  --ft-shell-on-bar-faint: rgba(255, 255, 255, 0.5);  /* search glyph */
  --ft-shell-search-ground: rgba(255, 255, 255, 0.1);
  --ft-shell-bar-edge: rgba(255, 255, 255, 0.2);      /* Create button ring */
  --ft-shell-bar-rule: rgba(255, 255, 255, 0.16);     /* bar divider */
  --ft-shell-badge: #1F5FB0;                    /* notification badge, avatar, unread dot */
  --ft-shell-menu-ground: var(--ft-ground-page);        /* was #fff */
  --ft-shell-menu-edge: var(--ft-edge-field);           /* was #E2E6ED */
  --ft-shell-menu-rule: var(--ft-edge-soft);            /* was #EEF1F5 */
  --ft-shell-menu-ink: var(--ft-ink-strong);            /* was #0E1A2B */
  --ft-shell-ink-body: var(--ft-ink);                   /* was #41506A */
  --ft-shell-ink-meta: var(--ft-ink-meta, #5A6473);     /* was #5A6473; --ft-ink-meta is dark-only in these sheets, so the light value is its fallback */
  --ft-shell-ink-faint: var(--ft-ink-faint);            /* was #8A94A3 */
  --ft-shell-ink-meta-dim: #5F6873;                     /* no role: menu headings, e-mail line, external glyph */
  --ft-shell-brass-text: #8C6624;                       /* no role: brass used as type */
  --ft-shell-rail-expanded: #EEF3FA;                    /* no role: an expanded rail group */
  /* type: app-shell wrote bare numbers (React appends px). 12/13/14/16 are the THEME steps --ft-text-2/3/4/5; these three had no step. */
  --ft-shell-type-9-5: 9.5px;    /* notification badge */
  --ft-shell-type-12-5: 12.5px;  /* trial pill, account-menu rows */
  --ft-shell-type-13-5: 13.5px;  /* search, account-menu items */
  --ft-shell-menu-shadow: 0 18px 44px rgba(10, 35, 68, 0.24);
  --ft-shell-flyout-shadow: 0 10px 28px rgba(14, 26, 43, 0.14);
  /* Roles app-shell read as var(--role, #fallback) that no light block defines on every shell page: the fallback moves
     here unchanged, so the page that does define the role (e.g. through ft-tokens-brand-delta-207.css) still wins. */
  --ft-shell-edge-hairline: var(--ft-edge-hairline, #EEF1F5);
  --ft-shell-edge-border: var(--ft-edge-border, #E2E6ED);
  --ft-shell-tint-navy: var(--ft-ground-tint-navy, #EAF0F8);
  --ft-shell-chrome-muted: var(--ft-ink-chrome-muted, #52627C);
  --ft-shell-brand-primary: var(--ft-brand-primary, #0A2344);
}

/* ══ rev265 · top bar + account menu → HubSpot today (FIXLIST-TopBar-and-Account-Menu, measured 2026-09-24 at 1920).
   Append-only; no existing token changed. */
:root {
  --ft-shell-search-lead: 60px;       /* bar inline padding 16 + logo 30 + gap 14: what sits before the search group */
  --ft-shell-search-min: 120px;       /* the search never shrinks below this; the cluster's labels drop first */
  --ft-shell-menu-radius: 6px;        /* HubSpot 6px (ours was 12) */
  /* ft-avatar initials, moved from its SIZE table (px → token, values unchanged) */
  --ft-avatar-type-20: 8px; --ft-avatar-type-24: 9px; --ft-avatar-type-32: 12px; --ft-avatar-type-48: 17px;
  --ft-avatar-type-28: 10.5px;   /* D-CB-46 (desk 8 Nov): the ruled 28 avatar (Availability), on his 0.375 type scale */
  --ft-shell-menu-shadow-soft: 0 24px 48px rgba(0, 0, 0, 0.08); /* HubSpot "0 24px 48px at 0.08"; the base colour was not given, black assumed */
}

/* ══ rev271b · OWNER RULING 2026-09-25 (desk 14:00, corrected): the FLYOUT sub-menu (CRM, Marketing and the rest) takes
   HubSpot's measured rhythm (149391748 at 1920) and has NO inner scroll. Geometry only; colours stay ours. Append-only:
   the rev162 values (192 wide, 4 padding, radius 12, rows 40 at pitch 40, a 12 gap before a group) are kept below. */
html:root {
  --ft-shell-flyout-inline-size: 212px;  /* panel width (was 192) */
  --ft-shell-flyout-pad-block: 12px;     /* panel padding, block (was 4) */
  --ft-shell-flyout-pad-inline: 8px;     /* panel padding, inline (was 4) */
  --ft-shell-flyout-radius: 16px;        /* (was --ft-radius-4 = 12) */
  --ft-shell-flyout-row-block: 40px;     /* row height (unchanged) */
  --ft-shell-flyout-row-pitch: 44px;     /* one pitch, first item included (was 40) */
  --ft-shell-flyout-row-gap: calc(var(--ft-shell-flyout-row-pitch) - var(--ft-shell-flyout-row-block));
  --ft-shell-flyout-text-inset: 16px;    /* text from the list start (unchanged) */
  --ft-shell-flyout-sep: 1px;            /* the separator between groups (was a 12 gap, no rule) */
  --ft-shell-flyout-sep-after: 13px;     /* separator to the next row */
  --ft-shell-flyout-edge: 8px;           /* the panel keeps this far from the viewport edge */
}

/* ═══ S014 O-1 (2026-09-25) · FIVE rev181 ROLES the rev277 tier reads and its own sheet no longer declares. disabled-reason.js
   (--ft-ink-annotation, no fallback), ft-container.js (--ft-ink-crumb · --ft-shadow-modal · --ft-accent-premium) and
   ft-layer.js (--ft-shadow-modal · --ft-scrim-modal) read them; his rev267c chain (the parity fixture) declares them at
   lines 71 · 72 · 135 · 144 · 145 and, dark, 229 · 235 — and rev277's ft-tokens-fasttera.css does not (raised with the
   desk in D009). Served here as HIS rev267c lines, verbatim, light then dark; they leave the parity PENDING register. */
:root {
  --ft-ink-annotation:    var(--ft-ink-note, #8A6420);
  --ft-accent-premium:    var(--ft-ink-accent-brass, #B8862F);
  --ft-ink-crumb:          #333333;
  --ft-scrim-modal:        rgba(20, 20, 20, 0.46);
  --ft-shadow-modal:       0 16px 32px rgba(20, 20, 20, 0.08);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ink-crumb:                #C3CCDA;
  --ft-scrim-modal:              rgba(0, 0, 0, 0.62);
}

/* ═══ W5 · desk shell item (2026-10-06) · THE PAGE TITLE. rev304's two tokens, HIS line verbatim (ft-tokens-fasttera.css
   792, the page-title half): HubSpot's settings page title measured 24 / 400 on Audit Log and Notifications. The Settings
   shell's <h1> reads them (it drew 26 / 700). The same line also declares --ft-chart-series-7…11; those are not this
   item and are not taken here. */
:root { --ft-page-title-size: var(--ft-text-7, 1.5rem); --ft-page-title-weight: 400; }

/* ═══ W5 · desk item 4 (2026-10-06) · THE CHIP'S THREE PREMIUM ROLES. The tier's ft-chip.js lock() reads
   --ft-ground-premium · --ft-edge-premium · --ft-ink-brass-text with NO fallback, and our layer declared none of them,
   so a lock chip painted no fill, no brass type and no border. Served as HIS lines, verbatim — rev331c's
   ft-tokens-fasttera.css lines 71 · 72 · 138 and, dark, 234 (the S014 O-1 precedent); they leave the parity PENDING
   register. The two grounds read --ft-ground-note / --ft-edge-note, which are declared in both modes (rev309 block). */
:root {
  --ft-ground-premium:    var(--ft-ground-note, #FBF6EA);
  --ft-edge-premium:      var(--ft-edge-note, #E8D9A8);
  --ft-ink-brass-text:     #8C6624;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ink-brass-text:           #D9AE5F;
}

/* ═══ S014 O-1 (2026-09-25) · --ft-font-ui, the script-aware type role of his ft-type-script.css (rev207 line), verbatim:
   rev277's ft-table.js reads it (a name our chain lacked — the ghost test); it resolves through the two TYPE SLOTS
   above (--ft-font-latin · --ft-font-arabic). */
:root {
  --ft-font-ui: var(--ft-font-latin, InterVariable, Inter, system-ui, -apple-system, sans-serif);
}
[lang|="ar"],
[dir="rtl"] {
  --ft-font-ui: var(--ft-font-arabic, "IBM Plex Sans Arabic", "Noto Sans Arabic", system-ui, sans-serif);
}

/* ═══ S016 (2026-09-26) · rev279 (D009 b) · VERSION 2026-09-26-s · HIS two lines, verbatim: the flyout text inset is MEASURED
   FROM THE PANEL'S OUTER EDGE (HubSpot's 16 = 1 border + 8 panel + 7 item); the rev280 app-shell.js reads the derived
   --ft-shell-flyout-item-pad-inline (the ghost test caught the name our layer lacked). The --ft-shell-flyout-text-inset
   line above keeps its value; this block adds the border and the derivation as his ft-tokens-fasttera.css rev279 declares them. */
:root {
  --ft-shell-flyout-border: 1px;
  --ft-shell-flyout-item-pad-inline: calc(var(--ft-shell-flyout-text-inset) - var(--ft-shell-flyout-pad-inline) - var(--ft-shell-flyout-border));
}

/* ═══ S019b (2026-09-26) · rev287 · HIS two lines, verbatim (ft-tokens-fasttera.css 515-516): the User activity bars on
   the rebuilt User Detail — a rust bar on a grey track. Both themes, because the rust that reads on white is not the
   rust that reads on navy: his dark pair is a lighter bar on a dark track, measured by his own census. Two ROLES, not
   two colours: the bar and the track are named for the job, so a chart elsewhere can ask for the same pair. */
:root { --ft-chart-bar-activity: #9F2800; --ft-chart-bar-track: #EBEBEB; }
:root[data-ft-theme="dark"], :root[data-theme="dark"] { --ft-chart-bar-activity: #E27A52; --ft-chart-bar-track: #2A3A52; }

/* ═══ S021 (2026-09-27) · rev289b · THE ONE LIST'S TOKENS + THE ONE SCROLLBAR. His block from
   ft-tokens-fasttera.css 518-541, values verbatim, with his own note kept: the scrollbar is for EVERY scroll area in
   the product (the Settings rail, the main navigation, every list and panel), 8px, thumb --ft-edge-strong, radius 8,
   2px inset, transparent track, on the inline end — the left in Arabic, because it follows dir. "Never set
   scrollbar-width/-color inline: in Chromium that switches the pseudo-elements off and brings the OS scrollbar back
   (the census fails it)." That is why `.ftset-rail__list`'s own `scrollbar-width: thin` goes in this commit.
   ⚖️ TWO ADAPTATIONS, both named. (1) His dark selector is `:root[data-theme="dark"]`; ours answers to BOTH attribute
   names, so each dark rule lists `[data-ft-theme="dark"]` beside it, as every appended block here does — porting his
   selector verbatim would leave a page in `data-ft-theme` mode on the light list.
   (2) 🔴 --ft-ink-muted IS DELIBERATELY NOT TAKEN, and this is the one place in the block where his bytes are not
   ours. rev289b restates it as the literal #5A6473; his OWN `inbox/tokens-e.css` — the head of the chain the T-1 parity
   spec resolves — still derives it from `--ft-source-ink-3` (#475569). Two of his artefacts, two answers, which is the
   case that spec exists to surface, and it reported both this role and `--ft-chart-label-ink`, which follows it.
   Our derived value therefore stays until the desk rules, because the alternative is to re-cut the parity chain from
   rev265 to rev289b and re-resolve every role in it — a round of its own, not a line in a scrollbar commit.
   The list rows that wanted it read --ft-ink-muted either way, so they follow whichever value wins. Asked in D016. */
:root {
  --ft-ground-raised: #FFFFFF; --ft-ground-hover: #F3F5F8; --ft-accent-soft: #EAF0F8;
  /* D-W5-0013 D12 (page 1 LIVE, 9 Oct) · an open list must read as a LAYER. The pack's lone 24px-offset 8% shadow left the
   edge so faint that a field cut in half beneath it read as showing THROUGH the list (General › Language). A contact ring and
   a near drop are added IN FRONT of the pack's far drop, which stays. Theme decision, noted to the designer via the desk. */
--ft-list-edge: #E2E6ED; --ft-list-search: #FFFFFF; --ft-list-ring: rgba(31,95,176,.14); --ft-shadow-list: 0 0 0 1px rgba(14,26,43,.06), 0 4px 12px rgba(14,26,43,.18), 0 24px 48px rgba(0,0,0,.08);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  /* --ft-ink-muted is left to our own dark chain here too, for the reason above */
  --ft-ground-raised: #0E1A2B; --ft-ground-hover: #1B2B41; --ft-accent-soft: #16253A;
  --ft-list-edge: #24384F; --ft-list-search: var(--ft-ground-sunken, #1B2C42); --ft-list-ring: rgba(122,169,230,.22); --ft-shadow-list: 0 0 0 1px rgba(0,0,0,.30), 0 4px 12px rgba(0,0,0,.45), 0 24px 48px rgba(0,0,0,.40);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ground-raised: #0E1A2B; --ft-ground-hover: #1B2B41; --ft-accent-soft: #16253A;
  --ft-list-edge: #24384F; --ft-list-search: var(--ft-ground-sunken, #1B2C42); --ft-list-ring: rgba(122,169,230,.22); --ft-shadow-list: 0 0 0 1px rgba(0,0,0,.30), 0 4px 12px rgba(0,0,0,.45), 0 24px 48px rgba(0,0,0,.40);
}
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--ft-edge-strong, #D7DCE4); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover { background: var(--ft-ink-muted, #5A6473); }
::-webkit-scrollbar-track { background: transparent; margin: 2px; }
::-webkit-scrollbar-corner { background: transparent; }
::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
@supports not selector(::-webkit-scrollbar) { * { scrollbar-width: thin; scrollbar-color: var(--ft-edge-strong, #D7DCE4) transparent; } }

/* 🔴 AND THE DESK HALF, which is the owner's actual complaint ("the scroll color again has brown"). Inside the Frappe
   Desk the block above cannot paint: `desk.bundle.css` sets `* { scrollbar-width: thin; scrollbar-color:
   var(--scrollbar-thumb-color) var(--scrollbar-track-color); }`, and a set `scrollbar-color` switches every
   ::-webkit-scrollbar rule off in Chromium. The desk read it live on the Settings rail: thumb rgb(199,199,199) on
   rgb(237,237,237) — core's own defaults, which is the brown-grey. So core's rule stays, and we give it OUR values.
   `html:root` (0,1,1) rather than `:root` (0,1,0): core defines the same two names, and specificity must settle it
   instead of the order the four brand deltas are served in. */
html:root {
  --scrollbar-thumb-color: var(--ft-edge-strong, #D7DCE4);
  --scrollbar-track-color: transparent;
}

/* ═══ rev296 F3 (desk S031, 2026-09-27) · THE DIALOG SURFACE ═══════════════════════════════════════════════════════
   The designer's own words for why: "In dark, a dialog painted with --ft-ground-page (or the list's raised ground) sat
   on the page colour with a faint scrim and no edge, so it read as floating text."

   🔴 IT IS OUR OWN BUG TOO, and that is how this arrived. Asked to build B6's modal on "the raised-surface token", I
   checked before building: in dark `--ft-ground-raised` resolves to #0E1A2B and so does `--ft-source-paper`, so the
   "raised" surface IS the page. The dark palette already holds a raised value (`--ft-source-surface` #142235) that the
   semantic token does not point at. The desk ordered that re-point to the designer for rev297 — the theme is his — and
   released these dialog tokens now so the modal has a real ground and a real edge in the meantime.

   ⚖️ FOUR TOKENS, and the border is the one to be careful about: NOT `--ft-edge-soft`, which has no dark override and
   would paint a near-white #EEF1F5 hairline on a #16263B surface.
   ⚠️ Our dark form, not his: this file keys dark on BOTH `[data-ft-theme]` and `[data-theme]`, and its system-default
   block excludes both, so a value set here reaches every way the theme can be chosen. His file writes `[data-theme]`
   only. Same values, our selectors — the parity chain compares values, not the selector spelling. */
:root {
  --ft-ground-dialog: #FFFFFF;
  --ft-edge-dialog: #E2E6ED;
  --ft-shadow-dialog: 0 24px 60px rgba(10, 35, 68, .28);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ground-dialog: #16263B;
  --ft-edge-dialog: #33506F;
  --ft-shadow-dialog: 0 24px 60px rgba(0, 0, 0, .55);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ground-dialog: #16263B;
  --ft-edge-dialog: #33506F;
  --ft-shadow-dialog: 0 24px 60px rgba(0, 0, 0, .55);
}

/* ═══ rev298 (S038) · THE PRIMARY ACTION, ONE TOKEN SET ═══════════════════════════════════════════════════════════
   The designer's values, verbatim from `ft-tokens-fasttera.css`; our selector form, for the reason the rev296 dialog
   block states — this file keys dark on BOTH `[data-ft-theme]` and `[data-theme]`, and its system-default block
   excludes both, so a value set here reaches every way the theme can be chosen. His file writes `[data-theme]` only.
   The parity chain compares VALUES, not the selector spelling.

   ⚖️ HIS MEASUREMENT, KEPT IN HIS WORDS: light keeps the brand navy #0A2344 (white on it 15.7:1). Dark could not keep
   navy — on the dark page it measured 1.02–1.53:1 and "the button's shape vanished" — so dark is azure #3572D1, which
   clears 3:1 on every dark ground (page 3.73 · raised 3.42 · dialog 3.26) with white text at 4.69:1. Hover and pressed
   step DARKER in dark, so the text only gains contrast. Disabled is exempt from the contrast rule (WCAG 1.4.3 / 1.4.11)
   and keeps an edge instead.

   ⚠️ CHECKBOXES, SWITCHES, TABS AND SELECTED ROWS KEEP THE NAVY (S038's table, explicitly): they show a STATE, not an
   action. So this token belongs to buttons and to nothing else, and `FtCheck`'s navy box is correct as it stands. */
:root {
  --ft-action-primary: #0A2344; --ft-action-primary-hover: #0F2E5A; --ft-action-primary-pressed: #071A33;
  --ft-action-primary-ink: #FFFFFF; --ft-action-primary-edge: transparent;
  --ft-action-primary-disabled: #EEF0F3; --ft-action-primary-disabled-ink: #6B7789; --ft-action-primary-disabled-edge: #D7DCE4;
  --ft-action-focus-ring: #1F5FB0;

  /* 🔴 S107 4 (owner) · CANCEL IS A BRAND-RED BUTTON, AND ITS OWN FAMILY SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
     "add a button, red colour (our brand colour), to Cancel on each pop-up."

     ⚖️ IT IS NOT `danger`, and the distinction is the ruling: danger means DELETE, and Tier-3 must be able to
     move the two apart. `danger` reads --ft-brand-secondary for rest and BORROWS
     --ft-ground-destructive-hover/-active for its states; the desk was explicit that Cancel must not. Sharing
     them would let a Tier-3 edit to the delete button silently repaint every Cancel on the platform.
     ⚠️ So nothing here points at a destructive token. The family resolves to the brand-secondary steps, which
     is where #B40C0E comes from today — "via --ft-ink-action-destructive" in the ruling named that origin, it
     was not a request to alias it. */
  --ft-action-cancel: var(--ft-brand-secondary);
  /* owner D-W5-0034 (a), 11 Oct: the designer's darker Cancel (#97090B / #7A0708), replacing S107 §4 */
  --ft-action-cancel-hover: var(--ft-ground-destructive-hover);
  --ft-action-cancel-pressed: var(--ft-ground-destructive-active);
  --ft-action-cancel-ink: #FFFFFF;
  --ft-action-cancel-edge: transparent;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-action-primary: #3572D1; --ft-action-primary-hover: #2F6AC4; --ft-action-primary-pressed: #285DB0;
  --ft-action-primary-ink: #FFFFFF; --ft-action-primary-edge: transparent;
  --ft-action-primary-disabled: #1B2537; --ft-action-primary-disabled-ink: #8A94A3; --ft-action-primary-disabled-edge: #3A5270;
  --ft-action-focus-ring: #7AA9E6;

  /* ⬜ S107 4 — DARK IS PAUSED, so these are DEFINED and carry the light reading. A token whose dark half
     repeats its light value is the designer's to fill, which is this codebase's own signal for "not yet
     measured". ⚠️ Leaving them UNDEFINED would be worse than repeating: in dark the cascade would hand the
     button whatever it found, and a Cancel that loses its shape reads as a bug rather than as unfinished. */
  --ft-action-cancel: var(--ft-brand-secondary);
  /* owner D-W5-0034 (a), 11 Oct: the designer's darker Cancel (#97090B / #7A0708), replacing S107 §4 */
  --ft-action-cancel-hover: var(--ft-ground-destructive-hover);
  --ft-action-cancel-pressed: var(--ft-ground-destructive-active);
  --ft-action-cancel-ink: #FFFFFF;
  --ft-action-cancel-edge: transparent;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-action-primary: #3572D1; --ft-action-primary-hover: #2F6AC4; --ft-action-primary-pressed: #285DB0;
  --ft-action-primary-ink: #FFFFFF; --ft-action-primary-edge: transparent;
  --ft-action-primary-disabled: #1B2537; --ft-action-primary-disabled-ink: #8A94A3; --ft-action-primary-disabled-edge: #3A5270;
  --ft-action-focus-ring: #7AA9E6;

  /* ⬜ S107 4 — DARK IS PAUSED, so these are DEFINED and carry the light reading. A token whose dark half
     repeats its light value is the designer's to fill, which is this codebase's own signal for "not yet
     measured". ⚠️ Leaving them UNDEFINED would be worse than repeating: in dark the cascade would hand the
     button whatever it found, and a Cancel that loses its shape reads as a bug rather than as unfinished. */
  --ft-action-cancel: var(--ft-brand-secondary);
  /* owner D-W5-0034 (a), 11 Oct: the designer's darker Cancel (#97090B / #7A0708), replacing S107 §4 */
  --ft-action-cancel-hover: var(--ft-ground-destructive-hover);
  --ft-action-cancel-pressed: var(--ft-ground-destructive-active);
  --ft-action-cancel-ink: #FFFFFF;
  --ft-action-cancel-edge: transparent;
}

/* ✅ G-1b's PIN IS RETIRED (rev302 intake, 2026-09-28) — and the block it replaced is named rather than silently gone.
   I pinned `--ft-ink-strong: var(--ft-source-ink)` in dark on 2026-09-28 because the rev267b ordered line held it at
   #141414 in both modes. rev302 restores the indirection at source as `var(--ft-ink-body-strong)` (#F4F6F9), with a
   selector list that now covers `data-ft-theme` too — the designer adopted our form. Two owners for one token is the
   thing this project keeps paying for, so mine goes and his stays. Removed symbols: that one block, nothing else. */

/* ═══ rev302 (S047) · DARK VALUES FOR EVERY COLOUR TOKEN THAT WAS PINNED IN LIGHT ONLY ═════════════════════
   APPENDED VERBATIM, which is new for this file and worth saying why: every earlier token intake had to be
   re-written in our selector form, because his sheet keyed dark on `[data-theme]` only while ours keys it on both
   `[data-theme]` and `[data-ft-theme]`. rev302's own selector list is
       :root[data-theme="dark"], :root[data-ft-theme="dark"], html:root[data-theme="dark"], html[data-theme="dark"]
   — he adopted our form, so there is nothing left to translate and the bytes below are his.

   ⚖️ WHAT IT ANSWERS: D021 §2 reported 41 tokens pinned to a literal with no dark override, 11 of them ink. His own
   census went further — 266 colour tokens, 108 failing (81 unchanged in dark, 27 under their floor) → 0 failing, with
   37 declared theme-neutral and 36 exempt, each with its reason in `ft-tokens-theme-neutral.json`.

   ⚠️ ORDER IS LOAD-BEARING HERE, and the desk flagged it: his file keeps an earlier dark `--ft-ink-faint: #6B7789`
   and the later value (#8A94A3, 5.70:1) wins by document order. So this block is APPENDED at the end of the sheet,
   after every ordered-lines block, and must stay last — the same lesson as `html:root` beating a plain `:root`.

   ⬜ Literal values, not only `var()` chains, are his choice and the right one for us: this sheet is flattened from
   several of his files, and a chain whose head lives in a file we do not serve would resolve to nothing. */
/* ═══ rev302 · DARK VALUES FOR EVERY COLOUR TOKEN THAT WAS PINNED IN LIGHT ONLY (desk rev302 §2).
   The token census (census-rev302-tokens.html) found 266 colour tokens; 81 resolved to the SAME value in dark and 27 fell under
   their floor on the dark grounds (page #0E1A2B · raised #142235 · dialog #16263B). Each one now either gets a dark value here
   (inks ≥ 4.5:1 on all three grounds, marks and control boundaries ≥ 3:1, grounds and hairlines mapped to the dark surface set),
   or is declared theme-neutral with its reason in ft-tokens-theme-neutral.json.
   Literal values are written out (not only var() chains) so a derived, flattened sheet carries them too.
   The --ft-ink-strong indirection is restored in dark: it reads --ft-ink-body-strong again, so a light pin (#141414, rev267b)
   can never win in dark. Selectors cover data-theme, data-ft-theme, html:root (Frappe sets data-theme on <html>) and the
   OS preference when no theme is set. */
:root[data-theme="dark"], :root[data-ft-theme="dark"], html:root[data-theme="dark"], html[data-theme="dark"] {
  --ft-ink-body-strong: #F4F6F9;
  --ft-ink-strong: var(--ft-ink-body-strong);
  --ft-ink-chrome-muted: #8793A5;
  --ft-ink-faint: #8A94A3;
  --ft-ink-recede: #8C939D;
  --ft-shell-ink-meta-dim: #8C929B;
  --ft-shell-brass-text: #AA8E5D;
  --ft-source-success-ink: #6B9D7E;
  --ft-source-warning-ink: #BB8768;
  --ft-source-danger-ink: #C67F7F;
  --ft-source-info-ink: #7C8FD0;
  --ft-shell-ink-faint: #8993A1;
  --ft-tab-close-ink: #8993A1;
  --ft-bubble-ink: #8D9299;
  --ft-source-ink-3: #8B93A0;
  --ft-source-ink-4: #8693A5;
  --ft-source-ink-5: #94A3B8;
  --ft-cal-today-ink: #6594DC;
  --ft-nav-ink-current: #6594DC;
  --ft-rail-item-ink-current: #6594DC;
  --ft-tab-current-rule-ink: #6594DC;
  --ft-channel-linkedin: #2576C9;
  --ft-channel-email: #3B82F6;
  --ft-channel-facebook: #1877F2;
  --ft-channel-instagram: #E1306C;
  --ft-channel-internal: #6E7583;
  --ft-channel-whatsapp: #25D366;
  --ft-chart-series-1: #3773D1;
  --ft-chart-series-3: #4076BB;
  --ft-chart-series-4: #A6643B;
  --ft-chart-series-5: #6A7686;
  --ft-chart-series-6: #B8862F;
  --ft-status-info: #3B82F6;
  --ft-status-neutral: #66758C;
  --ft-status-success: #10B981;
  --ft-status-warning: #F59E0B;
  --ft-source-azure: #4076BB;
  --ft-source-info: #3B82F6;
  --ft-source-success: #10B981;
  --ft-source-warning: #F59E0B;
  --ft-source-danger: #EF4444;
  --ft-accent-bar: #3B82F6;
  --ft-shell-badge: #4076BB;
  --ft-edge-strong: #63778F;
  --ft-edge-field: #677685;
  --ft-table-pager-current-edge: #63778F;
  --ft-source-line-field: #63778F;
  --ft-action-primary-hover: #4A83DB;
  --ft-action-primary-pressed: #3F7BD6;
  --ft-source-ink: #F4F6F9;
  --ft-source-ink-2: #D5DCE6;
  --ft-channel-tiktok: #FFFFFF;
  --ft-edge: #2A3F58;
  --ft-board-card-edge: var(--ft-edge);
  --ft-board-col-edge: var(--ft-edge);
  --ft-cal-edge: var(--ft-edge);
  --ft-dialog-edge: var(--ft-edge-dialog);
  --ft-nav-edge: var(--ft-edge);
  --ft-rail-edge: var(--ft-edge);
  --ft-report-card-edge: var(--ft-edge);
  --ft-tab-edge: var(--ft-edge);
  --ft-table-head-edge: var(--ft-edge);
  --ft-view-edge: var(--ft-edge);
  --ft-source-line: #2A3F58;
  --ft-bubble-ground-in: #1B2B41;
  --ft-count-ground: #1B2B41;
  --ft-tab-count-ground: #1B2B41;
  --ft-status-neutral-tint: #1B2B41;
  --ft-source-surface: #0E1A2B;
  --ft-source-surface-2: #142235;
  --ft-source-surface-3: #1B2B41;
  --ft-source-line-soft: #1B2B41;
  --ft-source-paper: #142235;
  --ft-ground-surface-2: #142235;
  --ft-ground-surface-3: #1B2B41;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ink-body-strong: #F4F6F9;
  --ft-ink-strong: var(--ft-ink-body-strong);
  --ft-ink-chrome-muted: #8793A5;
  --ft-ink-faint: #8A94A3;
  --ft-ink-recede: #8C939D;
  --ft-shell-ink-meta-dim: #8C929B;
  --ft-shell-brass-text: #AA8E5D;
  --ft-source-success-ink: #6B9D7E;
  --ft-source-warning-ink: #BB8768;
  --ft-source-danger-ink: #C67F7F;
  --ft-source-info-ink: #7C8FD0;
  --ft-shell-ink-faint: #8993A1;
  --ft-tab-close-ink: #8993A1;
  --ft-bubble-ink: #8D9299;
  --ft-source-ink-3: #8B93A0;
  --ft-source-ink-4: #8693A5;
  --ft-source-ink-5: #94A3B8;
  --ft-cal-today-ink: #6594DC;
  --ft-nav-ink-current: #6594DC;
  --ft-rail-item-ink-current: #6594DC;
  --ft-tab-current-rule-ink: #6594DC;
  --ft-channel-linkedin: #2576C9;
  --ft-channel-email: #3B82F6;
  --ft-channel-facebook: #1877F2;
  --ft-channel-instagram: #E1306C;
  --ft-channel-internal: #6E7583;
  --ft-channel-whatsapp: #25D366;
  --ft-chart-series-1: #3773D1;
  --ft-chart-series-3: #4076BB;
  --ft-chart-series-4: #A6643B;
  --ft-chart-series-5: #6A7686;
  --ft-chart-series-6: #B8862F;
  --ft-status-info: #3B82F6;
  --ft-status-neutral: #66758C;
  --ft-status-success: #10B981;
  --ft-status-warning: #F59E0B;
  --ft-source-azure: #4076BB;
  --ft-source-info: #3B82F6;
  --ft-source-success: #10B981;
  --ft-source-warning: #F59E0B;
  --ft-source-danger: #EF4444;
  --ft-accent-bar: #3B82F6;
  --ft-shell-badge: #4076BB;
  --ft-edge-strong: #63778F;
  --ft-edge-field: #677685;
  --ft-table-pager-current-edge: #63778F;
  --ft-source-line-field: #63778F;
  --ft-action-primary-hover: #4A83DB;
  --ft-action-primary-pressed: #3F7BD6;
  --ft-source-ink: #F4F6F9;
  --ft-source-ink-2: #D5DCE6;
  --ft-channel-tiktok: #FFFFFF;
  --ft-edge: #2A3F58;
  --ft-board-card-edge: var(--ft-edge);
  --ft-board-col-edge: var(--ft-edge);
  --ft-cal-edge: var(--ft-edge);
  --ft-dialog-edge: var(--ft-edge-dialog);
  --ft-nav-edge: var(--ft-edge);
  --ft-rail-edge: var(--ft-edge);
  --ft-report-card-edge: var(--ft-edge);
  --ft-tab-edge: var(--ft-edge);
  --ft-table-head-edge: var(--ft-edge);
  --ft-view-edge: var(--ft-edge);
  --ft-source-line: #2A3F58;
  --ft-bubble-ground-in: #1B2B41;
  --ft-count-ground: #1B2B41;
  --ft-tab-count-ground: #1B2B41;
  --ft-status-neutral-tint: #1B2B41;
  --ft-source-surface: #0E1A2B;
  --ft-source-surface-2: #142235;
  --ft-source-surface-3: #1B2B41;
  --ft-source-line-soft: #1B2B41;
  --ft-source-paper: #142235;
  --ft-ground-surface-2: #142235;
  --ft-ground-surface-3: #1B2B41;
}

/* ═══ rev307 (S057) · THE 17 SOURCE NAMES, DECLARED — APPENDED VERBATIM, AND THIS BLOCK IS NOW THE LAST ══════
   This answers D022 Q6, Q7 and Q8 at the source, and it retires three blocks of ours (see below).

   🔴 ORDER IS LOAD-BEARING, AND THE OWNERSHIP OF "LAST" MOVES HERE. rev302's block carries the note "must stay
   last", and that was true of it against every block before it. It is no longer the last block: rev307's LIGHT
   `:root` sets `--ft-ink-faint: #646F80`, and rev302's own LIGHT `:root` (line 925) sets it to #8A94A3. Whichever
   comes second wins. Putting rev307 first would leave Q7 unfixed — the 3.07:1 light caption ink the desk measured —
   while looking as though it had been taken. So rev307 goes AFTER rev302, and rev302's note is superseded rather
   than deleted: the rule it states is still the rule, and the block it names is no longer the one at the end.
   ⚠️ In DARK both cuts say #8A94A3 for that token, so the dark half is unaffected either way — which is exactly why
   this could have gone unnoticed: the mode the desk was measuring would have looked right.

   ⚖️ HIS SELECTOR LIST, UNCHANGED. rev302 adopted our two attribute names and this cut keeps them, adding
   `html[data-ft-theme="dark"]` — a superset of what we serve, so there is nothing to translate. Appended verbatim:
   these are his bytes, not our transcription of them.

   ✅ WHAT IT RETIRES ON OUR SIDE, all three written to be deleted and now deleted:
     · D-5's `.ft-link` dark rule — `--ft-status-info-ink` reads `--ft-ink-interactive`, now light #1F5FB0 / dark
       #6FA8E8, so the token resolves in both modes and the override has nothing left to do;
     · D-3's `.ftset-promo` dark rule — `--ft-ground-surface` reads `--ft-ground-sunken`, now light #F4F6F9 (the
       fallback it already had, byte-identical) / dark #1B2C42;
     · CAL-1's `.ftset-banner*` dark rules — `--ft-status-danger-tint` reads `--ft-ground-status-negative` (dark
       #3A1B1A) and `--ft-status-danger-ink` reads `--ft-ink-status-negative` (dark #F08A86).
   That is the prediction in Q6 coming true: those overrides existed only because the names they depended on were
   declared nowhere, and each one said in its own comment that it was written to be deleted. */
/* ═══ rev307 · THE 17 SOURCE NAMES, DECLARED IN BOTH MODES (developer D022, desk-measured on live 2026-09-28).
   22 tokens at the top of this file read these names through var(name, #hex). The names were declared only in the dark
   block, so any build that did not carry that block (or read it through a selector it did not parse) got the light
   fallback hex in dark too: the settings rail's active row and every inactive strip tab stayed #F4F6F9 (1.00:1, 2.86:1).
   TK-1 · each name is now declared outright. LIGHT = the fallback hex already in use (byte-identical). DARK = the dark block's
   value. Plain selector lists, no comments inside them.
   TK-2 · --ft-ink-faint in light was #8A94A3 (3.07:1 on white). It carries text (captions, the Deprecation row's dot label,
   helper lines), so it is raised to #646F80: 5.07:1 on white, 4.71:1 on the surface #F4F6F9. Still one step lighter than
   --ft-ink-recede (#5A6473), so the ladder keeps its order. Dark is unchanged (#8A94A3, rev302).
   TK-3 · --ft-ground-raised in dark follows paper (#142235), one step above the page (#0E1A2B). It read --ft-source-surface,
   which rev302 set to the page colour, so raised surfaces had no edge. */
:root {
  --ft-brand-primary:            #0A2344;
  --ft-edge-border:              #E2E6ED;
  --ft-edge-hairline:            #EEF1F5;
  --ft-ground-brand-deep:        #071A33;
  --ft-ground-brand-hover:       #0F2E5A;
  --ft-ground-status-negative:   #FBEBEA;
  --ft-ground-status-positive:   #E7F3EC;
  --ft-ground-status-warning:    #F7EFDD;
  --ft-ground-sunken:            #F4F6F9;
  --ft-ground-tint-navy:         #EAF0F8;
  --ft-ink-accent-brass:         #B8862F;
  --ft-ink-body:                 #41506A;
  --ft-ink-interactive:          #1F5FB0;
  --ft-ink-meta:                 #5A6473;
  --ft-ink-note:                 #8A6420;
  --ft-ink-status-negative:      #A3231F;
  --ft-ink-status-positive:      #19714A;
  --ft-ink-faint:                #646F80;
}
:root[data-theme="dark"], :root[data-ft-theme="dark"], html[data-theme="dark"], html[data-ft-theme="dark"] {
  --ft-brand-primary:            #1B3A66;
  --ft-edge-border:              #24384F;
  --ft-edge-hairline:            #1B2C42;
  --ft-ground-brand-deep:        #142E52;
  --ft-ground-brand-hover:       #24497C;
  --ft-ground-status-negative:   #3A1B1A;
  --ft-ground-status-positive:   #123023;
  --ft-ground-status-warning:    #33280F;
  --ft-ground-sunken:            #1B2C42;
  --ft-ground-tint-navy:         #16253A;
  --ft-ink-accent-brass:         #D9AE5F;
  --ft-ink-body:                 #C3CCDA;
  --ft-ink-interactive:          #6FA8E8;
  --ft-ink-meta:                 #93A0B4;
  --ft-ink-note:                 #D9AE5F;
  --ft-ink-status-negative:      #F08A86;
  --ft-ink-status-positive:      #4FBE8B;
  --ft-ink-faint:                #8A94A3;
  --ft-ground-raised:            var(--ft-source-paper, #142235);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-brand-primary:            #1B3A66;
  --ft-edge-border:              #24384F;
  --ft-edge-hairline:            #1B2C42;
  --ft-ground-brand-deep:        #142E52;
  --ft-ground-brand-hover:       #24497C;
  --ft-ground-status-negative:   #3A1B1A;
  --ft-ground-status-positive:   #123023;
  --ft-ground-status-warning:    #33280F;
  --ft-ground-sunken:            #1B2C42;
  --ft-ground-tint-navy:         #16253A;
  --ft-ink-accent-brass:         #D9AE5F;
  --ft-ink-body:                 #C3CCDA;
  --ft-ink-interactive:          #6FA8E8;
  --ft-ink-meta:                 #93A0B4;
  --ft-ink-note:                 #D9AE5F;
  --ft-ink-status-negative:      #F08A86;
  --ft-ink-status-positive:      #4FBE8B;
  --ft-ink-faint:                #8A94A3;
  --ft-ground-raised:            var(--ft-source-paper, #142235);
}

/* ═══ rev309 + rev309b (S060) · TOKENS FOR THE LITERALS THE CENSUS COULD NOT BIND — APPENDED VERBATIM, LAST ═════
   Two append-only blocks of his, over the rev307 file this sheet already carries: `before-rev309.css` hashes to
   c9bd4cd6…, which is byte-for-byte the rev307 cut flattened above, so the chain is unbroken and this is a pure
   append at both ends. The desk's own verification: 0 of the 279 tokens declared before rev309 change in light or
   dark; the only two light "changes" are `--ft-ground-note` and `--ft-edge-note`, which were undeclared and now
   carry their old fallbacks.

   ⚠️ "LAST" MOVES AGAIN, for the same reason it moved to rev307: this block is appended after it, and a later
   declaration of the same token wins. Nothing here redeclares an rev307 name (asserted at intake), so the two do not
   contend — but the rule stands for the next cut, and rev302's original note is now two revisions behind the block
   that owns the position.

   ✅ WHAT IT LETS US BIND, and every one of these was named in D023 as literal-for-the-designer:
     [ftset-ix-note]            → --ft-note-danger-ground     light #FBEDEC unchanged · dark #3A1B1A
     the disabled .ft-ghost act → --ft-act-warn-ground/-ink    light #FBF6EA / #8A6A1F · dark #2A2415 / #E0A155
     the disabled foreground    → --ft-ink-disabled-faint      light #B6BCC6 unchanged · dark #4A5668
     the narrowed chip edge     → --ft-chip-narrowed-edge      light #C9D6E8 unchanged · dark #35557D
     the warn banner            → --ft-banner-warn-*           light unchanged · dark #2A2415 / #5C4E26 / #E0A155
     the info banner edge       → --ft-banner-info-edge        light unchanged · dark #35557D

   🔴 ONE STATED LIGHT MOVE, and it is the designer's ruling (TK-11): the disabled act's ink goes from #8A6D1F to
   #8A6A1F, so ONE warn ink exists instead of two values a digit apart. 1.03:1 between them — not visible — and the
   contrast on #FBF6EA rises from 4.54 to 4.68. The desk asked for it to be named in the reply, and it is; the visual
   baseline for that one act may want re-approving.
   ⚠️ The two values were BOTH in my D023 list, one line apart, and I had not noticed they differed. The desk did. */
/* ═══ rev309 · THE WARN AND INFO BANNER TONES (TK-8 · TK-9, the developer's dark push).
   RULING: declare, don't remap. The live warn banner (fill #FBF6EA · edge #E8D9B8 · ink #8A6A1F) and the info banner edge
   (#C9D6E8) are their own measured tone, not the status-warning set (#F7EFDD tint, #8A6420 annotation ink) and not an existing edge.
   Remapping would change the light banner; these four tokens keep light byte-identical and give dark a value.
   Light: ink on fill 4.68:1 · on white 5.05:1. Dark: ink #E0A155 on fill #2A2415 6.90:1.
   The edges are decorative boundaries (light edge on the page 1.40, info 1.47); dark keeps them at least as visible (2.14 · 2.29).
   Also closed here: --ft-ground-note and --ft-edge-note had the rev307 hole (declared in dark only, read through a fallback in
   light by --ft-ground-premium / --ft-edge-premium). Declared in light at their fallback values, byte-identical. */
:root {
  --ft-ground-note:              #FBF6EA;
  --ft-edge-note:                #E8D9A8;
  --ft-banner-warn-ground:       #FBF6EA;
  --ft-banner-warn-edge:         #E8D9B8;
  --ft-banner-warn-ink:          #8A6A1F;
  --ft-banner-info-edge:         #C9D6E8;
}
:root[data-theme="dark"], :root[data-ft-theme="dark"], html[data-theme="dark"], html[data-ft-theme="dark"] {
  --ft-ground-note:              #2A2415;
  --ft-edge-note:                #4A3F1F;
  --ft-banner-warn-ground:       #2A2415;
  --ft-banner-warn-edge:         #5C4E26;
  --ft-banner-warn-ink:          #E0A155;
  --ft-banner-info-edge:         #35557D;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-ground-note:              #2A2415;
  --ft-edge-note:                #4A3F1F;
  --ft-banner-warn-ground:       #2A2415;
  --ft-banner-warn-edge:         #5C4E26;
  --ft-banner-warn-ink:          #E0A155;
  --ft-banner-info-edge:         #35557D;
}


/* ═══ rev309b · FOUR MORE LITERALS FROM THE DEVELOPER (ADDENDUM-rev309, TK-10 · TK-11 · TK-12 · TK-9 confirm).
   TK-10 · DECLARED, NOT MOVED. The danger note [ftset-ix-note] fill is #FBEDEC on a signed-off surface; --ft-status-danger-tint
     (#FBEBEA) is 1.01:1 away and invisible as a change, but the rule is byte-identical unless ruled, and no ruling is needed
     to keep it. --ft-note-danger-ground: #FBEDEC light, #3A1B1A dark (the danger-tint dark value). Danger ink on it: 6.54 / 6.43.
   TK-11 · ONE WARN INK, AND IT IS #8A6A1F (a stated LIGHT MOVE for the disabled act pair: #8A6D1F → #8A6A1F, 1.03:1 apart).
     Two inks 3 green-steps apart would be two tokens that nobody can tell apart and that drift independently. The banner value
     wins because it is the higher-contrast one (4.68 on #FBF6EA vs 4.54). The act pair reads its own names
     (--ft-act-warn-ground / --ft-act-warn-ink), aliased to the warn tone, so Tier 3 can split them later without a page edit.
     The fill #FBF6EA is already exactly --ft-banner-warn-ground: no move.
   TK-12 · --ft-ink-disabled-faint: #B6BCC6 light (1.91 on white), #4A5668 dark (2.35 on the page, 2.15 on raised). Exempt from
     4.5:1 (disabled, WCAG 1.4.3); dark keeps the same "faint" distance, one step quieter than --ft-ink-disabled (#6B7789).
     The existing --ft-ink-disabled (#A6AEBA) is unchanged: the two are separate rungs, both in use.
   TK-9 · ONE EDGE TOKEN FOR BOTH USES. --ft-edge-info (#C9D6E8 / #35557D) is the source; the info banner and the narrowed filter
     chip read it through their own names, so a Tier 3 edit of the info edge moves both, and a split is one line. */
:root {
  --ft-edge-info:                #C9D6E8;
  --ft-note-danger-ground:       #FBEDEC;
  --ft-ink-disabled-faint:       #B6BCC6;
  --ft-banner-info-edge:         var(--ft-edge-info);
  --ft-chip-narrowed-edge:       var(--ft-edge-info);
  --ft-act-warn-ground:          var(--ft-banner-warn-ground);
  --ft-act-warn-ink:             var(--ft-banner-warn-ink);
}
:root[data-theme="dark"], :root[data-ft-theme="dark"], html[data-theme="dark"], html[data-ft-theme="dark"] {
  --ft-edge-info:                #35557D;
  --ft-note-danger-ground:       #3A1B1A;
  --ft-ink-disabled-faint:       #4A5668;
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-edge-info:                #35557D;
  --ft-note-danger-ground:       #3A1B1A;
  --ft-ink-disabled-faint:       #4A5668;
}

/* V-10 / rev312 NT-5 — below 1280 the rail is NARROWED (contract 63: pinned ≥1280 · narrowed 1024–1279).
   `html:root` for the same ordering reason as the block above. */
@media (max-width: 1279px) { html:root { --ft-rail-now: var(--ft-rail-width-narrow, 204px); } }

/* CLOUD-E2 (2026-10-02) — rev328c's block, ported VERBATIM from the held pack's ft-tokens-fasttera.css (40 tokens, every
   name new to this layer and no value already carried by another token — checked by scripts/e2/colour-tokens.mjs). */
/* rev328c · batch 1 (Settings). Values the Settings pages painted with no token of the exact same value, named here with that value so the
   pages render the same pixels. The alpha scales are the shadow and border colours the Settings shell drew by hand (ink #0E1A2B, brand
   #0A2344, white). Consolidating them into fewer steps is a design decision for the owner (README). No dark values yet (dark is paused). */
:root {
  --ft-alpha-ink-08: rgba(14,26,43,.08);
  --ft-alpha-ink-09: rgba(14,26,43,.09);
  --ft-alpha-ink-12: rgba(14,26,43,.12);
  --ft-alpha-ink-14: rgba(14,26,43,.14);
  --ft-alpha-ink-15: rgba(14,26,43,.15);
  --ft-alpha-ink-16: rgba(14,26,43,.16);
  --ft-alpha-ink-18: rgba(14,26,43,.18);
  --ft-alpha-ink-25: rgba(14,26,43,.25);
  --ft-alpha-ink-45: rgba(14,26,43,.45);
  --ft-alpha-ink-62: rgba(14,26,43,.62);
  --ft-alpha-brand-14: rgba(10,35,68,.14);
  --ft-alpha-brand-16: rgba(10,35,68,.16);
  --ft-alpha-brand-18: rgba(10,35,68,.18);
  --ft-alpha-brand-20: rgba(10,35,68,.2);
  --ft-alpha-brand-30: rgba(10,35,68,.3);
  --ft-alpha-white-06: rgba(255,255,255,.06);
  --ft-alpha-white-12: rgba(255,255,255,.12);
  --ft-alpha-white-14: rgba(255,255,255,.14);
  --ft-alpha-white-30: rgba(255,255,255,.3);
  --ft-alpha-white-35: rgba(255,255,255,.35);
  --ft-alpha-white-45: rgba(255,255,255,.45);
  --ft-alpha-muted-55: rgba(90,100,115,.55);
  --ft-scrim-connect: rgba(10,20,35,.55);
  --ft-ink-neutral-dark: #444444;
  --ft-ground-success-soft: #7FB79A;
  --ft-ink-premium-deep: #8A6D1F;
  --ft-edge-info-strong: #A9C7E8;
  --ft-ink-placeholder-cool: #C9D2E0;
  --ft-ground-track: #DDE2EA;
  --ft-edge-soft-alt: #E7EAEF;
  --ft-ground-muted-3: #E8EBF0;
  --ft-ground-muted-2: #E9EDF3;
  --ft-ground-info-soft: #EAF2FB;
  --ft-ground-info-soft-alt: #EAF3FB;
  --ft-ground-ghost-hover: #F1F4F8;
  --ft-ground-danger-soft: #F7E4E3;
  --ft-ground-danger-faint: #FBF0F0;
  --ft-ink-muted-brand: #8A94A3;
  --ft-ink-faint-cool: #9AA6B8;
  --ft-edge-cool: #C3CCDA;
}

/* ═══ CLOUD-E2 · SETTINGS LIGHT-ONLY VALUES — KEPT SEPARATE BY DESK RULING (2026-10-02) ═══════════════════════════════════
   The desk: "keep them separate (no merge). Dark mode is paused by owner ruling; they'll be consolidated in the
   dark-mode round." Most of these repeat the LIGHT value of a semantic token that also carries a prefers-color-scheme
   dark value (#41506A is --ft-ink-body's light value); folding them onto those tokens is the dark-mode round's decision,
   because on an OS-dark screen it would change what this block keeps exactly as the literals painted it.
   ⚠️ Do not reuse an --ft-e2-* token in new code: reach for the semantic token, and let the dark-mode round decide.
   ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────── */
/* CLOUD-E2 · build (2026-10-02). Values the Settings components painted with no token of the EXACT same value
   (after rev328c's block above). Named by role + the colour's own hex, so a name says what it paints; one name per value.
   Consolidating them into fewer steps would change pixels, so it is the owner's call — the same rule rev328c's README states.
   No dark values (dark is paused). Generated by scripts/e2/colour-tokens.mjs. */
:root {
  --ft-e2-edge-a7f3d0: #A7F3D0;
  --ft-e2-edge-bcd6ee: #BCD6EE;
  --ft-e2-edge-bfc6d0: #BFC6D0;
  --ft-e2-edge-bfdbfe: #BFDBFE;
  --ft-e2-edge-bfe3cc: #BFE3CC;
  --ft-e2-edge-c9d1dd: #C9D1DD;
  --ft-e2-edge-c9d6e8: #C9D6E8;
  --ft-e2-edge-cbd2dc: #CBD2DC;
  --ft-e2-edge-d1d5db: #D1D5DB;
  --ft-e2-edge-d7dce4: #D7DCE4;
  --ft-e2-edge-dfe3eb: #DFE3EB;
  --ft-e2-edge-e2e6ed: #E2E6ED;
  --ft-e2-edge-e4c77a: #E4C77A;
  --ft-e2-edge-e5e7eb: #E5E7EB;
  --ft-e2-edge-e6e9ef: #E6E9EF;
  --ft-e2-edge-e8d9a8: #E8D9A8;
  --ft-e2-edge-e8d9b8: #E8D9B8;
  --ft-e2-edge-e9b7b5: #E9B7B5;
  --ft-e2-edge-ead9a8: #EAD9A8;
  --ft-e2-edge-eef1f5: #EEF1F5;
  --ft-e2-edge-f0d9a8: #F0D9A8;
  --ft-e2-edge-f1c4c2: #F1C4C2;
  --ft-e2-edge-f1c9c6: #F1C9C6;
  --ft-e2-edge-f1c9c9: #F1C9C9;
  --ft-e2-edge-f3f5f8: #F3F5F8;
  --ft-e2-edge-f4f6f9: #F4F6F9;
  --ft-e2-edge-fca5a5: #FCA5A5;
  --ft-e2-edge-fcd34d: #FCD34D;
  --ft-e2-edge-fecaca: #FECACA;
  --ft-e2-ground-00823a: #00823A;
  --ft-e2-ground-0e1a2b-a30: rgba(14,26,43,.3);
  --ft-e2-ground-0e1a2b-a32: rgba(14,26,43,.32);
  --ft-e2-ground-0e1a2b-a35: rgba(14,26,43,.35);
  --ft-e2-ground-0f172a-a45: rgba(15,23,42,.45);
  --ft-e2-ground-c9d6ea: #C9D6EA;
  --ft-e2-ground-d1fae5: #D1FAE5;
  --ft-e2-ground-e0f5f7: #E0F5F7;
  --ft-e2-ground-e4f2eb: #E4F2EB;
  --ft-e2-ground-e7f3ec: #E7F3EC;
  --ft-e2-ground-eaf0f8: #EAF0F8;
  --ft-e2-ground-eaf3ee: #EAF3EE;
  --ft-e2-ground-ecf0f5: #ECF0F5;
  --ft-e2-ground-ecfdf5: #ECFDF5;
  --ft-e2-ground-eef0ff: #EEF0FF;
  --ft-e2-ground-efe7fb: #EFE7FB;
  --ft-e2-ground-eff6ff: #EFF6FF;
  --ft-e2-ground-f1ebfb: #F1EBFB;
  --ft-e2-ground-f1f3f6: #F1F3F6;
  --ft-e2-ground-f1f3f7: #F1F3F7;
  --ft-e2-ground-f1f5f9: #F1F5F9;
  --ft-e2-ground-f2f5f9: #F2F5F9;
  --ft-e2-ground-f3ebdd: #F3EBDD;
  --ft-e2-ground-f4eee0: #F4EEE0;
  --ft-e2-ground-f5f5f5: #F5F5F5;
  --ft-e2-ground-f6f7fb: #F6F7FB;
  --ft-e2-ground-f7f8fa: #F7F8FA;
  --ft-e2-ground-f7f9fb: #F7F9FB;
  --ft-e2-ground-fafbfc: #FAFBFC;
  --ft-e2-ground-fbebea: #FBEBEA;
  --ft-e2-ground-fbedec: #FBEDEC;
  --ft-e2-ground-fbf3e2: #FBF3E2;
  --ft-e2-ground-fbf6ea: #FBF6EA;
  --ft-e2-ground-fbf6ec: #FBF6EC;
  --ft-e2-ground-fcf2f1: #FCF2F1;
  --ft-e2-ground-fcfdfe: #FCFDFE;
  --ft-e2-ground-fee2e2: #FEE2E2;
  --ft-e2-ground-fef2f2: #FEF2F2;
  --ft-e2-ground-fff5f5: #FFF5F5;
  --ft-e2-ground-fff9e8: #FFF9E8;
  --ft-e2-ground-fffbeb: #FFFBEB;
  --ft-e2-ink-047857: #047857;
  --ft-e2-ink-0b6e7a: #0B6E7A;
  --ft-e2-ink-0f172a: #0F172A;
  --ft-e2-ink-17714a: #17714A;
  --ft-e2-ink-19714a: #19714A;
  --ft-e2-ink-334155: #334155;
  --ft-e2-ink-33425b: #33425B;
  --ft-e2-ink-374151: #374151;
  --ft-e2-ink-41506a: #41506A;
  --ft-e2-ink-516f90: #516F90;
  --ft-e2-ink-5f6873: #5F6873;
  --ft-e2-ink-6431da: #6431DA;
  --ft-e2-ink-64748b: #64748B;
  --ft-e2-ink-6b4e00: #6B4E00;
  --ft-e2-ink-6b5416: #6B5416;
  --ft-e2-ink-6c4bd6: #6C4BD6;
  --ft-e2-ink-8a1c1c: #8A1C1C;
  --ft-e2-ink-8a6420: #8A6420;
  --ft-e2-ink-8a6a1f: #8A6A1F;
  --ft-e2-ink-92400e: #92400E;
  --ft-e2-ink-9aa3b1: #9AA3B1;
  --ft-e2-ink-9ca3af: #9CA3AF;
  --ft-e2-ink-a3231f: #A3231F;
  --ft-e2-ink-b6bcc6: #B6BCC6;
  --ft-e2-ink-b91c1c: #B91C1C;
  --ft-e2-ink-c5cad3: #C5CAD3;
  --ft-e2-paint-1b1b1b: #1B1B1B;
  --ft-e2-paint-1f8a5b: #1F8A5B;
  --ft-e2-paint-c7cdd6: #C7CDD6;
  --ft-e2-paint-c7d6ea: #C7D6EA;
  --ft-e2-paint-c9cfd8: #C9CFD8;
  --ft-e2-paint-f0cfcf: #F0CFCF;
  --ft-e2-paint-f0f7f3: #F0F7F3;
  --ft-e2-paint-f5a623: #F5A623;
  --ft-e2-paint-fbecec: #FBECEC;
  --ft-e2-shadow-0a2344-a25: rgba(10,35,68,.25);
  --ft-e2-shadow-0a2344-a28: rgba(10,35,68,.28);
  --ft-e2-shadow-0f172a-a06: rgba(15,23,42,.06);
  --ft-e2-shadow-0f172a-a25: rgba(15,23,42,.25);
  --ft-e2-shadow-0f172a-a35: rgba(15,23,42,.35);
  --ft-e2-shadow-141414-a16: rgba(20,20,20,.16);
  --ft-e2-shadow-1f5fb0-a14: rgba(31,95,176,.14);
  --ft-e2-shadow-6161ff-a12: rgba(97,97,255,.12);
  --ft-e2-paint-6b7a93: #6B7A93;
}

/* ═══ W2b (2026-10-04) · desk addendum — THE STAGE PALETTE. W3's census found the Lifecycle Stage table painting its
   stage colours as inline rgb() literals. The values are the seed's own (pipeline_seed.SEED = HubSpot's measured stage
   colours, the list the stage colour picker offers), so each colour is a ROLE a stage can carry; shared/stage-colors.js
   maps a stored hex to its role. One set for both themes, on purpose: a stage colour is the stage's identity (a dot
   beside its name, never text on a ground), and HubSpot keeps it in every theme. */
:root {
  --ft-stage-blue: #016DE1; --ft-stage-rust: #C93700; --ft-stage-teal: #007C7D; --ft-stage-magenta: #D20688;
  --ft-stage-gold: #F7C03E; --ft-stage-violet: #7D53E9; --ft-stage-lavender: #E5E1FA; --ft-stage-grey: #EBEBEB;
  --ft-stage-green: #00823A; --ft-stage-red: #AC0020;
}

/* ═══ W5 · W2c's request 13 (desk, 7 Oct) · ADJACENT CHART SERIES ARE DISTINGUISHABLE, light and dark ═══════════════════
   Measured in Chromium on the SERVED chain (this file + brand deltas 182 · 183 · 187 · 189) before this block: in light,
   series 2 (--ft-accent = delta 182's #1F5FB0) and series 3 (the azure, #1F5FB0) were IDENTICAL — W2c's Update and Delete
   in one blue; in dark, series 1 #3773D1 and series 3 #4076BB were near-identical. (CORRECTED 7 Oct: a first reading of
   this file alone said series 2 resolved to nothing; the deltas, loaded after it, define the accent.) The designer named the same collision
   in rev304 («series-3 = series-2 in light (#1F5FB0)») and, for the ten-bar report, used his series 11 in place of 3.
   This block takes HIS series 7–11 (rev304, ft-tokens-fasttera.css 790–792, verbatim values, each ≥ 3:1 against the
   raised ground in its theme) and points 2 → his teal (series 9, the hue the retired accent was) and 3 → his olive
   (series 11, his own substitute for 3). No colour of ours. The ramp reads navy · teal · olive · brown · slate · brass.
   Pinned by e2e/chart-series-distinct.spec.js (every series resolves; adjacent pairs ΔE76 ≥ 20, light and dark). */
:root {
  --ft-chart-series-7: #19714A; --ft-chart-series-8: #7A4FB0; --ft-chart-series-9: #0E7C86; --ft-chart-series-10: #9C3D7A; --ft-chart-series-11: #5A6B1F;
  --ft-chart-series-2: var(--ft-chart-series-9); --ft-chart-series-3: var(--ft-chart-series-11);
}
:root[data-theme="dark"], :root[data-ft-theme="dark"], html:root[data-theme="dark"], html[data-theme="dark"] {
  --ft-chart-series-7: #3FAE7A; --ft-chart-series-8: #A585D6; --ft-chart-series-9: #3BB0BA; --ft-chart-series-10: #D17AAE; --ft-chart-series-11: #A9BA5E;
  --ft-chart-series-2: var(--ft-chart-series-9); --ft-chart-series-3: var(--ft-chart-series-11);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-chart-series-7: #3FAE7A; --ft-chart-series-8: #A585D6; --ft-chart-series-9: #3BB0BA; --ft-chart-series-10: #D17AAE; --ft-chart-series-11: #A9BA5E;
  --ft-chart-series-2: var(--ft-chart-series-9); --ft-chart-series-3: var(--ft-chart-series-11);
}

/* ═══ rev340 · D-009 H3 · ONE FORM FIELD WIDTH. Owner found Profile fields at 320 / 140+220+96 / 360 / 320. A settings form's single-value
   field is now one width; the phone group (country + number) spans the same width and the extension sits after it (as measured).
   Applies to Settings ▸ General ▸ Profile and User detail ▸ Preferences ▸ Profile (theme-neutral, no dark value needed).
   W5 · page-1 hotfix (D-W5-0018): copied from the pack verbatim; W3's O3 already reads both with these values as fallbacks. */
:root { --ft-form-field-w: 360px; --ft-form-cc-w: 120px;
  /* rev340 · H4 · a left sub-menu is wide enough for its longest EN label on one line («Custom user properties» + lock = 212px);
     a longer label (or Arabic) WRAPS, never ellipsis. */
  --ft-subnav-w: 220px; }

/* ═══ W5 · D-W5-0028 · THEME STEP 1 · THE PACK'S TOKENS (FULL-rev370b + rev372, 2026-10-11) ═════════════════════════════
   The designer's chain (inbox/tokens-e.css → ft-tokens-fasttera.css → ft-tokens-brand-delta-207.css; the cut is
   tests/fixtures/design-pack/S-W5-2026-10-11-rev380-tokens (re-cut for rev380, D-W5-0036), each sha256 against his SEND-MANIFEST).
   Every line below is HIS declaration, verbatim — no value of ours. ft-token-parity.spec.js resolves both chains.
   · ADOPTED (14) — roles where his newer cut differs from ours and no ruling holds ours:
       --ft-chart-series-1…8: the B1 eight-colour series (desk D-W5-0026 ruling 2: GO). His 2 and 3 are distinct now
         (#C75A18 · #0F7B6C), so the 7 Oct hold that pointed 2 → series 9 and 3 → series 11 is released.
       --ft-board-card-w · --ft-board-col-ground (rev366 board) · --ft-chart-label-ink · --ft-ink-muted · --ft-empty-ink ·
       --ft-table-row-ground-hover (rev366: the CRM index row hover).
   · ADDED (201) — names his chain defines and ours did not: toolbar, bulk bar, pager, row rules, report/compact/avatar
       rows, meter, notify (D-022), message, option card, side panel, card radius, palette, fields, z-layers.
   · NOT here, on purpose: --ft-table-row-ground-alt and --ft-table-zebra (desk S206, the zebra stays gone);
       --ft-action-cancel-hover · -pressed (owner S107 §4), --ft-shadow-list (desk D12, the list edge),
       --ft-table-row-h 48 (owner H12) and --ft-table-row-h-comfortable 72 (the Data Model sheets) — HELD in the spec.
   · `html:root`, not `:root`: rev207's ordered series-6 line (above, in his html:root block) is (0,1,1) and would
     outrank a plain :root here; the B1 GO replaces it, so this block must win the same way. */
html:root {
  --ft-chart-series-1: #1F5FB0;
  --ft-chart-series-2: #C75A18;
  --ft-chart-series-3: #0F7B6C;
  --ft-chart-series-4: #C04B9C;
  --ft-chart-series-5: #6B3A12;
  --ft-chart-series-6: #28185F;
  --ft-chart-series-7: #A0884A;
  --ft-chart-series-8: #5E6E2E;
  --ft-board-card-w: 262px;
  --ft-board-col-ground: #F0F0F0;
  --ft-chart-label-ink: var(--ft-ink-muted);
  --ft-empty-ink: #333333;
  --ft-ink-muted: #5A6473;
  --ft-table-row-ground-hover: #E6E6E6;
  --ft-alpha-white-62: rgba(255,255,255,.62);
  --ft-beta-ground: #EFE7FB;
  --ft-beta-ink: #6431DA;
  --ft-board-col-radius: 16px;
  --ft-bulk-bar-edge: var(--ft-edge-strong);
  --ft-bulk-bar-h: 40px;
  --ft-button-h-sm: 24px;
  --ft-button-radius: var(--ft-radius-pill, 999px);
  --ft-card-radius: var(--ft-radius-3, 8px);
  --ft-cell-chip-h: 20px;
  --ft-cell-chip-radius: 6px;
  --ft-cell-picker-h: var(--ft-control-3, 32px);
  --ft-chart-legend-dot-edge: 2px;
  --ft-chart-legend-dot-radius: var(--ft-radius-pill, 999px);
  --ft-chart-legend-dot-size: 10px;
  --ft-chart-legend-gap: var(--ft-space-3, 8px);
  --ft-checkbox-col-w: 45px;
  --ft-checkbox-on: var(--ft-control-checked, var(--ft-accent));
  --ft-checkbox-size: 16px;
  --ft-chip-branch-ink: var(--ft-status-success-ink);
  --ft-chip-mixed-ground: var(--ft-banner-warn-ground);
  --ft-chip-mixed-ink: var(--ft-banner-warn-ink);
  --ft-confirm-w: 480px;
  --ft-edge-purple: #E9D5F0;
  --ft-edge-success: #BEE3CE;
  --ft-empty-box-h: 152px;
  --ft-empty-box-pad: 64px;
  --ft-export-w: 650px;
  --ft-field-edge: #333333;
  --ft-field-h: 40px;
  --ft-field-label-ink: #333333;
  --ft-field-label-size: 0.875rem;
  --ft-field-text: 1rem;
  --ft-filters-panel-w: 483px;
  --ft-focus-ring-soft: var(--ft-list-ring);
  --ft-ground-brand-active: var(--ft-ground-brand-deep);
  --ft-ground-destructive: #B40C0E;
  --ft-ground-destructive-active: #7A0708;
  --ft-ground-destructive-hover: #97090B;
  --ft-ground-field-disabled: #F7F8FA;
  --ft-ground-interactive: var(--ft-ink-interactive);
  --ft-ground-interactive-soft: var(--ft-accent-tint);
  --ft-ground-neutral-1: #D7DCE4;
  --ft-ground-neutral-2: #C3CCDA;
  --ft-ground-neutral-3: #5F6873;
  --ft-ground-neutral-4: #46586B;
  --ft-ground-on-brand-disabled: #33445A;
  --ft-ground-purple-tint: #F7EDFB;
  --ft-hit-min: 44px;
  --ft-ink-chevron-faint: #B6BECD;
  --ft-ink-column: #33425B;
  --ft-ink-interactive-active: var(--ft-accent-strong);
  --ft-ink-on-brand-icon: #8FB4E8;
  --ft-ink-pager-disabled: #9AA3B1;
  --ft-ink-purple: #7A3E96;
  --ft-list-text-current-ground: #F0F0F0;
  --ft-list-text-row-h: 30px;
  --ft-list-text-trigger-h: 24px;
  --ft-message-body-size: 14px;
  --ft-message-disc: 28px;
  --ft-message-disc-ink: #FFFFFF;
  --ft-message-enter-ms: 260ms;
  --ft-message-pad-block: 16px;
  --ft-message-pad-inline: 20px;
  --ft-message-radius: 16px;
  --ft-message-shadow: 0 8px 24px rgba(14,26,43,.14), 0 2px 6px rgba(14,26,43,.08);
  --ft-message-title-size: 16px;
  --ft-message-top: calc(var(--ft-shell-bar-h, 54px) + 12px);
  --ft-message-w: 668px;
  --ft-meter-fill: var(--ft-accent);
  --ft-meter-fill-at: var(--ft-status-danger-ink);
  --ft-meter-fill-near: var(--ft-status-warning-ink);
  --ft-meter-h: 8px;
  --ft-meter-radius: var(--ft-radius-pill, 999px);
  --ft-meter-track: var(--ft-ground-surface-2);
  --ft-notify-accent: var(--ft-accent);
  --ft-notify-accent-w: 3px;
  --ft-notify-badge-ground: #1F5FB0;
  --ft-notify-badge-h: 16px;
  --ft-notify-badge-ink: var(--ft-ink-on-brand);
  --ft-notify-badge-ring: var(--ft-shell-bar);
  --ft-notify-badge-text: 10px;
  --ft-notify-card-w: 360px;
  --ft-notify-drawer-title: 20px;
  --ft-notify-drawer-w: 480px;
  --ft-notify-glyph: var(--ft-control-3, 32px);
  --ft-notify-panel-max-h: 560px;
  --ft-notify-panel-w: 400px;
  --ft-notify-tab-h: 44px;
  --ft-notify-title-weight: var(--ft-weight-row-title, 500);
  --ft-notify-tools-h: 48px;
  --ft-notify-unread-dot: 8px;
  --ft-notify-unread-ground: var(--ft-brand-tint);
  --ft-option-card-edge: var(--ft-edge-strong);
  --ft-option-card-edge-hover: var(--ft-ink-muted);
  --ft-option-card-edge-on: var(--ft-accent);
  --ft-option-card-ground-locked: var(--ft-ground-surface);
  --ft-option-card-ground-on: var(--ft-accent-soft);
  --ft-pager-current-edge: #8A8A8A;
  --ft-pager-current-ground: #F0F0F0;
  --ft-pager-h: 32px; /* THE THEME RELEASE (W2d, 29 Oct): rev612's pager 32 (was 30; the pack sheet already served 32) */
  --ft-pager-ink: #333333;
  --ft-pager-ink-disabled: #8A8A8A;
  --ft-pager-radius: var(--ft-radius-pill, 999px);
  --ft-pager-select-h: 40px;
  --ft-palette-amber-1: #5A4A12;
  --ft-palette-blue-1: #2A6FDB;
  --ft-palette-blue-2: #2D8CFF;
  --ft-palette-blue-3: #5059C9;
  --ft-palette-blue-4: #5C6BC0;
  --ft-palette-blue-5: #1E88E5;
  --ft-palette-green-1: #00823A;
  --ft-palette-green-2: #43A047;
  --ft-palette-green-3: #7FE0A8;
  --ft-palette-magenta-1: #E5409E;
  --ft-palette-magenta-10: #D8A3B8;
  --ft-palette-magenta-2: #F4A3C0;
  --ft-palette-magenta-3: #B23A6B;
  --ft-palette-magenta-4: #4A154B;
  --ft-palette-magenta-5: #E2C2D2;
  --ft-palette-magenta-6: #D81B60;
  --ft-palette-magenta-7: #B8256F;
  --ft-palette-magenta-8: #D22A6E;
  --ft-palette-magenta-9: #B0409B;
  --ft-palette-orange-1: #B45309;
  --ft-palette-orange-2: #FF8F59;
  --ft-palette-orange-3: #F5C26B;
  --ft-palette-orange-4: #E8A33D;
  --ft-palette-orange-5: #F4A07A;
  --ft-palette-orange-6: #E8734A;
  --ft-palette-orange-7: #E8743B;
  --ft-palette-orange-8: #FB8C00;
  --ft-palette-orange-9: #B8862F;
  --ft-palette-red-1: #E7C3C3;
  --ft-palette-red-2: #F1C7C4;
  --ft-palette-red-3: #E0989A;
  --ft-palette-red-4: #F4C7C7;
  --ft-palette-red-5: #F0C4C2;
  --ft-palette-red-6: #B40C0E;
  --ft-palette-teal-1: #00A4BD;
  --ft-palette-teal-2: #00BDA5;
  --ft-palette-teal-3: #0E8C9B;
  --ft-palette-teal-4: #00A38D;
  --ft-palette-teal-5: #00897B;
  --ft-palette-teal-6: #7FD1DE;
  --ft-palette-teal-7: #5FB4B4;
  --ft-palette-violet-1: #7C4DFF;
  --ft-palette-violet-2: #8B5CF6;
  --ft-palette-violet-3: #6B4FB8;
  --ft-palette-violet-4: #6C4BD6;
  --ft-palette-violet-5: #7E57C2;
  --ft-palette-violet-6: #7A5CC0;
  --ft-palette-violet-7: #C9B6E0;
  --ft-palette-violet-8: #8A6BC8;
  --ft-palette-violet-9: #7C4DBE;
  --ft-panel-edge: var(--ft-edge-field);
  --ft-panel-edge-soft: var(--ft-edge-soft);
  --ft-panel-ground: var(--ft-ground-page);
  --ft-preview-w: 420px;
  --ft-radius-hair: 2px;
  --ft-radius-modal: 16px;
  --ft-scrim-drawer: rgba(14,26,43,.35);
  --ft-shadow-bar: 0 -2px 14px var(--ft-alpha-ink-09);
  --ft-shadow-drawer: 0 0 40px var(--ft-shadow-drawer-ink);
  --ft-shadow-drawer-ink: rgba(10,35,68,.25);
  --ft-shadow-inset: inset 0 1px 2px var(--ft-alpha-ink-08);
  --ft-shadow-scrim: 0 0 0 9999px var(--ft-alpha-muted-55);
  --ft-side-panel-head-ground: var(--ft-panel-ground);
  --ft-side-panel-specimen-h: 520px;
  --ft-side-panel-w: 460px;
  /* A3-T · D-A3-T-0012 §2 (A3-3 ASK-7) · the LIST-BESIDE-EDITOR layout — the pack's values (3 Nov tree, Announcements.dc.html
     :343 list 1 1 300 / max 360 · :106 editor 999 1 420 · :107 no-selection line max 420 · :131 field pairs minmax(200, 1fr)) */
  --ft-md-list-basis: 300px;
  --ft-md-list-max: 360px;
  --ft-md-detail-basis: 420px;
  --ft-md-empty-max: 420px;
  --ft-md-field-min: 200px;
  --ft-slider-thumb: 16px;
  --ft-slider-thumb-lg: 20px;
  --ft-slider-track: 4px;
  --ft-surface-header: #0A2344;
  --ft-surface-header-ink: #FFFFFF;
  --ft-switch-h: 22px;
  --ft-switch-knob: 18px;
  --ft-switch-w: 40px;
  --ft-table-cell-hover-edge: #666666;
  --ft-table-cell-pad-index: 2px 24px;
  --ft-table-freeze-edge: #CCCCCC;
  --ft-table-head-h-avatar: 40px;
  --ft-table-head-h-list: 44px;
  --ft-table-head-hover: #EBEBEB;
  --ft-table-row-h-avatar: 56px;
  --ft-table-row-h-compact: 32px;
  --ft-table-row-h-report: 57px;
  --ft-table-rule-head-first: rgb(51, 51, 51);
  --ft-table-rule-row: rgb(204, 204, 204);
  --ft-text-0: 0.625rem;
  --ft-text-8: 2rem;
  --ft-text-panel-title: 1.125rem;
  --ft-toolbar-bulk-ground: var(--ft-brand-tint);
  --ft-toolbar-chip-on: var(--ft-accent-soft);
  --ft-toolbar-chip-on-ink: var(--ft-ink-on-brand, #FFFFFF);
  --ft-toolbar-gap: var(--ft-space-5, 10px);
  --ft-toolbar-h: 40px;
  --ft-toolbar-pad-block: var(--ft-space-2, 4px);
  --ft-toolbar-search-w: 320px;
  --ft-z-open-menu: var(--ft-z-dropdown, 200);
  --ft-z-tooltip: 2147483500;
}
:root[data-theme="dark"], :root[data-ft-theme="dark"], html:root[data-theme="dark"], html[data-theme="dark"] {
  --ft-chart-series-1: #6FA8E8;
  --ft-chart-series-2: #F0915A;
  --ft-chart-series-3: #3FB8A6;
  --ft-chart-series-4: #E58AC8;
  --ft-chart-series-5: #A8743A;
  --ft-chart-series-6: #8F7CF0;
  --ft-chart-series-7: #EEF1F5;
  --ft-chart-series-8: #C9D46A;
  --ft-board-col-ground: var(--ft-ground-surface);
  --ft-empty-ink: var(--ft-ink);
  --ft-ink-muted: #9AA7B9;
  --ft-table-row-ground-hover: var(--ft-ground-surface-2);
  --ft-beta-ground: #241C3A;
  --ft-beta-ink: #A78BFA;
  --ft-field-edge: #677685;
  --ft-field-label-ink: var(--ft-ink);
  --ft-ground-destructive-active: #7A0708;
  --ft-ground-destructive-hover: #8F0A0C;
  --ft-ground-neutral-1: #33445A;
  --ft-ground-neutral-2: #3B4D66;
  --ft-ground-neutral-3: #8C929B;
  --ft-ground-neutral-4: #9AA7B9;
  --ft-ink-column: #D5DCE6;
  --ft-list-text-current-ground: var(--ft-ground-surface-2);
  --ft-message-disc-ink: var(--ft-ground-page);
  --ft-message-shadow: 0 8px 24px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.3);
  --ft-meter-track: #2C405C;
  --ft-notify-badge-ground: #4076BB;
  --ft-pager-current-edge: var(--ft-edge-strong);
  --ft-pager-current-ground: var(--ft-ground-surface-2);
  --ft-pager-ink: var(--ft-ink-strong);
  --ft-pager-ink-disabled: var(--ft-ink-disabled);
  --ft-palette-amber-1: #A49B7D;
  --ft-palette-blue-1: #2A6FDB;
  --ft-palette-blue-2: #2D8CFF;
  --ft-palette-blue-3: #5059C9;
  --ft-palette-blue-4: #5C6BC0;
  --ft-palette-blue-5: #1E88E5;
  --ft-palette-green-1: #73BA93;
  --ft-palette-green-2: #43A047;
  --ft-palette-green-3: #7FE0A8;
  --ft-palette-magenta-1: #E5409E;
  --ft-palette-magenta-10: #D8A3B8;
  --ft-palette-magenta-2: #F4A3C0;
  --ft-palette-magenta-3: #B23A6B;
  --ft-palette-magenta-4: #9B7E9C;
  --ft-palette-magenta-5: #343849;
  --ft-palette-magenta-6: #D81B60;
  --ft-palette-magenta-7: #B8256F;
  --ft-palette-magenta-8: #D22A6E;
  --ft-palette-magenta-9: #B0409B;
  --ft-palette-orange-1: #B45309;
  --ft-palette-orange-2: #FF8F59;
  --ft-palette-orange-3: #F5C26B;
  --ft-palette-orange-4: #E8A33D;
  --ft-palette-orange-5: #F4A07A;
  --ft-palette-orange-6: #E8734A;
  --ft-palette-orange-7: #E8743B;
  --ft-palette-orange-8: #FB8C00;
  --ft-palette-orange-9: #B8862F;
  --ft-palette-red-1: #353846;
  --ft-palette-red-2: #373947;
  --ft-palette-red-3: #E0989A;
  --ft-palette-red-4: #373947;
  --ft-palette-red-5: #373946;
  --ft-palette-red-6: #B40C0E;
  --ft-palette-teal-1: #00A4BD;
  --ft-palette-teal-2: #00BDA5;
  --ft-palette-teal-3: #0E8C9B;
  --ft-palette-teal-4: #00A38D;
  --ft-palette-teal-5: #73BEB6;
  --ft-palette-teal-6: #7FD1DE;
  --ft-palette-teal-7: #5FB4B4;
  --ft-palette-violet-1: #7C4DFF;
  --ft-palette-violet-2: #8B5CF6;
  --ft-palette-violet-3: #6B4FB8;
  --ft-palette-violet-4: #6C4BD6;
  --ft-palette-violet-5: #7E57C2;
  --ft-palette-violet-6: #7A5CC0;
  --ft-palette-violet-7: #C9B6E0;
  --ft-palette-violet-8: #8A6BC8;
  --ft-palette-violet-9: #7C4DBE;
  --ft-panel-edge: var(--ft-source-line, #24384F);
  --ft-panel-edge-soft: var(--ft-source-line, #24384F);
  --ft-panel-ground: var(--ft-ground-raised);
  --ft-scrim-drawer: rgba(0,0,0,.55);
  --ft-shadow-drawer-ink: rgba(0,0,0,.50);
  --ft-surface-header: #0A2344;
  --ft-surface-header-ink: #FFFFFF;
  --ft-table-cell-hover-edge: var(--ft-ink);
  --ft-table-freeze-edge: var(--ft-edge-strong);
  --ft-table-head-hover: var(--ft-ground-surface-2);
}
:root[data-ft-theme="dark"], :root[data-theme="dark"] {
  --ft-chart-series-1: #6FA8E8;
  --ft-chart-series-2: #F0915A;
  --ft-chart-series-3: #3FB8A6;
  --ft-chart-series-4: #E58AC8;
  --ft-chart-series-5: #A8743A;
  --ft-chart-series-6: #8F7CF0;
  --ft-chart-series-7: #EEF1F5;
  --ft-chart-series-8: #C9D46A;
  --ft-board-col-ground: var(--ft-ground-surface);
  --ft-empty-ink: var(--ft-ink);
  --ft-ink-muted: #9AA7B9;
  --ft-table-row-ground-hover: var(--ft-ground-surface-2);
  --ft-beta-ground: #241C3A;
  --ft-beta-ink: #A78BFA;
  --ft-field-edge: #677685;
  --ft-field-label-ink: var(--ft-ink);
  --ft-ground-destructive-active: #7A0708;
  --ft-ground-destructive-hover: #8F0A0C;
  --ft-ground-neutral-1: #33445A;
  --ft-ground-neutral-2: #3B4D66;
  --ft-ground-neutral-3: #8C929B;
  --ft-ground-neutral-4: #9AA7B9;
  --ft-ink-column: #D5DCE6;
  --ft-list-text-current-ground: var(--ft-ground-surface-2);
  --ft-message-disc-ink: var(--ft-ground-page);
  --ft-message-shadow: 0 8px 24px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.3);
  --ft-meter-track: #2C405C;
  --ft-notify-badge-ground: #4076BB;
  --ft-pager-current-edge: var(--ft-edge-strong);
  --ft-pager-current-ground: var(--ft-ground-surface-2);
  --ft-pager-ink: var(--ft-ink-strong);
  --ft-pager-ink-disabled: var(--ft-ink-disabled);
  --ft-palette-amber-1: #A49B7D;
  --ft-palette-blue-1: #2A6FDB;
  --ft-palette-blue-2: #2D8CFF;
  --ft-palette-blue-3: #5059C9;
  --ft-palette-blue-4: #5C6BC0;
  --ft-palette-blue-5: #1E88E5;
  --ft-palette-green-1: #73BA93;
  --ft-palette-green-2: #43A047;
  --ft-palette-green-3: #7FE0A8;
  --ft-palette-magenta-1: #E5409E;
  --ft-palette-magenta-10: #D8A3B8;
  --ft-palette-magenta-2: #F4A3C0;
  --ft-palette-magenta-3: #B23A6B;
  --ft-palette-magenta-4: #9B7E9C;
  --ft-palette-magenta-5: #343849;
  --ft-palette-magenta-6: #D81B60;
  --ft-palette-magenta-7: #B8256F;
  --ft-palette-magenta-8: #D22A6E;
  --ft-palette-magenta-9: #B0409B;
  --ft-palette-orange-1: #B45309;
  --ft-palette-orange-2: #FF8F59;
  --ft-palette-orange-3: #F5C26B;
  --ft-palette-orange-4: #E8A33D;
  --ft-palette-orange-5: #F4A07A;
  --ft-palette-orange-6: #E8734A;
  --ft-palette-orange-7: #E8743B;
  --ft-palette-orange-8: #FB8C00;
  --ft-palette-orange-9: #B8862F;
  --ft-palette-red-1: #353846;
  --ft-palette-red-2: #373947;
  --ft-palette-red-3: #E0989A;
  --ft-palette-red-4: #373947;
  --ft-palette-red-5: #373946;
  --ft-palette-red-6: #B40C0E;
  --ft-palette-teal-1: #00A4BD;
  --ft-palette-teal-2: #00BDA5;
  --ft-palette-teal-3: #0E8C9B;
  --ft-palette-teal-4: #00A38D;
  --ft-palette-teal-5: #73BEB6;
  --ft-palette-teal-6: #7FD1DE;
  --ft-palette-teal-7: #5FB4B4;
  --ft-palette-violet-1: #7C4DFF;
  --ft-palette-violet-2: #8B5CF6;
  --ft-palette-violet-3: #6B4FB8;
  --ft-palette-violet-4: #6C4BD6;
  --ft-palette-violet-5: #7E57C2;
  --ft-palette-violet-6: #7A5CC0;
  --ft-palette-violet-7: #C9B6E0;
  --ft-palette-violet-8: #8A6BC8;
  --ft-palette-violet-9: #7C4DBE;
  --ft-panel-edge: var(--ft-source-line, #24384F);
  --ft-panel-edge-soft: var(--ft-source-line, #24384F);
  --ft-panel-ground: var(--ft-ground-raised);
  --ft-scrim-drawer: rgba(0,0,0,.55);
  --ft-shadow-drawer-ink: rgba(0,0,0,.50);
  --ft-surface-header: #0A2344;
  --ft-surface-header-ink: #FFFFFF;
  --ft-table-cell-hover-edge: var(--ft-ink);
  --ft-table-freeze-edge: var(--ft-edge-strong);
  --ft-table-head-hover: var(--ft-ground-surface-2);
}

/* ═══ W5 · desk D-W5-0036 — rev380 THEME Page Header, the designer's block VERBATIM (ft-tokens-fasttera.css rev380, sha256
   024f487e…9d758c). FtStrip reads --ft-page-tab-h; the Settings header reads the gaps; ft-token-parity.spec.js holds it. */
/* rev380 · D-030 — THE PAGE HEADER (one part for every Settings page). HubSpot, desk reading 11 Oct (1920 wide, Account Defaults and
   Users & Teams): title 24px in a 30px line → 17px → tab strip of 48px tabs; a page's explanation sits UNDER the strip (≈19px).
   The pack rounds both gaps to the 4px grid: 16. States: (a) title → gap → tabs · (b) title → title-desc → description → gap → tabs
   · (c) title → gap → content · (d) tabs → gap → explanation. Same values in light and dark (spacing, not colour). */
:root {
  --ft-page-title-lh: 30px;
  --ft-page-header-gap: 16px;
  --ft-page-header-title-desc: 8px;
  --ft-page-header-title-tabs: var(--ft-page-header-gap);
  --ft-page-header-desc-tabs: var(--ft-page-header-gap);
  --ft-page-header-title-content: var(--ft-page-header-gap);
  --ft-page-header-tabs-text: var(--ft-page-header-gap);
  --ft-page-tab-h: 48px;
}

/* ═══ D-W5-0076 §2 · THE LOGO SIZE TOKENS (desk ruling 16 Oct; unblocks W6 items 3 + 4) ═══════════════════════════════
   Exact names, which the Designer adds to his sheet as well. T3's Brand slots override them per tenant through the one
   applier. Appended — nothing above changed. */
:root {
  --ft-logo-signin: 160px;
  --ft-shell-logo: 32px;
  --ft-shell-lockup-w: 140px;
  --ft-shell-lockup-h: 32px;
}

/* D-TaskB-0122 §2 · the sign-in and loading-screen logo is sized by HEIGHT (the owner's file is almost square). Appended. */
:root {
  --ft-logo-signin-h: 120px;
  --ft-logo-signin-h-sm: 96px;
}


/* ═══ THE THEME RELEASE · W2d · the parity register re-cut to rev612 (desk D-W2d 29 Oct 11:05 #2) ══════════════════
   ft-token-parity.spec.js now compares this chain with rev612-FINAL's (S-W2d-2026-10-29-rev612-tokens). The roles that
   drifted take the pack's declaration, verbatim — the desk: «the pack wins, except a value a desk ruling already fixed»
   (body 14, Settings rows 72, pager 32 are the pack's already). --ft-table-head-ground is white too (the owner's R39,
   12 Oct, superseded OR-T1; rev612 carries it), changed IN PLACE above: ft-table-no-zebra keeps it to ONE declaration.
   ⚠️ TWO BLOCKS ON PURPOSE. A plain :root here loses every tie to the pack sheet, which loads after: those roles are
   served at the pack's value already, and this only makes the theme sheet say so. --ft-action-cancel is among them —
   under html:root it would out-rank rev585's neutral Cancel and paint every Cancel red. html:root only for the seven
   roles whose html:root lines above out-rank the pack today; those seven (and the head ground above) move on screen, to the pack's final values
   (--ft-field-edge among them: the pack's D-192 light field border, was #333333). */
:root {
  --ft-brand-secondary:        var(--ft-accent-destructive, #7C1027);
  --ft-form-field-w:           450px;
  --ft-page-header-title-desc: 16px;
  --ft-popup-edge:             #CCCCCC;
  --ft-popup-option-active:    #F0F0F0;
  --ft-popup-radius:           16px;
  --ft-popup-shadow:           0 24px 48px rgba(20,20,20,.08);
  --ft-ring-offset:            -2px;
  --ft-row-h-compact:          var(--ft-table-row-h-compact);
  --ft-tab-count-ground:       #7C1027;
  --ft-tab-count-ink:          #FFFFFF;
  --ft-view-edge:              #8A8A8A;
  --ft-action-cancel:          var(--ft-ground-destructive, #B40C0E);
}
html:root {
  --ft-field-edge:             #8A94A3;   /* D-W2d-0025 · ASK J: rev658-final's G1-3 value (ft-tokens-fasttera.css :root, 3:1 on white); was rev612's #D7DCE4. Kept at html:root so it out-ranks the older #333333 above; the dark blocks (higher specificity) keep #677685 */
  --ft-board-card-w:           264px;
  --ft-notify-drawer-w:        488px;
  --ft-notify-tab-h:           40px;
  --ft-pager-select-h:         32px;
  --ft-table-head-h-list:      40px;
  --ft-table-row-h-list:       48px;
}
