/*
 * CONNECT design tokens.
 *
 * The token layer is a real stylesheet, not markup, and it is the file a tenant
 * theme overrides. Everything downstream reads these names; no page defines its
 * own colours.
 *
 * Structure:
 *   1. Light theme            - the complete palette, on bare :root
 *   2. Dark theme             - the same names, redefined, twice:
 *                                 a) system preference, when no explicit choice
 *                                 b) explicit [data-theme="dark"]
 *   3. Legacy aliases         - original appearance-based names, repointed
 *   4. Typography and spacing - unchanged
 *
 * THREE THEME STATES, which is why the dark block appears twice:
 *   - no data-theme attribute  -> follow the operating system  (the default)
 *   - data-theme="light"       -> force light, even on a dark OS
 *   - data-theme="dark"        -> force dark, even on a light OS
 * The media query is guarded with :not([data-theme="light"]) so an explicit
 * light choice beats a dark OS. Without that guard, "Light" would do nothing
 * for anyone whose machine is set to dark.
 *
 * COLOUR RULES, decided 20 August 2026:
 *   - One accent hue (teal). The previous scheme needed two unrelated colours
 *     because gold #b8965a fails WCAG AA both as text (2.62:1 on the page) and
 *     as a text background (2.78:1 under white), so a brown was carrying text.
 *   - The navy navigation surface is identical in both themes.
 *   - Status colours are identical in both themes and only ever appear as pill
 *     fills, never as text: the mint and yellow are unreadable on a light ground
 *     (1.82:1 and 1.99:1) and the red is unreadable on a dark one (2.42:1).
 *   - Every text pairing here meets 4.5:1; every control edge meets 3:1.
 *     ConnectDesignTokenTest asserts this and will fail if a value drifts.
 */

:root {
  /* 7b. THE QR CODE'S GROUND, and the one colour in CONNECT that is
     deliberately the same in every theme. A scanner reads contrast, and a QR
     inverted for dark mode is a QR that will not scan - so this is declared
     three times with one value rather than left as a literal, which is what
     the token layer is for. */
  --connect-qr-ground: #ffffff;
  --connect-qr-ink: #0b0f19;

  /* §5: the popup close control. A grey disc rather than a darker shade of the
     popup - Neerav's choice - so it reads as a control on any surface it lands
     on, including a photograph. */
  --popup-close-fill: #E4E7EC;
  --popup-close-fill-hover: #D5D9E0;
  --popup-close-border: #D0D5DD;
  --popup-close-glyph: #1E2230;

  color-scheme: light;

  /* ---------------------------------------------------------------
   * 0. PALETTE - PRIVATE. The only place a raw value appears.
   *
   * A design system is not a set of tokens. It is a set of decisions with
   * exactly one place each, and a guard that fails the build when a second
   * place appears. Everything below this block is a ROLE, and a role names
   * what a colour is FOR. Roles are the public surface a tenant theme
   * overrides; these underscore names are not, and nothing outside this file
   * may reference one.
   *
   * The distinction earns its keep the moment two roles share a value. When
   * --connect-border and a surface's edge were both #e2e5e9, they were one
   * decision by accident, and changing a divider changed every card edge.
   * Naming the palette entry once and pointing two roles at it makes that a
   * CHOICE, visible in the file, that either role can leave without the other
   * noticing.
   * ------------------------------------------------------------- */
  --_ivory: #faf8f3;
  --_white: #ffffff;
  --_mist: #f1f3f5;
  --_navy-ink: #1f2430;
  /* THE BRAND HUE IS TEAL, in both themes. Light takes the DEEP tone, dark
     takes the light one - one hue, two lightnesses, which is what makes the
     two themes read as one product rather than two.
     These were --_navy #24456F. The light accent was migrated to navy in
     error and is reversed here, not completed. Renamed as well as revalued,
     because a palette entry called navy holding a teal is a comment that
     lies, and this file's whole argument is that a value has one home with
     one true name. */
  --_teal-brand: #0B5A55;      /* 8.05:1 under white; 7.59:1 as text on ivory */
  --_teal-brand-deep: #084440; /* hover; 10.99:1 under white */
  --_teal-tint: #E6F0EF;       /* 1.09:1 vs ivory - a tint, not a fill; carries ink at 13.36:1 */
  --_shell: #1e2230;
  --_shell-raised: #303545;
  --_shell-text-muted: #c8ceda;
  --_slate: #5f6875;
  --_slate-deep: #3f4652;
  --_steel: #7e8794;
  --_steel-pill: #7d838e;
  --_hairline: #e2e5e9;
  --_gold: #b8965a;
  /* ROUND 42 A(1). The gold DARKENED FOR A LIGHT GROUND, chosen by Neerav on
     11 September as option A. The estate already proves why it is needed:
     the_gold_that_was_replaced_still_fails_so_it_cannot_come_back measures
     #b8965a at under the text floor on white, and the nav's active mark on a
     light ground was one of the places still paying it at 2.97:1. */
  --_gold-ink: #8a6520;
  /* H4: the brand line's midpoint - the bridge between gold at the top and
     teal at the foot. A raw value, so it lives here with the others. */
  --_brand-line-bridge: #2b7f8f;
  --_teal-edge: #4E8F87;
  --_green: #1f7a47;
  --_green-soft: #dff6e9;
  --_green-pill: #1B7A47;
  --_green-pill-edge: #4c9370;
  --_green-fill: #5fd08a;
  --_green-edge: #9cc7af;
  --_amber: #8a5a00;
  --_amber-soft: #fff1c9;
  --_amber-pill: #a75f00;
  --_amber-pill-edge: #ca7300;
  --_yellow-fill: #ffd028;
  --_red: #b3261e;
  --_red-soft: #fce7e5;
  --_red-border: #d9a6a2;
  --_red-edge: #E1544B;        /* 3.77:1 on the white card - the quiet edge of the danger two-tone */
  --_red-pill: #9B2C2C;
  --_red-pill-edge: #af5959;
  --_teal-pill: #0F766E;
  --_teal-pill-edge: #3f918b;

  /* ---------------------------------------------------------------
   * 1. SURFACES - four elevation steps, not one flat ground.
   * ------------------------------------------------------------- */
  --connect-canvas: var(--_ivory);           /* warm ivory page */
  --connect-surface: var(--_white);          /* cards, panels, icon buttons */
  --connect-surface-muted: var(--_mist);    /* hovered rows, menu items */
  --connect-surface-action: var(--_mist);
  --connect-surface-overlay: var(--_white);  /* menus, dialogs */

  /* Navigation shell - the constant across both themes. */
  --connect-nav-surface: var(--_shell);
  --connect-nav-surface-raised: var(--_shell-raised);
  --connect-nav-text: var(--_white);
  --connect-nav-text-muted: var(--_shell-text-muted);

  /* ---------------------------------------------------------------
   * 2. TEXT
   * ------------------------------------------------------------- */
  --connect-text: var(--_navy-ink);             /* 14.62:1 on canvas */
  --connect-text-muted: var(--_slate);       /*  5.31:1 on canvas - #67707e sat on exactly 4.50 */
  --connect-text-on-status: var(--_slate-deep);   /* pill labels; 4.92:1 on the mint fill */

  /* ---------------------------------------------------------------
   * 3. ACCENT - one hue. Teal sits 46 degrees from the navy and 38 from
   * the success green, the widest gap left once red, amber and green
   * are spoken for by the status roles.
   * ------------------------------------------------------------- */
  /* ONE BRAND HUE - TEAL - AT TWO LIGHTNESSES. Light takes the deep tone,
     dark takes the light one. Not a per-mode pair of different colours:
     the same hue, moved along lightness so it carries on either ground.
     Measured against the REAL ivory ground #faf8f3, not the #ffffff surface
     token - the table is transparent and the page is ivory. */
  --connect-accent: #1868DB;           /* §7: BLUE is the default in both themes. 4.90:1 on ivory */
  --connect-accent-hover: #1250aa;     /* the deeper blue */
  --connect-accent-text: #1868DB;      /* 4.90:1 on ivory */
  /* ROUND 7 §1: THE INK IS PART OF THE PAIR, NOT A GLOBAL.
     This was declared ONCE, here, as white - which was a correct reading of a
     spec that described four accent FILLS and never said what sits on them. It
     broke the moment an accent went pale: mono in dark mode fills #e9e7e2 and
     painted white text on it at 1.24:1, so "Set as organisation default" was a
     blank block. Every accent block now declares its own ink, so a new accent
     cannot inherit one nobody measured against its fill.
     Measured, all eight fills with white: light blue 5.20, teal 8.05, indigo
     6.62, mono 15.83; dark blue 4.55, teal 4.81, indigo 4.97 - and mono-dark
     1.24, which takes #1E2230 at 12.81 instead. Hovers measured with them.
     This value remains the DEFAULT-accent ink, for a shell carrying no
     data-accent at all. */
  --connect-accent-on: var(--_white);        /* text sitting on a filled accent */
  /* Text and hairlines sitting on any saturated fill: the dark sidebar rail, the
     mobile bar, the reminder banner, a status pill. White in both themes because
     the fill underneath stays saturated in both - but it is a token now, so a
     future theme can reach it. Previously 17 hardcoded #fff the theme could not see. */
  --connect-on-fill: var(--_white);
  /* Derived in hue space, not by mixing navy into ivory: a 9% mix came out
     #e7e8e7, a flat grey, because the warm ground cancelled the blue. This
     keeps hue 214 and reads as navy. 1.09:1 vs ivory - a tint, not a fill -
     and carries ink at 13.41:1. */
  --connect-accent-soft: #e7eefb;      /* tinted callouts */

  /* The menu trigger is a PER-MODE treatment, expressed as tokens so the
     component keeps owning its paint without !important.
     LIGHT: no ground. The quiet fill reads as DISABLED on ivory - a grey
     ground among outlined controls says "unavailable", which a trigger must
     never accidentally say. The chevron carries the distinction, and nothing
     else in the product wears one.
     DARK: the quiet fill stays; on a dark ground it is a lift, not a greying. */
  --connect-trigger-ground: transparent;
  --connect-trigger-edge: var(--connect-accent);

  /* THE REFERENCE CONTROL - and the RELATIONSHIP, not the pair.
   *
   * Three roles that are only correct together: the ground a control sits on,
   * the edge that bounds it, and the icon that sits inside it. Recording them
   * as three values invites a later edit to move one - which is how a control
   * ends up with a 3:1 edge against a ground it no longer sits on.
   *
   * The relationship, which is what a future theme must preserve:
   *   BG is the lightest of the three and carries the other two.
   *   EDGE owes 3:1 against BG          - it is a non-text boundary (WCAG 1.4.11).
   *   ICON owes 4.5:1 against BG        - it carries meaning, so it is treated
   *                                       as text, not as decoration.
   *   EDGE is lighter than ICON, always. The boundary must not out-shout the
   *   thing it contains; when it did, the control read as a disabled field.
   *
   * Light, decided 25 August 2026 by measuring the live control in both themes:
   * a white ground rather than the ivory canvas, because these controls sit on
   * cards; a desaturated teal edge; and a deep teal icon.
   *
   * DARK IS DELIBERATELY UNCHANGED. It was measured and it holds - the same
   * three relationships already resolve there through the accent roles, so
   * restating them would create a second place for one decision. */
  /* The icon-only control's SIZE, one token for every one of them.
     connect-overflow-toggle pinned 32px of its own, which can never reach the
     44px coarse-pointer target - that is drift, not a decision, and it ends
     here. Coarse pointers lift this to 44px in the block at the foot of the
     file, the same way the other control heights are lifted.

     ROUND 18 A2a: 32px, WAS 38px. An icon button sits BESIDE a text button in
     every action tray in the product, and the text button is 32px
     (--connect-button-height). 38 beside 32 is a 6px step that nothing
     justifies and the eye reads as a mistake - measured on /admin/users, where
     a 38px pencil and a 32px "Delete user" sat 3px apart vertically. */
  --control-size: 32px;
  /* ROUND 18 A2a: THE GLYPH INSIDE IT, ONE SIZE EVERYWHERE.
     There were two: 24px in 26 of the boxes measured and 19px in 5 - the
     privacy toggle, which shrank its ink deliberately and is the 19px variant
     A2a retires. Two glyph sizes inside one box size is not a distinction
     anybody was reading; it was drift with a reason attached to one side of it.

     20px in a 32px box leaves 6px of ink-free space on each side. That number
     is not written down anywhere: --control-icon-inset derives it, so it stays
     correct when the box is resized again. */
  --control-icon-size: 20px;
  --control-icon-inset: calc((var(--control-size) - var(--control-icon-size)) / 2);
  --control-bg: var(--_white);
  /* ROUND 7 §3: OFF TEAL, ONTO THE TIERS. These were correct when teal WAS
     the accent. Round 6 made Blue the default and never re-pointed them, so
     every action chip in CONNECT stayed painted in the old brand colour
     regardless of the user's accent - invisible to a search for hex literals,
     which is directive 17.

     The glyph leads its row, so it takes the PRIMARY tier. The edge takes the
     DECORATIVE tier: the bright edge was most of what made a forty-row list
     shout, and quietening it matters more than the glyph shade.

     §3.2 also named #3a4256 for this edge specifically. Measured, it is 1.58
     on surface, 1.41 on surface-muted and 1.22 on overlay against a 3.0
     non-text floor - it would be an invisible control boundary. The same
     section's table assigns chip edges to the decorative tier, which measures
     4.00 / 3.56 / 3.08. The table is the half that survives measurement.
     Reported rather than shipped. */
  --control-border: var(--connect-icon-decorative);
  --control-icon: var(--connect-icon-primary);

  /* D6, 30 Aug: 52px -> 58px, one token so .side.side--collapsed's own
     width and .shell's grid-template-columns (the ONE contract that owns
     whether .main follows the rail) always move together. 10% of 52 is
     57.2; 58 was chosen over rounding down because it gives even 19px
     gutters around a 20px icon (58 - 20 = 38, halved) rather than an odd
     split, and because it lets --control-size's own 44px coarse-pointer
     variant fit inside the rail's content box (58 - 7 - 7 padding = 44)
     for the first time - at 52 it overflowed by 6px. */
  --connect-rail-collapsed-width: 58px;

  /* Brand mark. The travelSTYLE CONNECT wordmark is identity, not an accent -
   * it must not follow a theme or an accent change. Gold on the navy sidebar
   * measures 5.69:1, and the sidebar is identical in both themes, so one value
   * is correct everywhere. */
  --connect-brand-mark: var(--_gold);
  /* THE NAV'S ACTIVE MARK IS AN ACCENT, NOT THE WORDMARK, and the difference
     is why this is a second role rather than a theme on the first.
     connect-brand-mark is recorded as deliberately theme-invariant - "the
     wordmark is identity, not an accent, it must not follow a theme" - and
     that decision is correct and untouched. The nav's active state is not
     identity: it says WHERE YOU ARE, it changes as you move, and on a light
     ground it has to be legible rather than on-brand. So it follows the theme
     and the wordmark does not. */
  --connect-nav-active-mark: var(--_gold-ink);

  /* ---------------------------------------------------------------
   * 4. STRUCTURE
   * ------------------------------------------------------------- */
  --connect-border: var(--_hairline);           /* decorative dividers */
  --connect-border-strong: var(--_steel);    /* inputs and control edges; 3.42:1 */
  /* THE SCROLL THUMB, taken off the surface it sits on rather than left to the
     browser. A default thumb is painted by the platform in the platform's own
     theme, so a dark card got a light scrollbar and the one loud thing in the
     tile was its scrollbar. Deliberately quiet at rest and stronger on hover:
     the resting state is below the 3:1 non-text floor BY REQUEST - discreet was
     the ask - and the interactive state is above it. Scrolling never depends on
     seeing it; wheel, trackpad, touch and keyboard all reach the same rows. */
  --connect-scroll-thumb: #b3bcc8;                 /* 1.92:1 on #ffffff */
  --connect-scroll-thumb-hover: var(--_steel);     /* 3.63:1 on #ffffff */

  /* The edge of a SURFACE, as distinct from a divider drawn across one.
     They shared --connect-border, so they were one decision by accident: a
     card's outline and a rule between two rows had no way to differ, and
     changing either changed both. Same value today - that is the point. It is
     now a choice the file records rather than a coincidence, and either can
     move without dragging the other. */
  --surface-edge: var(--_hairline);
  --connect-elevation: 0 1px 2px rgba(31, 36, 48, .06), 0 8px 24px rgba(31, 36, 48, .06);
  /* §5 follow-up: the focus ring and info hue ARE the brand/accent hue, so they
     track the person's chosen accent (var(--connect-accent-text)) rather than a
     fixed teal - otherwise a table link stayed teal under a blue accent
     (Neerav). Same resolved value at the default, so no default change. */
  --connect-focus-ring: var(--connect-accent-text);  /* 9.18:1 on ivory, non-text floor is 3.0 */

  /* ---------------------------------------------------------------
   * 5. STATUS
   *
   * Two distinct roles, deliberately separated:
   *   *-fill   the pill background. Identical in both themes.
   *   *-text   a text-safe value for the rare places a status colour has
   *            to be a word rather than a badge.
   * ------------------------------------------------------------- */
  --connect-status-success-fill: var(--_green-fill);
  --connect-status-warning-fill: var(--_yellow-fill);
  --connect-status-danger-fill: var(--_red);
  --connect-status-danger-on: var(--_white);  /* the only fill dark enough for white */

  /* PHASE 0, 2 September: SUCCESS DROPS ITS OWN HUE.
   * It renders as the same two-tone teal every neutral control wears - the
   * quiet edge of --control-border around the stronger ink of --control-icon
   * - so "done" is carried by the checkmark SHAPE rather than by a second
   * colour competing with the brand. Six role tokens moved, in all three
   * theme blocks: the ink, the soft ground, the positive icon and its edge,
   * and the success pill's ink and edge.
   *
   * TWO THINGS DELIBERATELY LEFT GREEN, neither an oversight:
   *
   *   --connect-status-success-fill is a saturated status GROUND, not a
   *   two-tone control. It is guarded as identical in both themes and its
   *   label contrast is tuned to it, so it is a separate decision.
   *
   *   --connect-chart-green is CATEGORICAL - it means "Task" beside blue,
   *   amber and red in the workload donut, not "success". Recolouring it
   *   would cost the chart its distinctness and say nothing about status.
   *
   * Note the dark pairing: success takes --control-icon (#6fcfc4), which is
   * NOT --connect-accent (#118075) there. The brief names the neutral
   * control, and the control's ink is what a positive glyph should match. */
  /* D1 (round 6): SUCCESS IS SEMANTIC AND NEVER FOLLOWS THE ACCENT. "This
     worked" must look the same to everyone, or a person on Mono gets grey
     confirmations and a person on Blue cannot tell "saved" from "selected".
     This was var(--_teal-brand) - the BRAND hue - which is why every success
     message went on rendering teal after the accent moved to blue.
     5.03 as text on ivory, 5.34 on white, 4.70 on its own tint. */
  --connect-success: var(--_green);
  --connect-success-soft: var(--_green-soft);
  /* #ffd028 IN BOTH THEMES. Neerav's decision, 5 September 2026, taken with
     the contrast in front of him and knowingly accepting it. This was
     var(--_amber) #8a5a00. The value now matches --connect-status-away,
     --connect-status-warning-fill and --_yellow-fill, which were already
     #ffd028 in both themes; one yellow, not three.

     MEASURED, AND FAILING THE TEXT FLOOR ON PURPOSE:
       #ffd028 on #ffffff  1.47:1   (was 3.46:1)   floor is 4.5:1
       #ffd028 on #fff1c9  1.31:1   (was 5.27:1)   warning-soft ground
       #ffd028 on #1e2230 10.78:1                  dark, unaffected

     The second line is not in the decision record and is the worst of the
     three: two of the five text consumers - the status-hold pill and the
     OneDrive connection note - paint warning text ON warning-soft, and that
     pairing lands at 1.31:1. Recorded in DEFERRED-DEBT so it is traceable
     when the value is revisited, which Neerav intends to do as a single
     token change. */
  /* CHAT 12 ITEM 11(b) - THE REVISIT THE RECORD ABOVE SAID WAS COMING.
     
     The 5 September decision put #ffd028 here knowingly, and the paragraph
     above records the debt it created and that Neerav intended to revisit it
     "as a single token change". This is that change, asked for in his own
     overnight queue, and it moves ONE token: light-theme warning TEXT.
     
     #8a5a00 is not a new colour - it is the value that was here before 5
     September, so the estate is returning to an amber it already knew rather
     than inventing one.
     
       #8a5a00 on #ffffff  5.93:1   (was 1.47:1)   floor 4.5:1
       #8a5a00 on #faf8f3  5.58:1   (was 1.38:1)   the ivory canvas
       #8a5a00 on #fff1c9  5.27:1   (was 1.31:1)   warning-soft, the worst
                                                   pairing the record names
     
     DARK IS UNTOUCHED at #ffd028 and 10.78:1, which was never the problem.
     --_yellow-fill keeps its value, so the status dot, the chart amber and
     --connect-status-away are unchanged: this is the text token only, and the
     "one yellow, not three" intent survives everywhere it was about fills. */
  --connect-warning: #8a5a00;
  --connect-warning-soft: var(--_amber-soft);
  --connect-danger: var(--_red);           /* 6.54:1 on white */
  --connect-danger-soft: var(--_red-soft);
  --connect-danger-border: var(--_red-border);
  --connect-danger-deep: var(--_red);
  --connect-danger-deep-soft: var(--_red-soft);
  /* MIGRATED TO NAVY. When the light accent moved from teal to navy this one
     did not come with it, so links and informational text stayed teal on a
     page whose every other accent was navy - two accents in a system whose
     first colour rule is that there is one. It was missed because nothing
     references --connect-info by that name on a page: nine of its fifteen
     uses arrive through the --link-blue alias, and a rename that follows the
     token does not follow the alias. */
  --connect-info: var(--connect-accent-text);

  /*
   * NOTE on --connect-warning. Yellow is intrinsically light: the darkest yellow
   * that carries white text, or that reads as text on white, is olive-brown.
   * There is no yellow that is both vivid and text-safe. The four remaining
   * places that use this as a word should become pills, after which this token
   * can be retired. Until then it is text-only and never a surface.
   */

  /* ---------------------------------------------------------------
   * 6. ICON ROLES - colour carries meaning, never decoration.
   * ------------------------------------------------------------- */
  /* The four comments that sat here described tokens deleted in !268 for
     having no references. The COMMENTS were left behind, describing nothing -
     a role vocabulary with no roles in it. Restored as real tokens, because
     the vocabulary is the useful part: it is what lets a new icon be given a
     meaning rather than a colour picked to match a neighbour. */
  /* ── THE THREE NEUTRAL TIERS (round 7 §3.2) ──────────────────────────────
     Icons are neutral furniture in three weights, and the accent has exactly
     three jobs: the current item in main-body navigation, the primary action
     in a set, and links. Most icons are no accent colour at all.

     Hover raises an icon ONE tier (decorative -> standard -> primary) and
     never changes hue. Focus keeps the accent ring.

     These are TIERS OF EMPHASIS, deliberately separate from the semantic icon
     roles below (positive/negative/quiet), which say what a thing MEANS. A
     tier says how loudly it speaks; a role says what it is. An icon takes one
     of each, never a tier in place of a role - a success tick is green because
     it means done, whatever its emphasis. */
  --connect-icon-primary: #1E2230;      /* top-bar actions; the single action chip in a row */
  --connect-icon-standard: #5F6875;     /* row actions where several compete; widget titles */
  --connect-icon-decorative: #818B9B;   /* drag grips, resize corners, the magnifier, chip edges;
                                          #8791A0 measured 2.87 on surface-muted */

  --connect-icon-neutral: var(--_slate);          /* neutral action */
  /* E2: success is ONE colour. A tick that means "done" and text that means
     "done" are the same semantic, so splitting them across two hues would be an
     accident rather than a treatment (directive 13).
     NOTE, reported not hidden: nothing currently reads this token - the tick
     controls take --connect-success directly - so moving it changes no pixel
     today. It is moved anyway so that whoever next reaches for "positive" gets
     the success green rather than a brand colour left pointing the wrong way. */
  --connect-icon-positive: var(--_green);        /* answered, heard, done */
  /* 1c, 2 September: DANGER GETS THE EDGE ITS OPPOSITE ALREADY HAD.
   * --connect-icon-edge-positive has existed since the icon tier was built;
   * there was no negative counterpart, so a danger control had an ink and no
   * quiet edge to pair it with while every neutral and positive one did.
   * That asymmetry is what "complete the danger token at two lightnesses"
   * meant - the two lightnesses are the INK and the EDGE, per theme, not two
   * shades of the same job.
   *
   * The weights are matched to the neutral control's edge on the SAME ground
   * rather than picked by eye: 3.77 against its 3.75 on the white card, 3.29
   * against its 3.29 on the dark one. Both clear the 3:1 non-text floor.
   *
   * NOTE WHICH ONE IS LIGHTER - it flips with the ground, exactly as the
   * design record requires. On the white card the edge (#E1544B) is lighter
   * than the ink (#b3261e); on the dark card the edge (#DC281A) is darker
   * than the ink (#f08a82). The icon is always the strong tone. */
  --connect-icon-negative: var(--_red);           /* missed, waiting, destructive */
  --connect-icon-edge-negative: var(--_red-edge);
  --connect-icon-quiet: var(--_steel);            /* inactive, none waiting */
  --connect-icon-edge: var(--_steel);
  /* The green pill edge rather than --_green-edge: measured on the real ground,
     --_green-edge is 1.87:1 and fails the 3:1 non-text floor, while this is
     3.67:1 on white and 3.46 on ivory - the same standing the teal it replaces
     had (3.75 / 3.53). */
  --connect-icon-edge-positive: var(--_green-pill-edge);

  /* ---------------------------------------------------------------
   * 7. LEGACY ALIASES - original names kept pointing at the roles above,
   * so existing rules follow the new palette without being rewritten.
   * ------------------------------------------------------------- */
  --bg: var(--connect-canvas);
  --card: var(--connect-surface);
  --surface: var(--connect-surface);
  --surface-action: var(--connect-surface-action);
  --nav: var(--connect-nav-surface);
  --nav2: var(--connect-nav-surface-raised);
  /* --gold and --link-blue are DELETED, not renamed.
     Both outlived the colours they were named for: --gold resolved to a teal
     and --link-blue to #6fcfc4, a teal called blue. A name that lies is worse
     than no name, because the next reader trusts it - and --gold was the sole
     cause of the one inconsistent control on /contacts, holding #0F766E at
     2.89:1, the exact value replaced this morning for being below the floor.
     46 references now point at the accent role and 7 at the info role -
     the roles they actually meant all along. */
  --ink: var(--connect-text);
  --muted: var(--connect-text-muted);
  --line: var(--connect-border);
  --border: var(--connect-border);
  --shadow: var(--connect-elevation);
  --green: var(--connect-success);
  --green-soft: var(--connect-success-soft);
  --amber: var(--connect-warning);
  --amber-soft: var(--connect-warning-soft);
  --danger: var(--connect-danger);
  --danger-soft: var(--connect-danger-soft);
  --red: var(--connect-danger-deep);
  --red-soft: var(--connect-danger-deep-soft);

  /* ─────────────────────────────────────────────────────────────────────────
     ROUND 41 J — THE ICON AND CONTROL PACK'S TOKEN NAMES, BRIDGED.

     The pack is a self-contained system: its controls.css opens with its own
     :root carrying literal hex for --sur-0/1/2, --ink, --line, --success and
     the rest. Installing that file whole would overwrite this estate's palette
     with the pack's, and re-theme every page in the product - including --ink
     and --line, which this block has aliased since round 18.

     So the NAMES are bridged and the COLOURS stay the estate's. The pack's
     classes then resolve through the theme that is already here and follow
     data-theme in both directions, which importing its :root would have
     broken.

     --active is the pack's "could not proceed here, go elsewhere" blue, which
     is this estate's info role; --neutral is its informational grey, which is
     the muted text role. Every other pairing is the same word twice. */
  --success: var(--connect-success);
  --warning: var(--connect-warning);
  --active: var(--connect-info);
  --neutral: var(--connect-text-muted);
  --tint: 14%;

  /* ─────────────────────────────────────────────────────────────────────────
     ROUND 18 A2c — THE CONTROL HEIGHTS, AND WHAT EACH ONE IS FOR.

     Six heights were measured across sixteen module pages before this item.
     There are four, and every control takes its height from one of them:

       30px  a pill or a chip - a piece of STATE, not something you press
       32px  anything you press: a button, an icon button, a menu trigger
       38px  anything you type or choose in: an input, a select, a textarea
       44px  all of the above, where the pointer is coarse or the viewport is
             below the shell's 767px breakpoint

     WHAT THE OTHER MEASURED HEIGHTS TURNED OUT TO BE, because A2c asks for the
     cause and "accident" was not it:

       17px  an <a> inside a SENTENCE - the "Change" links on the settings
             overview, and frankfurter.dev inside a paragraph on Currency &
             margin. Its height is its line box. An inline link in prose is not
             a control and giving it a control height would put a 32px box in
             the middle of a paragraph.
       20px  a.connect-jump-link, which round 17 deliberately made quiet
             underlined text rather than a chip, and native checkboxes and
             radios, which the coarse-pointer floor already lifts.
       19px  the ONE genuine defect: .connect-privacy-toggle shrank its glyph
             to 19px inside a 38px box. Retired by A2a - one glyph size.

     NAVIGATION ITEMS ARE NOT IN THE SET, and that is a decision rather than an
     omission. Measured after this item, at 1440: module tabs 42px, settings
     rail items 35px, dashboard widget rows 34px. Each takes its height from its
     own padding and line box, because a tab is not a button - forcing a tab
     onto the button height would either crop its underline or pad the row out
     to nothing anybody asked for. What they must not do is disagree with
     THEMSELVES, and each of the three is one number wherever it appears.
     ───────────────────────────────────────────────────────────────────────── */
  --connect-button-height: 32px;       /* §3: 32px is the in-app default for action buttons */
  /* 5C, 9 September: ONE CONTROL HEIGHT, AND THIS IS IT.
     Fields were never brought to the button height. Measured across 447
     controls on thirteen pages: 189 render 38 (every text input, select, date
     and number field) and 135 render 32 (every button and action link), so on
     every form in the product a button sat 6px shorter than the input beside it
     and their centres never aligned. The 32 is the value that stays - twelve
     controls on one page and four on another already render it, and §3 chose
     it - so the field height comes to meet it rather than the other way round.
     A 14px/16.8 line in a 32px box leaves 6.6px of vertical room, which is not
     tight: the compact control has been 30 since it was written.
     The coarse-pointer block below still raises BOTH to 44, so nothing about
     touch targets changes. */
  /* The button's own horizontal padding. Off the 4px scale on purpose - 14 is
     what every action control in the product has used since §3 - and named
     here so the rules that need it stop spelling it out. */
  --connect-button-padding-x: 14px;
  --connect-control-height: 32px;      /* 5C: fields and buttons are one height now */
  --connect-control-radius: 8px;
  /* THE SPACE BETWEEN TWO CONTROLS IN A ROW. ONE VALUE, AND ONE WRITER.

     Round 47 §4.1 measured eight rows: 4px on /contacts page actions (from a
     whitespace text node, not a rule), 8px on /contacts, /finance and
     /settings, 10px on /calls, /dashboard and the accent swatches. Three
     values and three mechanisms - a gap, a margin, and nothing at all - and
     every row reads from this now.

     8px, because it is the majority and the tighter of the two real values. A
     row of controls is one object; 10px started to read as two.

     ROUND 48: AND IT IS DECLARED ONCE. Round 47 wrote this token a SECOND time
     two hundred lines further down, with the paragraph above attached to it.
     Same value, so nothing rendered differently and nothing failed - and one
     token with two writers is the fault I had corrected in two other places
     that same round. The guard I wrote for it matched the duplicate as happily
     as the original, because it asked whether the token existed, not how often.

     A CONTEXT NEEDING DIFFERENT SEPARATION GETS ITS OWN NAMED TOKEN, never an
     unexplained literal - see --connect-topbar-gap below. */
  --connect-control-gap: 8px;

  /* 5D: THE FIELD WIDTH SCALE. Measured first: 14 distinct fr multipliers were
     in use across the product's grids - 1fr in 186 places and thirteen one-off
     values in 42 more (2, 1.55, 1.3, 1.2, 1.1, .9, .8, .75, .7, .65, .62, .6,
     .4). None of them was derived from anything; each was tuned by eye against
     one page at one width.

     Three rungs, keyed on WHAT THE FIELD HOLDS, not on how it looked that day:

       narrow  a bounded number - a quantity, a day count, a percentage
       field   the default - a date, a money amount, a code, a select
       wide    free text carrying a name, a description, a label

     A field that matters more takes MORE TRACKS, not a bigger fraction: span
     is importance, width is content class. That keeps the two decisions
     separable, which a hand-tuned 1.55fr does not.

     Same rule as the height scale above and the date presets in
     DateTimeFormatter: a named scale, never a free zoom. */
  --connect-track-narrow: .6fr;
  --connect-track-field: 1fr;
  --connect-track-wide: 2fr;

  /* ROUND 25 Q6: THE REGION SPLIT SCALE, DERIVED FROM WHAT WAS THERE.
     Eight declarations sat off the field scale. Their main:aside ratios,
     measured before anything was proposed:

       1.5    x3   1.2fr .8fr                      three pages
       2.067  x2   minmax(0,1.55fr) minmax(300px,.75fr)
       2.0    x1   .connect-two-thirds
       0.818  x1   an INVERSION - the aside is wider
       n/a    x1   a three-column comparison row, not a split at all

     They cluster, and tightly: three at exactly 1.5, and three within 3% of
     2.0 - which is 2.0 with 1.55/.75 tuned by eye beside it. Two rungs, both
     read off that measurement rather than chosen:

       3:2  the aside is a companion       (was 1.2/.8, identical)
       2:1  the aside is a sidebar          (was 1.55/.75, 0.7% narrower main)

     A SPLIT IS NOT A FIELD WIDTH and these are not the .6/1/2 rungs. A field
     width says what a box HOLDS; a split says how a page divides. The values
     coincide at 2fr and the questions do not, so they are named separately -
     the merge comparison row uses the FIELD tokens and looks identical.

     The inversion is left hand-set and named: one page, one occurrence, and
     an aside wider than its main is a different intent that two rungs derived
     from six main-dominant splits have no authority over. */
  --connect-split-3-2-major: 3fr;
  --connect-split-3-2-minor: 2fr;
  --connect-split-2-1-major: 2fr;
  --connect-split-2-1-minor: 1fr;
  /* ROUND 19 B3b. 32px, matching the buttons and icon buttons, which is what
     Neerav asked for: "Pills should have the new height like icons and
     buttons." 30 was not on the 4px scale either. See B3d in the close-out for
     the measured consequence at an 11px label. */
  /* ROUND 45 §3. 24, NOT 32. The pack's pill is a label, not a control, and a
     32px box made it read as one - a status sitting in a table cell at the
     same height as the button beside it claims the same weight. The pack's own
     spec records that the 5px/4px asymmetric padding "was an accident of the
     32 box; removed", which is the same finding from the other end: the box
     was wrong and the padding was compensating for it. */
  /* ROUND 48 §3.1 - THE TOPBAR IS DENSER, AND THAT IS A DECISION.

     Round 47 §4.4 permits a second value as a SECOND NAMED TOKEN with a stated
     reason. The topbar cluster has been 4px since round 18 and the reason was
     written beside the rule but never given a name: the bar's own 16px is the
     gap BETWEEN groups (brand | trail | controls), and inside a group it left
     the icon buttons 50px apart. One gap cannot do both jobs, which is why
     that container exists at all.

     So it keeps 4px and stops being an anonymous literal. A row of icon-only
     controls in chrome is not a row of labelled controls in page content -
     there is no text between them, so the same 8px reads as a wider space. */
  --connect-topbar-gap: 4px;
  --connect-pill-height: 24px;
  --connect-pill-padding-x: 10px;
  --connect-pill-radius: 999px;
  --connect-button-radius: 8px;
  /* Deliberately compact controls (the sidebar theme switch). Small on a mouse,
     full touch target on a finger - the whole reason this is a token and not a
     literal is that the coarse-pointer block below can reach it. */
  --connect-control-height-compact: 30px;
  --connect-button-font-size: 14px;
  /* ROUND 47 §1.2 - --connect-button-text IS GONE, NOT RENAMED.

     It held var(--connect-accent-text) and had exactly ONE reader in the whole
     estate: the non-primary control default in connect-overrides.css. Its name
     said "the colour of text on a button" and its value said "the accent",
     which is how every secondary control in CONNECT came to be blue.

     §1.2 offered renaming it if some reader genuinely needed the accent. None
     does. `.primary` never read it - it takes --connect-accent-on for white on
     the fill - so after §1.1 the token had no readers at all. A token nobody
     reads is a name people write new rules against. */
  --radius: 10px;

  /* D1, 30 Aug: ONE SHARED POPOVER TOKEN. The nav flyouts carried 6px
     interior padding against a 9px corner radius - INTERIOR PADDING EXCEEDS
     THE CORNER RADIUS crowds the curve, content runs into the rounded
     corner instead of clearing it. The avatar panel already had this right
     (14px padding, 12px radius - padding exceeds radius, clears every
     corner). Named here once so every pop-out surface - flyouts, the
     avatar panel, the notification stack - reads from the same pair
     instead of three separately-tuned ones that can drift apart again. */
  --connect-popover-padding: 14px;
  --connect-popover-radius: 12px;

  /* F1, 30 Aug: THE PERSISTENT TOP BAR'S HEIGHT. The bar is not new - it is
     .mobile-bar (68px, below 850px only, containing nothing but the wordmark)
     promoted to every width and given a job. One token because four separate
     rules have to agree on it: the bar's own height, body's padding-top (the
     bar is fixed, so nothing else pushes content clear of it), the rail's
     sticky top offset, and .shell's min-height. A literal in any one of them
     is a fifth place for the bar's height to be wrong.
     64/56 rather than the old 68: the bar now carries controls sized by
     --control-size (38px) rather than a two-line wordmark, and 64 clears that
     with 13px above and below. */
  --connect-topbar-height: 64px;

  /* H1, 31 Aug: CHROME INK - a token whose SCOPE is part of its definition.
     The top bar and the sidebar are the two chrome surfaces, they share ONE
     icon colour, and nothing else in the product uses it. That scope is the
     whole point of naming it: the moment a third surface reads it, it has
     stopped being chrome ink and the name is a lie.

     THE CONSEQUENCE IS THE POINT: TEAL BECOMES A DATA COLOUR. The bar's icon
     controls took --control-icon (the teal brand) because they used the
     shared icon primitive unmodified, so one hue was doing two jobs - chrome
     and data - which is the actual reason the bar icons and the donut looked
     mismatched. Chrome wears ink; teal belongs to charts and accents.

     Rest is the rail's own muted ink, so the bar's controls and the rail's
     nav rows are already the same colour rather than two greys. Hover and
     active go to full-strength on-fill. The hover GROUND is the raised nav
     surface - the same one .connect-avatar-trigger already hovers to - so
     the product has one rule for icon-only controls on chrome, not two. */
  /* C1: the search pill sits on chrome, which is the SAME dark in both themes,
     so its ink cannot come from --connect-text - that flips and the pill's
     ground does not. These are the light theme's own ink values, pinned. */
  --connect-search-ink: #1F2430;
  --connect-search-ink-muted: #5F6875;
  /* D6: THE CHROME GROUND IS NOT THE TIER SCALE'S GROUND, AND THAT IS WHY
     THESE TWO STAY OUT OF IT.

     The three neutral icon tiers (round 7 §3.2) are measured against surface,
     surface-muted and overlay - grounds that change with the theme. The two
     persistent global controls, Call note and Alerts, do not sit on any of
     them: they sit on the top bar, whose ground is #1E2230 in BOTH themes.
     Applying a scale outside the domain it was measured in is how the teal
     tokens survived an accent change, so these keep their own role.

     Measured on the real ground: the served glyph is #C8CEDA on #1E2230 =
     10.02:1, in both themes. Nothing to change. For comparison, forcing them
     onto a tier would give 7.75 at standard - too quiet for two persistent
     globals - or 12.81 at primary, which shouts.

     The name was already honest, so it is kept rather than churned: this is
     the ink for CHROME, and it is not icon-only - hence not
     --connect-icon-chrome, which would be narrower than its two consumers. */
  --connect-chrome-ink: var(--connect-nav-text-muted);
  --connect-chrome-ink-active: var(--connect-on-fill);
  --connect-chrome-hover-ground: var(--connect-nav-surface-raised);

  /* H4, 31 Aug: THE BRAND LINE. Gold at the very top, teal at the foot, and
     it decorates neither the bar nor the rail - it runs the full page height
     past both as one unbroken edge, which is the whole idea: a single mark
     that says the two chrome surfaces are one object.
     The midpoint is the bridge hue between the two ends; named here so the
     gradient is three tokens rather than three literals. */
  --connect-brand-line-width: 3px;
  --connect-brand-line-top: var(--_gold);
  --connect-brand-line-mid: var(--_brand-line-bridge);
  --connect-brand-line-foot: var(--connect-accent);

  /* H5, 31 Aug: THE RAIL SURFACE FADE.
     Three things this has to get right, each of which was a defect first:
       - Its top value must EQUAL the bar's background EXACTLY. The rail used
         to start lighter than the bar, and that difference at the seam is
         what read as a cut-off edge rather than one surface.
       - It needs a FLAT RUN TO DEPART FROM. A ramp starting at 0% across the
         whole rail does not read as a fade at all; it reads as a panel that
         is slightly the wrong colour everywhere. Hence 60% flat, then ease.
       - MORE THAN TWO STOPS. A two-stop ramp falls linearly and looks
         mechanical, so the lift is 22/55/88 across 76/89/100 - an ease-in
         curve, not a straight line.
     Derived with color-mix from the two nav surface roles, so the ramp
     cannot go stale against a token change and carries no literals.

     ONE RAMP SERVES BOTH THEMES, and that is not an inversion dodged - the
     navy navigation surface is IDENTICAL in light and dark by express
     decision (see the colour rules at the head of this file), so both ends
     of this ramp are theme-invariant already. A second set of light stops
     would be a copy of these values under another name, which is the
     duplication this file exists to prevent. If the chrome surface ever does
     become theme-dependent, this is the block that needs a light twin. */
  /* THE CHART PALETTE - FOUR SERIES SLOTS, ONE SET OF VALUES, BOTH THEMES.
     PR A, 31 August. Replaces --connect-chart-profit / --connect-chart-supplier
     (a dark pair and a separate light pair) and the four SEMANTIC ROLE tokens
     the workload donut was borrowing through a PHP array - three writers of a
     chart colour, now one.

     WHY ONE SET FOR BOTH THEMES, reversing what the retired block argued. That
     block's case was that the ground changes so the marks must. These four
     clear their floors on BOTH grounds - measured against the dark card
     #1E2230 and the light card #FFFFFF - so a second set would be two names
     for one decision, which is the duplication this file exists to prevent.
     They are named in ConnectDesignTokenTest's DELIBERATELY_THEME_INVARIANT
     with this reason; a role that is theme-invariant BY ACCIDENT is exactly
     what that guard exists to catch, so being invariant on purpose has to be
     written down where the guard reads it.

     FOUR CONSTRAINTS SHIP WITH THESE VALUES. They are conditions, not notes.

     1. THE GREEN IS DELIBERATELY A SHADE WARM. A purer green measures ΔE 5.5
        against this red under deuteranopia - the textbook red-green failure.
        #03A970 clears the 8.0 floor at 8.2. Do not "correct" it toward a
        purer green.
     2. RED AND AMBER MUST NEVER BE THE ONLY TWO SERIES IN ONE CHART (ΔE 5.0
        deutan on white). Under the assignment below they never are: they
        co-occur only in the four-series workload donut, where every slice is
        direct-labelled with its name and its count. That condition stops
        being true if that donut is ever reduced to two series or shipped
        with bare swatches.
     3. AMBER IS 2.25:1 ON THE WHITE CARD, below 1.4.11's 3:1. The relief the
        standard allows is that colour is never the only carrier: EVERY donut
        legend prints the series name AND its value. THE LEGEND IS
        LOAD-BEARING - a donut that ships without labelled values fails
        1.4.11, including the masked state, where the name still prints.
     4. ONE DECLARED BAND DEVIATION, dark only: amber sits at lightness 0.751
        against the 0.67 ceiling. A vivid amber cannot be dark, and pulling it
        into band costs the vibrancy and weakens it against the green. Taken
        deliberately, for one slot. There is no lightness-band guard in this
        suite, so this comment is the only thing holding it.

     Six-check validator against #1E2230 and #FFFFFF: green+red pass all six
     (deutan 8.2, normal 33.0, both >= 3:1); blue+amber protan 31.1, normal
     34.5, blue >= 3:1 on both grounds.

     ASSIGNMENT, which is also ring order clockwise from 12 o'clock:
       outstanding workload  1 service case blue, 2 task green,
                             3 refund follow-up amber, 4 ticket to issue red
       sales and profit      1 gross profit blue, 2 supplier cost amber
     Lateness, when it lands, is a WORD and a WEIGHT in the legend row, never
     a colour change - red is a category here and --connect-danger stays
     reserved for a genuine alert. Two reds can therefore appear on one
     dashboard. That is deliberate: collapsing them is how the danger step got
     spent on a category in the first place. */
  /* K3, 1 September. PRESENCE, ONE SET OF VALUES IN BOTH THEMES.
     A status ring reports the same fact whichever theme is on, and a person
     who learns "green means available" in dark must not have to relearn it in
     light. Same decision as the chart palette, recorded the same way in
     ConnectDesignTokenTest's invariant list rather than left to look like an
     omission. OFFLINE IS NOT HERE: it takes the existing neutral border token
     per theme, because absence of presence is not a presence colour. */
  --connect-status-online: #5fd08a;
  --connect-status-away: #ffd028;
  --connect-status-busy: #f08a82;
  /* ROUND 7 Q1: THE CATEGORICAL PALETTE MEETS THE 3.0 GRAPHICAL FLOOR.
     Six values were below it, not the two the brief named - measured against
     every ground a chart actually draws on (surface, surface-muted, overlay),
     in both themes:

       light   green   2.73 on surface-muted
               amber   2.25 / 2.02 / 2.25   - all three
       dark    red     2.86 on overlay
               magenta 2.91 / 2.52
               indigo  2.84 / 2.45

     All corrected HUE-PRESERVING, lightness only, so the palette keeps its
     identity: light darkens toward its grounds, dark lightens toward its own.
     That is why the two themes no longer share one hex - they never should
     have, since a value that clears white cannot also clear #303545.

     SEPARATION RE-CHECKED after the move. Note that blue and indigo are NEVER
     DRAWN TOGETHER - indigo IS chart-blue under the Blue accent - so the check
     is within each variant set of five, not across six. Worst pair before and
     after is blue/magenta: 55.2 -> 55.2 light default, 42.7 -> 42.7 light
     Blue, 55.2 -> 52.1 dark default, 48.1 -> 42.6 dark Blue. Every variant
     stays far above the point where two categories could be confused. */
  --connect-chart-green: #039E69;   /* was #03A970, 2.73 on surface-muted */
  --connect-chart-red:   #E5372C;   /* 4.27 / 3.84 - already clear */
  --connect-chart-blue:  #3D86EF;   /* 3.59 / 3.22 - already clear */
  --connect-chart-amber: #ffd028;   /* was #B18216 (3.46 on white). One yellow
                                       across both themes - see --connect-warning
                                       above for the decision and the figures. */
  /* §1 (round 7): the call-note segment. Magenta at hue 321 is the widest gap
     left - 83 degrees from indigo/mono and 43 from red - so it cannot be taken
     for any of the four accents or for another slot. #B5479B clears the 3.0
     graphical floor on ALL FOUR grounds (4.84 card, 4.56 canvas, 3.27 dark
     surface, 3.70 dark canvas), which several older entries in this palette do
     not - that shortfall is reported rather than copied. */
  --connect-chart-magenta: #B5479B;

  --connect-rail-fade-1: color-mix(in srgb, var(--connect-nav-surface-raised) 22%, var(--connect-nav-surface));
  --connect-rail-fade-2: color-mix(in srgb, var(--connect-nav-surface-raised) 55%, var(--connect-nav-surface));
  --connect-rail-fade-3: color-mix(in srgb, var(--connect-nav-surface-raised) 88%, var(--connect-nav-surface));

  /* ---------------------------------------------------------------
   * 8. STATUS PILL GEOMETRY
   *
   * A pill in a GROUP takes the shared width so a set reads as a column;
   * 84px is the smallest that fits the whole status vocabulary. A pill on
   * its OWN is being read rather than compared, so it sizes to its text.
   * The cap is a backstop: a status that needs truncating has already
   * failed, which is why labels are held to 11 characters.
   * ------------------------------------------------------------- */
  --connect-pill-min-width: 92px;
  /* Uppercase, letter-spaced labels are wider than sentence case. At 140px
     three real voicemail labels were clipped - "Transcription failed" needed
     149px against a 138px content box. The clamp keeps a status column tidy,
     so it is widened rather than the label being shrunk to fit a number
     chosen before the labels were uppercase. */
  --connect-pill-max-width: 172px;

  /* The lane a status column reserves.
   *
   * The widest pill the vocabulary can produce, plus clearance. Sized against
   * the MAXIMUM and not the average, because a column sized for NEW leaves
   * HEARD touching the column beside it - which is what it did.
   *
   * The pill itself stays content-sized: uniform construction, variable width,
   * fixed lane. Padding every short status out to the longest label would make
   * a column of NEW read as a column of buttons. */
  --connect-status-lane: 196px;

  /* The audio-and-transcript lane.
   *
   * Same principle as the status lane, different content: this column holds an
   * audio control AND a transcript preview, and between 1032px and 1232px it
   * collapsed to 84px - narrow enough that its own header printed as
   * "Audio / Transcr" with the rest cut off mid-word, and the preview showed
   * about six characters.
   *
   * 260px is the audio control plus enough of a sentence to be worth reading.
   * Below the width where that fits, the table scrolls inside its card, which
   * is the ratified behaviour rather than a column quietly crushing itself. */
  --connect-audio-transcript-lane: 260px;

  /* QUEUE 22.4. THE SIDE PANEL'S LANE.
   *
   * The send-intake panel is a docked sheet, not a dialog, and its width is the
   * one number it cannot take from the spacing scale: it is a measure, chosen
   * so a form label, a 92px number box and the email preview beside them all
   * read without wrapping, and so the record behind it stays visible on a
   * laptop. Named rather than written into the rule, because the next docked
   * panel must be the same width as this one and a literal in one stylesheet
   * is not something the second one can find. */
  --connect-intake-panel-width: 460px;

  /* ── Shading means navigation ──────────────────────────────────────────
   * Ratified 24 Aug 2026 from the grouped-button decision pack, option C.
   *
   * A shaded ground marks a set of DESTINATIONS - the customer sub-tabs, and
   * anything else that is a set of places you can go. Everything that ACTS
   * (Save, Add payment, a header cluster) is quiet and sits on the card.
   *
   * The shading therefore carries information rather than decoration, and the
   * question for a new cluster has an answer - is this destinations or verbs -
   * instead of a look to match by eye. Matching by eye is how four different
   * section-box borders happened on four tabs of one record.
   *
   * WHAT THIS REPLACED: three hardcoded literals (#ddd7cc twice, #ddd8ce once)
   * in page-level <style> blocks, which no theme can reach. In dark mode they
   * drew a pale beige hairline on a near-black ground on Finance,
   * Communications and Tasks, while Overview - the one tab using the token -
   * followed the theme correctly. */
  --connect-section-edge: var(--connect-border);
  --connect-group-surface: var(--connect-surface-muted);
  --connect-group-edge: var(--connect-border);
  --connect-group-pad: 8px;
  --connect-group-radius: 12px;

  /* Status pills: outline treatment. Transparent ground, hairline border,
     coloured label. Measured against the surface a pill actually sits on -
     ivory #faf8f3 in light, #1e2230 in dark - not against the surface token,
     because the table is transparent and the pill sits on the page.
     Label owes 4.5:1, border owes 3:1. */
  --connect-pill-success-ink: var(--_green-pill);    /* 5.36:1 on white, 5.05 on ivory */
  --connect-pill-success-edge: var(--_green-pill-edge);   /* 3.67:1 on white, 3.46 on ivory */
  --connect-pill-warning-ink: var(--_amber-pill);   /* 4.62:1 - amber. Yellow cannot be
     compliant text on a light ground: #ffd028 measures 1.44:1 on ivory. This
     is the most saturated amber that clears the floor. */
  --connect-pill-warning-edge: var(--_amber-pill-edge);  /* 3.32:1 */
  --connect-pill-danger-ink: var(--_red-pill);    /* 7.09:1 */
  --connect-pill-danger-edge: var(--_red-pill-edge);   /* 4.50:1 */
  /* ── IN PROGRESS: ITS OWN FAMILY (D2, 5 Sep) ─────────────────────────────
     Both consumers of the old --connect-pill-active-* pair meant RECORD
     STATUS - "this case is In progress" - and neither meant selection. The
     name said "active", the class said "active", and neither meant "currently
     selected", which is most of why one pair looked like two things sharing.

     "In progress" is the quiet one on the status scale. Not success (it is not
     finished), not warning (nothing is wrong), not neutral grey (it is live
     work). A desaturated steel reads as motion without competing.

     ACCENT-INDEPENDENT BY CONSTRUCTION. A status pill that re-tints with a
     personal preference is the same error as a success message doing it - and
     --connect-info could not be used precisely because it resolves to
     var(--connect-accent-text).

     SEPARATION FROM BLUE, measured rather than asserted: hue 216 against the
     accent's 220 is close, so the separation is carried by SATURATION - 0.16
     against 0.84 - which is what "desaturate hard rather than picking a true
     blue" buys. CIE Lab dE against #2f6fed is 60.8 for the ink and 62.3 for
     the edge, against a just-noticeable threshold near 2.3. A user on Blue
     sees both on one row and cannot confuse them. */
  --connect-status-progress-ink: #3A3F9E;   /* ROUND 47 §8.2. 8.81:1 on white surface, 8.30:1 on ivory canvas */
  --connect-status-progress-edge: #7E82CF;  /* 3.51:1 on white, 3.31:1 on ivory; non-text floor 3.0 */
  /* ONE GREY, ONE VALUE - but still TWO ROLES.
     Muted body text and the ink inside a neutral pill are genuinely two
     roles, and collapsing them into one name would lose expressiveness for
     nothing. This is not aliasing: aliasing is one role wearing many names,
     which is what --gold and --link-blue were.
     What it was, was DRIFT. #5C6472 sat three units from #5f6875 - invisible
     on screen, so it cannot have been an optical decision, and it gave one
     grey two places to change. The role keeps its name and points at the
     other role's value. If these ever need to genuinely differ, that is one
     line, and the divergence becomes deliberate and visible. */
  --connect-pill-neutral-ink: var(--connect-text-muted);   /* 5.31:1 */
  --connect-pill-neutral-edge: var(--_steel-pill);  /* 3.59:1 */
  /* ROUND 47 §8.2 - ACTIVE IS NOT NEUTRAL AND MUST NOT LOOK LIKE IT.

     Measured: connect-pill--active rendered #7d8ca1 edge / #617086 ink -
     indistinguishable from neutral at a glance. Six "In progress" pills on
     /visa-immigration read as dormant when they mark the one state that still
     needs somebody. The cause was not the colour but the cascade: --active is
     absent from the variant list in the outline rule, whose declarations all
     carry !important, so it fell to that rule's neutral default and the later
     --active rule could never apply. It is in the list now.

     INDIGO, not the accent. The accent is a personal choice - a tenant on the
     mono palette would have a near-black "in progress" against a grey
     "closed", which is no distinction at all - and it already means "this
     control is selected". A status must mean the same thing in every tenant,
     so this is a fixed hue like the other four.

     WHAT SEPARATES IT FROM NEUTRAL IS HUE, NOT LUMINANCE, and the numbers
     below do not measure that: a contrast ratio compares brightness, and
     active against neutral is 1.56:1 light and 1.24:1 dark. Saturated indigo
     beside desaturated steel is the same kind of distinction green and red
     already rely on. The ratios below are each variant against its own
     ground, which is what the floors are for.

     THE VALUES LIVE ON --connect-status-progress-*, BELOW, WHICH IS WHAT THE
     WINNING RULE READS. I first added a parallel --connect-pill-active-* pair
     and a rule to apply it; the pill still rendered grey, because a later
     !important rule was already painting it from the progress tokens. A second
     name for one fact is how this file got four writers for a stroke width.
     The duplicate is gone and the existing tokens carry the new colour. */

  /* ---------------------------------------------------------------
   * 9. TYPOGRAPHY
   * ------------------------------------------------------------- */
  --connect-font-body: Inter, Arial, sans-serif;

  --connect-text-2xs: 11px;
  --connect-text-xs: 12px;
  --connect-text-sm: 13px;
  --connect-text-md: 16px;
  --connect-text-lg: 19px;
  --connect-text-2xl: 30px;




  /* ---------------------------------------------------------------
   * 10. SPACING AND RADIUS - 4px base.
   * ------------------------------------------------------------- */

  --connect-radius-control: 8px;
  --connect-radius-container: 12px;
  --connect-radius-pill: 999px;
}

/* ===================================================================
 * DARK THEME
 *
 * Declared twice on purpose - see the header. Only the values change;
 * every name above still resolves, so nothing downstream needs to know
 * which theme is active.
 *
 * The values are not an inversion. A colour needs more chroma on a dark
 * ground to read with the same intensity, so the accent lightens to
 * #6fcfc4 for text while the button fill stays #118075, and the muted
 * text lightens rather than simply flipping.
 * =================================================================== */

@media (prefers-color-scheme: dark) {

  :root:not([data-theme="light"]) {
    /* Dark ground: the gold is legible as drawn, so the mark is the brand gold. */
    --connect-nav-active-mark: var(--_gold);
  /* 7b. THE QR CODE'S GROUND, and the one colour in CONNECT that is
     deliberately the same in every theme. A scanner reads contrast, and a QR
     inverted for dark mode is a QR that will not scan - so this is declared
     three times with one value rather than left as a literal, which is what
     the token layer is for. */
  --connect-qr-ground: #ffffff;
  --connect-qr-ink: #0b0f19;
    color-scheme: dark;

    /* H11's chart pair, and I5's lesson about where it goes.
       These two lines were originally emitted at the TOP LEVEL of this
       @media block, outside any selector. Bare declarations are invalid
       there, and CSS error recovery does not warn - it discards until it
       resynchronises, which took the whole of this :root rule with it. The
       dark palette became unreachable for everyone on system-dark, so Auto
       rendered light for every member of staff who had never touched the
       theme control. Exactly the failure connect-overrides.css shipped
       twice before: a parse error silently deletes the rules after it. */
    /* THE CHART PALETTE IS ONE SET OF VALUES IN BOTH THEMES - see the block
       in :root. Repeated here rather than left to inherit so the pair is
       visible in the dark scope and parity is by construction, not by
       absence. */
  /* K3, 1 September. PRESENCE, ONE SET OF VALUES IN BOTH THEMES.
     A status ring reports the same fact whichever theme is on, and a person
     who learns "green means available" in dark must not have to relearn it in
     light. Same decision as the chart palette, recorded the same way in
     ConnectDesignTokenTest's invariant list rather than left to look like an
     omission. OFFLINE IS NOT HERE: it takes the existing neutral border token
     per theme, because absence of presence is not a presence colour. */
  --connect-status-online: #5fd08a;
  --connect-status-away: #ffd028;
  --connect-status-busy: #f08a82;
    --connect-chart-green: #03A970;   /* 5.21 / 4.64 / 4.02 - clear on dark */
    --connect-chart-red:   #E7453B;   /* was #E5372C, 2.86 on overlay */
    --connect-chart-blue:  #3D86EF;   /* 4.41 / 3.93 / 3.40 - clear */
    --connect-chart-amber: #ffd028;   /* 10.78 on #1e2230. Same yellow as light. */
    /* §1 (round 7): the call-note segment. Magenta at hue 321 is the widest gap
       left - 83 degrees from indigo/mono and 43 from red - so it cannot be taken
       for any of the four accents or for another slot. #B5479B clears the 3.0
       graphical floor on ALL FOUR grounds (4.84 card, 4.56 canvas, 3.27 dark
       surface, 3.70 dark canvas), which several older entries in this palette do
       not - that shortfall is reported rather than copied. */
    --connect-chart-magenta: #BF5CA7;   /* was #B5479B, 2.52 on overlay */

    /* PALETTE - PRIVATE, dark. Same contract as light: one place per value,
       roles below point here, nothing outside this file may name an
       underscore token. */
    --_d-canvas: #14171f;
    --_d-surface: #1e2230;
    --_d-surface-muted: #262b3a;
    --_d-overlay: #303545;
    --_d-text: #e9e7e2;
    --_d-text-muted: #9aa1af;
    /* #118075, not #0f766e. The accent paints a control EDGE - every
       .secondary button borders it - so it owes the same 3:1 non-text floor
       that --control-border does. #0f766e measures 2.89:1 on #1E2230, and the
       floor was applied to --control-border while the value it was copied from
       kept the failing number: one decision in two places. 3.29:1 as an edge,
       4.81:1 under white as a fill. */
    --_d-teal: #118075;
    --_d-teal-deep: #0b5c55;
    --_d-teal-light: #6fcfc4;
    --_d-teal-soft: #1a2a2c;
    --popup-close-fill: #3a4256;
    --popup-close-fill-hover: #465066;
    --popup-close-border: #4a5369;
    --popup-close-glyph: #ffffff;
    --_d-hairline: #2e3546;
    --_d-steel: #6a7488;
    --_d-green: #5fd08a;
    --_d-green-soft: #16302e;
    --_d-green-edge: #4a7a60;
    --_d-yellow: #ffd028;
    --_d-yellow-soft: #2a2418;
    --_d-red: #f08a82;
    --_d-red-soft: #2c1a19;
    --_d-red-border: #8a4a45;
    --_d-red-edge: #DC281A;    /* 3.29:1 on the dark card */
    --_d-green-pill: #6FD79B;
    --_d-green-pill-edge: #5eb185;
    --_d-yellow-pill-edge: #c9a730;
    --_d-red-pill: #F08B8B;
    --_d-red-pill-edge: #c87878;
    --_d-teal-pill: #4FD1C5;
    --_d-teal-pill-edge: #46aca4;
    --_d-neutral-pill: #9AA1AD;
    --_d-neutral-pill-edge: #828994;


    --connect-canvas: var(--_d-canvas);
    --connect-surface: var(--_d-surface);
  /* Dark mode carries a real yellow - the product's own #ffd028 - because
     luminance is no longer the constraint it is on a light ground. */
  --connect-pill-success-ink: var(--_d-green-pill);    /* 8.95:1 on surface, 10.13 on canvas */
  --connect-pill-success-edge: var(--_d-green-pill-edge);   /* 6.11:1 on surface, 6.91 on canvas */
  --connect-pill-warning-ink: var(--_d-yellow);   /* 10.78:1 */
  --connect-pill-warning-edge: var(--_d-yellow-pill-edge);  /* 6.82:1 */
  --connect-pill-danger-ink: var(--_d-red-pill);    /* 6.61:1 */
  --connect-pill-danger-edge: var(--_d-red-pill-edge);   /* 4.83:1 */
  --connect-status-progress-ink: #A9ADF7;   /* ROUND 47 §8.2, dark. 7.54:1 on surface #1e2230 */
  --connect-status-progress-edge: #6F74CC;  /* 3.80:1 on surface, non-text floor 3.0 */
  --connect-pill-neutral-ink: var(--_d-neutral-pill);   /* 6.09:1 */
  --connect-pill-neutral-edge: var(--_d-neutral-pill-edge);  /* 4.49:1 */
    --connect-surface-muted: var(--_d-surface-muted);
    --connect-surface-action: var(--_d-surface-muted);
    --connect-surface-overlay: var(--_d-overlay);

    --connect-text: var(--_d-text);
    --connect-text-muted: var(--_d-text-muted);

    /* Round 5 §5: dark now DEFAULTS to BLUE (Neerav, 4 Sep). Teal stays
       available as a chosen accent via [data-accent="teal"]. This is the
       prefers-color-scheme copy - a user on system-dark reaches this block and
       never the [data-theme] one, so both carry the same default. */
    --connect-accent: #2f6fed;
    --connect-accent-hover: #2455bb;
    --connect-trigger-ground: var(--connect-surface-muted);
    --connect-trigger-edge: var(--connect-border-strong);
--connect-accent-text: #8fb8ff;
    --connect-accent-soft: #16202e;

    --connect-border: var(--_d-hairline);
    --connect-border-strong: var(--_d-steel);
    --connect-scroll-thumb: #4b5570;                 /* 2.14:1 on #1e2230 */
    --connect-scroll-thumb-hover: var(--_d-steel);   /* 3.37:1 on #1e2230 */
    --connect-elevation: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .3);
    --connect-focus-ring: var(--connect-accent-text);

    --connect-success: var(--_d-green);
    --connect-success-soft: var(--_d-green-soft);
    --connect-warning: var(--_d-yellow);
    --connect-warning-soft: var(--_d-yellow-soft);
    --connect-danger: var(--_d-red);
    --connect-danger-soft: var(--_d-red-soft);
    --connect-danger-border: var(--_d-red-border);
    --connect-danger-deep: var(--_d-red);
    --connect-danger-deep-soft: var(--_d-red-soft);
    --connect-info: var(--connect-accent-text);

    /* The three neutral tiers, dark. Primary is --connect-text's own value:
       an icon leading its context speaks at the same weight as body copy. */
    --connect-icon-primary: #e9e7e2;
    --connect-icon-standard: #aeb6c4;
    --connect-icon-decorative: #788093;  /* #6a7284 measured 2.53 on overlay */

    --connect-icon-edge: var(--_d-steel);
    --connect-icon-edge-positive: var(--_d-green-pill-edge);

    /* THE REFERENCE CONTROL, DARK. The relationship is the decision; these are
       the values that satisfy it on a dark ground.
       Defined here because a role defined in ONE theme scope silently inherits
       the other theme's value - and this trio was light-only until Guard 0 was
       written. Consumed, that would have put a near-black icon (#0B5A55) on a
       near-black ground (#1E2230): invisible, and invisible in the theme that
       is harder to notice being wrong.

       --control-border is #118075, not the #0F766E the search button renders.
       #0F766E measures 2.89:1 against #1E2230 and the non-text floor is 3.0
       (WCAG 1.4.11). #118075 is two units along, indistinguishable by eye, and
       measures 3.29:1. The design is kept; the boundary is made legible.
       --control-icon 8.59:1. */
    --control-bg: var(--_d-surface);
    --control-border: var(--connect-icon-decorative);
    --control-icon: var(--connect-icon-primary);

    /* The new roles, dark. --control-* is deliberately NOT restated: dark was
       measured and holds, and its three relationships already resolve through
       the accent roles. Restating them would make a second place for one
       decision, which is the thing this file exists to prevent. */
    --surface-edge: var(--_d-hairline);
    --connect-icon-neutral: var(--_d-text-muted);
    --connect-icon-positive: var(--_d-green);
    --connect-icon-negative: var(--_d-red);
  --connect-icon-edge-negative: var(--_d-red-edge);
    --connect-icon-quiet: var(--_d-steel);
  }
}

:root[data-theme="dark"] {
    /* Dark ground: the gold is legible as drawn, so the mark is the brand gold. */
    --connect-nav-active-mark: var(--_gold);
  /* 7b. THE QR CODE'S GROUND, and the one colour in CONNECT that is
     deliberately the same in every theme. A scanner reads contrast, and a QR
     inverted for dark mode is a QR that will not scan - so this is declared
     three times with one value rather than left as a literal, which is what
     the token layer is for. */
  --connect-qr-ground: #ffffff;
  --connect-qr-ink: #0b0f19;

    /* The chart palette, the same four values as every other scope. */
  /* K3, 1 September. PRESENCE, ONE SET OF VALUES IN BOTH THEMES.
     A status ring reports the same fact whichever theme is on, and a person
     who learns "green means available" in dark must not have to relearn it in
     light. Same decision as the chart palette, recorded the same way in
     ConnectDesignTokenTest's invariant list rather than left to look like an
     omission. OFFLINE IS NOT HERE: it takes the existing neutral border token
     per theme, because absence of presence is not a presence colour. */
  --connect-status-online: #5fd08a;
  --connect-status-away: #ffd028;
  --connect-status-busy: #f08a82;
    --connect-chart-green: #03A970;   /* 5.21 / 4.64 / 4.02 - clear on dark */
    --connect-chart-red:   #E7453B;   /* was #E5372C, 2.86 on overlay */
    --connect-chart-blue:  #3D86EF;   /* 4.41 / 3.93 / 3.40 - clear */
    --connect-chart-amber: #ffd028;   /* 10.78 on #1e2230. Same yellow as light. */
  --connect-chart-magenta: #BF5CA7;   /* was #B5479B, 2.52 on overlay */

  color-scheme: dark;

  /* PALETTE - PRIVATE, dark. Same contract as light: one place per value,
     roles below point here, nothing outside this file may name an
     underscore token. */
  --_d-canvas: #14171f;
  --_d-surface: #1e2230;
  --_d-surface-muted: #262b3a;
  --_d-overlay: #303545;
  --_d-text: #e9e7e2;
  --_d-text-muted: #9aa1af;
  /* #118075 - see the note in the media-query copy above. */
  --_d-teal: #118075;
  --_d-teal-deep: #0b5c55;
  --_d-teal-light: #6fcfc4;
  --_d-teal-soft: #1a2a2c;
  --popup-close-fill: #3a4256;
  --popup-close-fill-hover: #465066;
  --popup-close-border: #4a5369;
  --popup-close-glyph: #ffffff;
  --_d-hairline: #2e3546;
  --_d-steel: #6a7488;
  --_d-green: #5fd08a;
  --_d-green-soft: #16302e;
  --_d-green-edge: #4a7a60;
  --_d-yellow: #ffd028;
  --_d-yellow-soft: #2a2418;
  --_d-red: #f08a82;
  --_d-red-soft: #2c1a19;
  --_d-red-border: #8a4a45;
  --_d-red-edge: #DC281A;    /* 3.29:1 on the dark card */
  --_d-green-pill: #6FD79B;
  --_d-green-pill-edge: #5eb185;
  --_d-yellow-pill-edge: #c9a730;
  --_d-red-pill: #F08B8B;
  --_d-red-pill-edge: #c87878;
  --_d-teal-pill: #4FD1C5;
  --_d-teal-pill-edge: #46aca4;
  --_d-neutral-pill: #9AA1AD;
  --_d-neutral-pill-edge: #828994;


  --connect-canvas: var(--_d-canvas);
  --connect-surface: var(--_d-surface);
  /* Dark mode carries a real yellow - the product's own #ffd028 - because
     luminance is no longer the constraint it is on a light ground. */
  --connect-pill-success-ink: var(--_d-green-pill);    /* 8.95:1 on surface, 10.13 on canvas */
  --connect-pill-success-edge: var(--_d-green-pill-edge);   /* 6.11:1 on surface, 6.91 on canvas */
  --connect-pill-warning-ink: var(--_d-yellow);   /* 10.78:1 */
  --connect-pill-warning-edge: var(--_d-yellow-pill-edge);  /* 6.82:1 */
  --connect-pill-danger-ink: var(--_d-red-pill);    /* 6.61:1 */
  --connect-pill-danger-edge: var(--_d-red-pill-edge);   /* 4.83:1 */
  --connect-status-progress-ink: #A9ADF7;   /* ROUND 47 §8.2, dark. 7.54:1 on surface #1e2230 */
  --connect-status-progress-edge: #6F74CC;  /* 3.80:1 on surface, non-text floor 3.0 */
  --connect-pill-neutral-ink: var(--_d-neutral-pill);   /* 6.09:1 */
  --connect-pill-neutral-edge: var(--_d-neutral-pill-edge);  /* 4.49:1 */
  --connect-surface-muted: var(--_d-surface-muted);
  --connect-surface-action: var(--_d-surface-muted);
  --connect-surface-overlay: var(--_d-overlay);

  --connect-text: var(--_d-text);
  --connect-text-muted: var(--_d-text-muted);

  /* Round 5 §5: dark DEFAULTS to BLUE (Neerav, 4 Sep) - the [data-theme="dark"]
     copy of the same default the prefers-color-scheme block carries. Teal is
     still one tap away as a chosen accent via [data-accent="teal"] below. */
  --connect-accent: #2f6fed;
  --connect-accent-hover: #2455bb;
  --connect-trigger-ground: var(--connect-surface-muted);
   --connect-trigger-edge: var(--connect-border-strong);
--connect-accent-text: #8fb8ff;
  --connect-accent-soft: #16202e;

  --connect-border: var(--_d-hairline);
  --connect-border-strong: var(--_d-steel);
  --connect-scroll-thumb: #4b5570;                 /* 2.14:1 on #1e2230 */
  --connect-scroll-thumb-hover: var(--_d-steel);   /* 3.37:1 on #1e2230 */
  --connect-elevation: 0 1px 2px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .3);
  --connect-focus-ring: var(--connect-accent-text);

  --connect-success: var(--_d-green);
  --connect-success-soft: var(--_d-green-soft);
  --connect-warning: var(--_d-yellow);
  --connect-warning-soft: var(--_d-yellow-soft);
  --connect-danger: var(--_d-red);
  --connect-danger-soft: var(--_d-red-soft);
  --connect-danger-border: var(--_d-red-border);
  --connect-danger-deep: var(--_d-red);
  --connect-danger-deep-soft: var(--_d-red-soft);
  --connect-info: var(--connect-accent-text);

  /* The three neutral tiers, dark (stamped). */
  --connect-icon-primary: #e9e7e2;
  --connect-icon-standard: #aeb6c4;
  --connect-icon-decorative: #788093;  /* #6a7284 measured 2.53 on overlay */

  --connect-icon-edge: var(--_d-steel);
  --connect-icon-edge-positive: var(--_d-green-pill-edge);

  /* THE REFERENCE CONTROL, DARK. The relationship is the decision; these are
     the values that satisfy it on a dark ground.
     Defined here because a role defined in ONE theme scope silently inherits
     the other theme's value - and this trio was light-only until Guard 0 was
     written. Consumed, that would have put a near-black icon (#0B5A55) on a
     near-black ground (#1E2230): invisible, and invisible in the theme that
     is harder to notice being wrong.

     --control-border is #118075, not the #0F766E the search button renders.
     #0F766E measures 2.89:1 against #1E2230 and the non-text floor is 3.0
     (WCAG 1.4.11). #118075 is two units along, indistinguishable by eye, and
     measures 3.29:1. The design is kept; the boundary is made legible.
     --control-icon 8.59:1. */
  --control-bg: var(--_d-surface);
  --control-border: var(--connect-icon-decorative);
  --control-icon: var(--connect-icon-primary);

  /* The new roles, dark. --control-* is deliberately NOT restated: dark was
     measured and holds, and its three relationships already resolve through
     the accent roles. Restating them would make a second place for one
     decision, which is the thing this file exists to prevent. */
  --surface-edge: var(--_d-hairline);
  --connect-icon-neutral: var(--_d-text-muted);
  --connect-icon-positive: var(--_d-green);
  --connect-icon-negative: var(--_d-red);
  --connect-icon-edge-negative: var(--_d-red-edge);
  --connect-icon-quiet: var(--_d-steel);
}

/* ===================================================================
 * PRINT
 *
 * Invoices and receipts get printed or exported. They must not follow the
 * interface theme - a dark-mode invoice is wrong on paper and wrong when a
 * customer opens the PDF.
 * =================================================================== */
@media print {
  :root, :root[data-theme="dark"] {
    color-scheme: light;
    --connect-canvas: #ffffff;
    --connect-surface: #ffffff;
    --connect-text: #1f2430;
    --connect-text-muted: #4b5563;
    --connect-border: #d4d8dd;
  }
}

/* ---------------------------------------------------------------------------
   Touch targets.

   38px clears WCAG 2.5.8 (AA, 24px minimum) comfortably, and it is what keeps a
   filter bar's inputs and its buttons on one line - they were previously 44px and
   38px on the same row, agreeing with nothing.

   Where the pointer is coarse there is no such alignment constraint and a finger
   is not a cursor, so controls take the 44px of WCAG 2.5.5 (AAA) instead. One
   token, two contexts, rather than two hardcoded numbers that never met.

   ROUND 18 A2b ADDS THE WIDTH, and asked for it not to be assumed: the shell's
   mobile breakpoint is 767/768px, set by connect-base.css's
   `@media(max-width:767px){.shell{display:block}}` and by every layout rule in
   this project that pairs with it. It is the only layout breakpoint the shell
   has.

   BOTH CONDITIONS, not one. Pointer capability is the better question - a
   finger is a finger on a 1024px tablet - and this file has answered it that
   way since round 6. But a narrow window on a desktop is also where the
   product is used one-handed on a small laptop, and A2b names the breakpoint
   explicitly. `,` is OR, so either one lifts the target; neither can lower it.

   THE GLYPH DOES NOT GROW WITH THE BOX. A2b is explicit - 44px box, the same
   20px glyph - so --control-icon-size is absent from this block on purpose,
   and --control-icon-inset recomputes itself to 12px from the box that did
   change.
--------------------------------------------------------------------------- */
@media (pointer: coarse), (max-width: 767px) {
  :root {
    --connect-control-height: 44px;
    --connect-button-height: 44px;
    --connect-control-height-compact: 44px;
    --control-size: 44px;
  }
}

/* ===================================================================
 * ROUND 6 §7 - PER-USER ACCENT COLOUR
 *
 * A person's chosen accent is stamped on <html data-accent="slug"> from their
 * shell preference (key accent_colour; the stored value is a CHOICE NAME, never
 * a hex). It overrides the theme default in BOTH themes.
 *
 * BLUE IS THE DEFAULT in both themes (Neerav, 4 Sep) - set on the base :root
 * and both dark blocks above, so no data-accent attribute is needed for it.
 *
 * Only the accent ROLE tokens are set here; everything derived from them
 * (info, focus ring, trigger edge, button text, brand foot-line, links) follows
 * automatically. Dark is reached two ways - an explicit [data-theme="dark"] and
 * system-dark with no explicit light override - so each accent is declared for
 * both, the same three-block shape the base palette uses. Colour literals are
 * permitted here: this IS the token layer.
 *
 * Contrast on the ivory ground #FAF8F3, from the six-check:
 *   Blue 4.90:1 · Teal 7.59:1 · Indigo 6.24:1 · Mono 14.91:1 - all clear 4.5:1.
 * =================================================================== */

/* ROUND 46 §3.1 - THE PREVIEW AND THE ACCENT, ONE SOURCE.

   The swatch dots were typed into the Blade as light hexes and rendered in
   whichever theme the reader was in, so all four previewed a colour the tile
   did not select. Monochrome was the plainest case: a near-black dot for an
   accent that is near-white in the dark.

   A preview cannot drift from what it previews if it IS what it previews. So
   each accent's value is named once per theme below, and the accent rule reads
   its token rather than repeating a hex. The dots read the same tokens - and
   they read ALL FOUR, not just the active one, because the swatch row shows
   every choice at once while only one data-accent is on <html>. That is why
   these sit on bare :root and not beside each [data-accent] rule. */
:root{--connect-accent-preview-blue:#1868DB;--connect-accent-preview-teal:#0B5A55;
  --connect-accent-preview-indigo:#4B4FBF;--connect-accent-preview-mono:#1E2230}
:root[data-theme="dark"]{--connect-accent-preview-blue:#2f6fed;--connect-accent-preview-teal:#118075;
  --connect-accent-preview-indigo:#5b5fe6;--connect-accent-preview-mono:#e9e7e2}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]){--connect-accent-preview-blue:#2f6fed;--connect-accent-preview-teal:#118075;
    --connect-accent-preview-indigo:#5b5fe6;--connect-accent-preview-mono:#e9e7e2}
}

/* --- Light --- */
:root[data-accent="blue"]{--connect-accent:var(--connect-accent-preview-blue);--connect-accent-hover:#1250aa;--connect-accent-text:#1868DB;--connect-accent-soft:#e7eefb;--connect-accent-on:#ffffff}
:root[data-accent="teal"]{--connect-accent:var(--connect-accent-preview-teal);--connect-accent-hover:#084440;--connect-accent-text:#0B5A55;--connect-accent-soft:#E6F0EF;--connect-accent-on:#ffffff}
:root[data-accent="indigo"]{--connect-accent:var(--connect-accent-preview-indigo);--connect-accent-hover:#3a3d96;--connect-accent-text:#4B4FBF;--connect-accent-soft:#eaeafa;--connect-accent-on:#ffffff}
:root[data-accent="mono"]{--connect-accent:var(--connect-accent-preview-mono);--connect-accent-hover:#0f121c;--connect-accent-text:#1E2230;--connect-accent-soft:#eceef1;--connect-accent-on:#ffffff}

/* --- Dark, explicit choice --- */
:root[data-theme="dark"][data-accent="blue"]{--connect-accent:var(--connect-accent-preview-blue);--connect-accent-hover:#2455bb;--connect-accent-text:#8fb8ff;--connect-accent-soft:#16202e;--connect-accent-on:#ffffff}
:root[data-theme="dark"][data-accent="teal"]{--connect-accent:var(--connect-accent-preview-teal);--connect-accent-hover:#0b5c55;--connect-accent-text:#6fcfc4;--connect-accent-soft:#1a2a2c;--connect-accent-on:#ffffff}
:root[data-theme="dark"][data-accent="indigo"]{--connect-accent:var(--connect-accent-preview-indigo);--connect-accent-hover:#4749b8;--connect-accent-text:#aeb0f5;--connect-accent-soft:#1e1f33;--connect-accent-on:#ffffff}
:root[data-theme="dark"][data-accent="mono"]{--connect-accent:var(--connect-accent-preview-mono);--connect-accent-hover:#c8c6c1;--connect-accent-text:#e9e7e2;--connect-accent-soft:#26272b;--connect-accent-on:#1E2230}

/* --- Dark, from the system with no explicit light override --- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"])[data-accent="blue"]{--connect-accent:var(--connect-accent-preview-blue);--connect-accent-hover:#2455bb;--connect-accent-text:#8fb8ff;--connect-accent-soft:#16202e;--connect-accent-on:#ffffff}
  :root:not([data-theme="light"])[data-accent="teal"]{--connect-accent:var(--connect-accent-preview-teal);--connect-accent-hover:#0b5c55;--connect-accent-text:#6fcfc4;--connect-accent-soft:#1a2a2c;--connect-accent-on:#ffffff}
  :root:not([data-theme="light"])[data-accent="indigo"]{--connect-accent:var(--connect-accent-preview-indigo);--connect-accent-hover:#4749b8;--connect-accent-text:#aeb0f5;--connect-accent-soft:#1e1f33;--connect-accent-on:#ffffff}
  :root:not([data-theme="light"])[data-accent="mono"]{--connect-accent:var(--connect-accent-preview-mono);--connect-accent-hover:#c8c6c1;--connect-accent-text:#e9e7e2;--connect-accent-soft:#26272b;--connect-accent-on:#1E2230}
}

/* §7 BUILD RULE: choosing BLUE - the default, so this covers the unset case too
   - recolours the Outstanding-workload donut's "Service case" segment from
   --connect-chart-blue (#3D86EF) to indigo, so the segment cannot be mistaken
   for the accent it now sits beside. Nothing else in the chart palette moves. */
:root:not([data-accent]),:root[data-accent="blue"]{--connect-chart-blue:#4B4FBF}
:root[data-theme="dark"]:not([data-accent]),:root[data-theme="dark"][data-accent="blue"]{--connect-chart-blue:#6F73E9}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not([data-accent]),:root:not([data-theme="light"])[data-accent="blue"]{--connect-chart-blue:#6F73E9}
}

/* ─────────────────────────────────────────────────────────────────────────
   THE SPACING SCALE. Round 16 item 20a.

   ITEM 0m'S ANSWER, MEASURED: there was no spacing scale. The token file
   carried 289 custom properties and a handful of one-off spacing values -
   --connect-control-gap: 8px, --connect-popover-padding: 14px,
   --connect-group-pad: 8px - but nothing a rule could be written against. So
   every gap in the product was a number somebody typed, which is why fourteen
   interactive controls on /settings/ai-connections sat within 8px of their
   neighbour and five of them at exactly 0px.

   A 4px base, because the values already in the file (8, 12, 14, 18, 24) sit
   on or near it and a scale that contradicts what exists is a scale nobody
   adopts.

   TWO OF THESE ARE RULES, NOT SUGGESTIONS, and the guard measures them:

     --connect-space-2 (8px)   the MINIMUM between two interactive siblings
     --connect-space-3 (12px)  the MINIMUM from a control to its container's
                               inner edge, on all four sides

   Named by step rather than by purpose (--space-tight, --space-loose) because
   purpose names invite argument about which one a case is, and a number does
   not.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  --connect-space-1: 4px;
  --connect-space-2: 8px;
  --connect-space-3: 12px;
  --connect-space-4: 16px;
  --connect-space-5: 24px;
  --connect-space-6: 32px;

  /* The two floors, named so a rule can say WHY rather than restate a number. */
  --connect-min-control-gap: var(--connect-space-2);
  --connect-min-control-inset: var(--connect-space-3);
}

/* ═════════════════════════════════════════════════════════════════════════
   THE LAYOUT CONTRACT. Round 20 item B1.

   Neerav, round 19 and again in round 20: "All tiles on the system should have
   the same buffer spacing, this should also apply when a new page is created,
   it should be in the core formatting with all the other design rules… a new
   page that is made should always follow a set design. this also helps in the
   future when the system becomes more dynamic and can start building out pages
   on its own."

   THE CENSUS THAT JUSTIFIES IT, taken today with tools/contract/census.py:
   732 spacing declarations off the 4px scale, 45 control heights outside the
   control set, 10 focus treatments that are not the standard one. 34 distinct
   off-scale spacing values, of which 10px (x104), 18px (x105) and 14px (x101)
   account for a third on their own.

   B1a: TOKENS THE COMPONENTS CONSUME, NOT A DOCUMENT DESCRIBING THEM. A rule
   that a component can ignore by typing a number is advisory, and round 16
   already ran that experiment - it established a 4px scale and the census above
   is what happened to it. Everything below is consumed by a component and
   measured by a guard.
   ═════════════════════════════════════════════════════════════════════════ */
:root {
  /* ── THE SPACING SCALE ───────────────────────────────────────────────────
     The permitted values. Not "any multiple of four" - a scale is a SHORT list,
     and 34 distinct values on a 4px base is what having no list looks like.

     Steps 1-6 already existed and are untouched, because eighty-three
     declarations consume them and renumbering a scale to make it prettier is
     how a contract loses its first argument. 0, and steps 7-9, are the values
     the census showed were needed and missing. */
  --connect-space-0: 0;         /* deliberately none - a reset, stated as one */
  /* --connect-space-1: 4px  hairline separation inside one control */
  /* --connect-space-2: 8px  the MINIMUM between two interactive siblings */
  /* --connect-space-3: 12px the MINIMUM from a control to its container edge */
  /* --connect-space-4: 16px inside a card or panel; the contract's padding */
  /* --connect-space-5: 24px between stacked blocks; the vertical rhythm */
  /* --connect-space-6: 32px between one section of a page and the next */
  --connect-space-7: 40px;      /* around a page header, above the first block */
  --connect-space-8: 48px;      /* the page's own outer margin at the sides */
  --connect-space-9: 64px;      /* the bottom of a page, so content clears the fold */

  /* ── CARD AND PANEL PADDING ──────────────────────────────────────────────
     ONE VALUE, UNIFORM ON ALL FOUR SIDES, and the choice is 16px.

     It is the convergent value across IBM Carbon, Shopify Polaris (xs), Fluent
     and Material 3, and - the part that decided it - it is ON THE SCALE.
     Polaris's 20px at sm and up is equally defensible and is not on this scale,
     and adding a step to the scale to justify one padding value would be the
     scale bending to the component rather than the other way round.

     Cards were 22px. This tightens them by 6px, everywhere, on purpose. */
  --connect-card-padding: var(--connect-space-4);

  /* THE DASHBOARD WIDGET FOOTER, 8 September.
   *
   * The distance from the hairline above a widget's hint to the card's outer
   * bottom edge. One number for every widget, so the rule stops being a
   * function of what happens to be above it - a label's height, a legend's row
   * count, a card's padding.
   *
   * 84px at a cell of 225 and up, which is where the wide tiles already sat.
   * Below 431 the tiles are content-sized and much shorter - 155px at 360 -
   * and an 84px band there is more than half the card, so the small screen
   * takes 64: still one number, still measured from each card's own bottom
   * edge, which is what 2b asks. */
  --connect-widget-footer: 64px;

  /* ── VERTICAL RHYTHM ─────────────────────────────────────────────────────
     One gap between stacked blocks. 24px, which is what most of the product
     already uses when it is not using 18, 20, 22, 26 or 28. */
  --connect-block-gap: var(--connect-space-5);

  /* ── THE TYPE SCALE ──────────────────────────────────────────────────────
     B2a: FIXED PAIRS. A size never appears without its line height, because a
     size that carries whatever line height the page felt like is how 14px body
     came to render at 17px (ratio 1.20) on some pages and 22px (1.60) on
     others - and 1.20 is below every value any documented system publishes.

     14/20 is IBM Carbon body-01, Atlassian font.body, Fluent 2 Body 1 and
     Material 3 body-medium, independently. Every line height here lands on the
     4px grid, as Carbon, Polaris, Primer, Fluent and Atlassian all do.

     THE RATIO TIGHTENS AS THE SIZE GROWS, which is why there is no single
     multiplier: 11px takes 1.45 and 30px takes 1.20. GOV.UK uses 1.04 at 48px,
     Material 3 1.12 at 57px, Atlassian 1.125 at 32px. */
  --connect-leading-2xs: 16px;  /* with --connect-text-2xs (11px) - 1.45 */
  --connect-leading-xs: 16px;   /* with --connect-text-xs  (12px) - 1.33 */
  --connect-leading-sm: 20px;   /* with --connect-text-sm  (13px) - 1.54 */
  --connect-text-base: 14px;    /* body prose and every control label */
  --connect-leading-base: 20px; /* 1.43 - Carbon, Atlassian, Fluent, Material */
  --connect-leading-md: 24px;   /* with --connect-text-md  (16px) - 1.50 */
  --connect-leading-lg: 24px;   /* with --connect-text-lg  (19px) - 1.26, h2 */
  --connect-text-xl: 20px;      /* a widget's own number, and nothing else */
  --connect-leading-xl: 28px;   /* 1.40 - the step between 16 and 30 that the
                                   first version of this scale left out. Nine
                                   rules sat outside the scale and four of them
                                   were here; a scale missing a step people need
                                   is the scale being wrong, not four rules. */
  --connect-leading-2xl: 36px;  /* with --connect-text-2xl (30px) - 1.20, h1 */

  /* THE SUB-LABEL ROW A DOUBLE-WIDE METRIC TILE CARRIES AND A SINGLE-WIDE ONE
     MUST RESERVE, so the two figures in a row share a baseline.

     DERIVED FROM THE RAMP ABOVE, NOT MEASURED OFF A SCREEN: a grouped tile's
     sub-label renders on --connect-leading-2xs, and its band separates that
     name from the figure by --connect-space-1. The reserve is that line box
     plus that gap - so restyling the ramp moves BOTH cards together and cannot
     put them back out of line. A number typed here would have held only at the
     width it was measured at; the gap it replaced grew from 38.6px to 71.9px
     as the card grew. */
  /* QUEUE 16.5 MOVED THE GAP AND THE RESERVE HAD TO MOVE WITH IT.
     §4 doubles the distance between a group row's label and its figure. The
     reserve is that label's line box PLUS that gap - that is what the note
     above says it is - so leaving it on space-1 while the row moved to space-3
     put the two cards out of line by 30px, measured at 1440: the stat tile's
     figure at y60 in its tile against the group's at y90.
     ONE NAME FOR THE GAP, used by the reserve and by the row that creates it,
     so the pair cannot drift again. */
  --connect-metric-sublabel-gap: var(--connect-space-3);
  --connect-metric-sublabel-reserve: calc(var(--connect-leading-2xs) + var(--connect-metric-sublabel-gap));

  /* ── THE MEASURE ─────────────────────────────────────────────────────────
     B2b: running prose only. Real paragraphs measured at 56, 78, 80, 92, 104,
     119, 123 and 154 characters; Material caps at 60, USWDS targets 66,
     GOV.UK 75, Primer 80. 72ch sits inside the 66-80 band and above USWDS's
     target, which suits a working screen read at a desk rather than an article.

     NOT applied to tables, code or data rows. Those legitimately run wider and
     capping them would be a worse fault than the one being fixed. */
  --connect-measure: 72ch;

  /* ── HEADING SPACING ─────────────────────────────────────────────────────
     B2c: space ABOVE a heading exceeds space below it, so a heading groups with
     the text it introduces rather than floating between two blocks.

     STATED AS A REASONED CHOICE, NOT A CITATION. No system I can source puts a
     number on the asymmetry: Atlassian encodes it as banded tokens (0-8px
     within a group, 12-24px around one) and Fluent states the principle without
     numbers. 24 above and 12 below sits inside Atlassian's bands in both
     directions and keeps the 2:1 relation legible.

     h2 computed margin-bottom: 0 on three of the four pages measured - a
     heading with no space beneath it at all. */
  --connect-heading-space-above: var(--connect-space-5);
  --connect-heading-space-below: var(--connect-space-3);

  /* ── THE CONTROL SET ─────────────────────────────────────────────────────
     Written down as the contract rather than left as three separate decisions.
     These are the ONLY heights a control may be. Each already existed; the
     contract is the list, not new numbers.

       30px  --connect-control-height-compact  a control inside a dense table
       32px  --control-size                    buttons, icon buttons, pills
       38px  --connect-control-height          text inputs and selects
       44px  all of the above under (pointer: coarse) or below 767px

     A nav row is NOT a control and is not on this list: the module row is 42px
     and sized by its own token. B3b's 20px jump-nav link was on neither list,
     which is the whole of that item. */
  /* 44px, which is the touch floor already in the control set above. The
     row was 42 while the jump nav was 38; both are the contract's 14/20
     pair with 12px of padding now, which lands on 44 and on a value the
     contract already names. */
  --connect-nav-row-height: 44px;

  /* ── THE FOCUS TREATMENT ─────────────────────────────────────────────────
     One rule since round 19, pinned here so a page cannot invent a second.
     3px solid the focus ring, offset 2px, on every focusable thing. Measured:
     5.20:1 against the page and 4.90:1 against a card in light; 8.93:1 and
     7.89:1 in dark. The non-text floor is 3.0. */
  --connect-focus-width: 3px;
  --connect-focus-offset: 2px;

  /* ── BORDER RADIUS ───────────────────────────────────────────────────────
     Confirmed consistent and pinned. Three values, and the existing
     --connect-radius-control / -container / -pill are those three. A fourth
     radius is a component inventing a shape. */
}
