/* ============================================================
   ix-tokens-v1.0.4.css
   INBXIFY · ix component series — THE TOKEN SOURCE OF TRUTH

   LOAD THIS FIRST. Before ix-buttons, ix-form-controls, ix-modals,
   ix-header, ix-ai-badge, ix-lightbox, title-admin-page-design, and
   before any surface stylesheet. Everything downstream reads from it.

   ════════════════════════════════════════════════════════════
   WHY THIS FILE EXISTS — TD-241, and six months of bugs
   ════════════════════════════════════════════════════════════
   Until now the ix series defined NO tokens. There was no :root
   block anywhere. ~1,000 var(--ix-*) references across the modules
   and the design CSS were each relying on whatever inline fallback
   the author happened to type that day.

   An audit found:

   · 25 TOKENS WITH NO FALLBACK ANYWHERE. A var() with no fallback
     and no definition is INVALID. The browser drops the whole
     declaration and the INHERITED value wins. So
         color: var(--ix-teal)          → inherits the parent colour
         background: var(--ix-surface)  → no background at all
         font-size: var(--ix-text-tiny) → inherits the parent size
     `--ix-teal` was referenced 14 times and rendered as inherited
     text every single time. On a light surface inheriting from a
     white parent, that is white text on white. This is the
     mechanism behind the recurring white-on-white and
     cream-on-cream failures — not one careless stylesheet, but a
     design system with no source of truth for its own values.

   · CONFLICTING FALLBACKS for the same token: --ix-white was
     #FFFFFF in some files and #ffffff in others; --ix-blue-shadow
     carried two different alphas; --ix-font-body had two different
     stacks. Tokens that disagree with themselves are not tokens.

   ════════════════════════════════════════════════════════════
   HOW THE VALUES WERE CHOSEN
   ════════════════════════════════════════════════════════════
   Tokens that ALREADY had a de-facto fallback keep that exact
   value, so rooting them changes nothing on screen. Where two
   fallbacks disagreed, the more common one won and is noted.

   Tokens that resolved to NOTHING are defined here for the first
   time. Those WILL change rendering — from "silently broken" to
   "as designed". Expect teal text to appear where inherited text
   used to be, and surfaces to gain the backgrounds they were
   always meant to have. That is the fix, not a regression.

   ════════════════════════════════════════════════════════════
   CONTRAST IS AUDITED, NOT EYEBALLED
   ════════════════════════════════════════════════════════════
   Every foreground/background pair defined in a consuming stylesheet
   must be run through contrast-audit.py before shipping. It resolves
   these tokens, composites translucent tints over their backdrop, and
   fails anything under 4.5:1 (3:1 for large or bold text).

   Tint-on-tint pairs are the ones that fail. A colour at 12% alpha
   over white and its own full-strength value look obviously different
   to the eye and are frequently under 3:1 in fact. Do not trust the
   eye on those; run the numbers.


   ════════════════════════════════════════════════════════════
   v1.0.4 (Aug 26, 2026) — STATUS COLOURS FOR DARK BARS
   ════════════════════════════════════════════════════════════
   --ix-danger and --ix-ok are tuned for LIGHT surfaces. On the
   teal app bar they measure 3.4:1 and 2.34:1 — both under floor.
   Every surface with a dark bar therefore needs a lightened pair,
   and until now each one invented its own.

   The Cadence Board had already done exactly that: a local
   #FFB4AD for error text on teal, with a comment reading "if a
   second surface ever needs it, promote it to --ix-danger-light."
   A second surface now does — its own success messages. So both
   halves of the pair are promoted here rather than one being
   tokenised and the other left local.

     --ix-danger-light  #FFB4AD   7.24:1 on --ix-teal
     --ix-ok-light      #7FD8B4   7.24:1 on --ix-teal

   The two ratios are matched deliberately. A success and a
   failure arriving in the same slot must not differ in how loud
   they are — only in hue. Pick replacements by measuring against
   the other one, not just against the floor.

   NOT for light surfaces. Both fail badly on white (1.31:1 and
   1.70:1). They are dark-bar colours only; --ix-danger and
   --ix-ok remain correct everywhere else.

   ════════════════════════════════════════════════════════════
   v1.0.3 (Aug 25, 2026) — THE SECOND SOURCE OF TRUTH IS GONE
   ════════════════════════════════════════════════════════════
   v1.0.2 declared this file the token source of truth. It was
   not. title-admin-page-design carried its OWN :root with 47
   tokens, 28 of which disagreed with the values here. Whichever
   file loaded last won — for the whole page and every ix module
   downstream. The design system had two sources of truth and the
   winner was decided by script order.

   That rival block is DELETED in title-admin-page-design v1.4.24.
   This file is now what it always claimed to be. The conflicts
   were resolved deliberately, not by whoever loaded last:

   KEPT AT THE ix VALUE (the design file's value discarded):
     --ix-blue-deep    #3F57B0 over #4060dd  — 6.56:1 vs 4.0:1
     --ix-gold-bright  #E8C172 over #dbb868
     --ix-gold-warm    #B8934A over #e8c172  — the design file had
                       "warm" holding a LIGHTER value than "bright",
                       which inverts the whole scale's meaning
     --ix-surface      #FFFFFF over #fdfcf9
     --ix-cream-warm   #FDFCF8 over #f5f0e4
     --ix-cream-deep   #F1EDE2 over #ebe5d4
     --ix-ok           #1F7A5C over #27ae60  — 4.8:1 vs 2.9:1
     --ix-warn         #6F5722 over #e8a030  — the v1.0.1 contrast
                       fix, silently reverted by the rival block
     --ix-z-lightbox   99998 over 10001      — 10001 sat ABOVE
                       --ix-z-modal (10000); the lightbox was
                       covering modals opened from it
     plus the blue/gold tints, shadows, font stacks and --ix-ease.

   TWO CONFLICTS NEEDED A REAL DECISION, NOT A PICK:

   1. --ix-text-tiny was 11px HERE and #a8abb4 THERE. Same name,
      different TYPES. It is read as a colour 43 times and as a
      font-size 3 times across the codebase, so one of those
      groups has been invalid — silently inheriting — the entire
      time. It stays a SIZE here, because it sits in the type
      block beside the font stacks and every other --ix-text-*
      that is a colour says so in its name. The 36 colour uses in
      title-admin-page-design are remapped to --ix-text-mid in
      v1.4.24. #a8abb4 is not preserved anywhere: at 2.5:1 on
      white it was never a legal text colour.

   2. --ix-teal-soft was a 10% tint HERE and solid #2d5a5a THERE.
      Both are wanted, for different jobs. The tint keeps the
      name; the solid becomes --ix-teal-mid (NEW). The W circle's
      gradient reads the solid.

   ── ONE TOKEN CHANGED VALUE ──
     --ix-text-light  #8A8E9A → #6B7280
   At the old value it measured 3.27:1 on white and 3.11:1 on
   cream, and it is used 61 times in title-admin-page-design
   alone — every small label, every empty-state line, every meta
   row. It was the single largest source of unreadable text on
   the platform. The new value is 4.83:1 on white, 4.59:1 on
   cream. Everything using it gets legible at once, which is the
   point of owning a token rather than a hex.

   ── NEW: INK VARIANTS ──
   Gold at #C4A35A measures 2.40:1 on white and salmon 2.95:1.
   Both are beautiful as FILLS, SPINES and BORDERS and illegal as
   TEXT on a light surface. Rather than change colours the brand
   is recognised by, each gains a darker ink sibling for the text
   case only. Fill stays gold; the letters on it go ink.

   ── NEW: RAIL AND CANVAS ──
   The T-A tab rail becomes dark teal in v1.4.24. Cream rail on
   white panel measured 1.05:1 — no boundary at all. These tokens
   own that surface so a second dark rail elsewhere cannot drift
   from it.

   ════════════════════════════════════════════════════════════
   NEW IN THE LANGUAGE — SALMON
   ════════════════════════════════════════════════════════════
   --ix-salmon is the platform's "needs you now" accent, promoted
   from the Cadence Board mockup to a first-class token so every
   surface can speak it.

   IT HAS ONE MEANING AND ONE ONLY: this item is waiting on the
   operator. The next issue to build. An overdue obligation. A
   failed intake. A blocking defect.

   It is NOT a hover, NOT a brand accent, NOT decoration, and NOT
   an error colour — --ix-danger owns destructive and failed. A
   colour that appears everywhere tells you nothing, and salmon
   only works because it is rare. If you are reaching for it and
   the thing is not literally waiting on a person, reach for gold.

   GOLD remains the editing-state / pending-change accent
   (ix-form-controls' --ix-changed-border). TEAL remains structure
   and headings. BLUE remains links and focus. Four accents, four
   jobs, no overlap.
   ============================================================ */

:root {

  /* ── Brand core ────────────────────────────────────────────
     Teal is structure: headings, dark bars, primary text on light.
     Gold is accent and pending-state. Blue is link and focus.     */
  --ix-teal:            #1A3A3A;   /* was UNDEFINED — 14 refs rendering as inherited */
  --ix-teal-deep:       #14302F;
  --ix-teal-soft:       rgba(26, 58, 58, 0.10);   /* was UNDEFINED */
  --ix-teal-mid:        #2D5A5A;   /* v1.0.3 — absorbed from the design file's
                                      rival --ix-teal-soft. Solid mid-teal for
                                      gradients; the TINT above keeps the name. */
  --ix-teal-ghost:      rgba(26, 58, 58, 0.05);   /* was UNDEFINED */

  --ix-gold:            #C4A35A;
  --ix-gold-muted:      #C4A35A;
  --ix-gold-bright:     #E8C172;   /* was UNDEFINED */
  --ix-gold-light:      #E8C172;
  --ix-gold-warm:       #B8934A;   /* was UNDEFINED */
  --ix-gold-glow:       rgba(196, 163, 90, 0.12);
  --ix-gold-shadow:     rgba(196, 163, 90, 0.20);
  --ix-gold-ink:        #8A6E2E;   /* v1.0.3 · 4.83:1 on white, 4.70 on cream-warm.
                                      GOLD AS TEXT ONLY. Fills, borders and spines
                                      stay --ix-gold; only the letters go ink. */

  --ix-blue:            #5B7FFF;   /* case unified — was #5B7FFF and #5b7fff */
  --ix-blue-deep:       #3F57B0;   /* was UNDEFINED — 21 refs */
  --ix-blue-light:      #9CB5FF;
  --ix-blue-ghost:      rgba(91, 127, 255, 0.08);  /* was UNDEFINED — 13 refs */
  --ix-blue-glow:       rgba(91, 127, 255, 0.14);  /* was UNDEFINED */
  --ix-blue-shadow:     rgba(91, 127, 255, 0.20);  /* two alphas existed; .20 was commoner */

  /* ── Salmon · "needs you now" ──────────────────────────────
     Read the header note before using this. One meaning only.    */
  --ix-salmon:          #EC7259;
  --ix-salmon-deep:     #D25842;
  --ix-salmon-soft:     rgba(236, 114, 89, 0.14);
  --ix-salmon-ghost:    rgba(236, 114, 89, 0.07);
  --ix-salmon-ink:      #C0432B;   /* v1.0.3 · 5.15:1 on white. Same rule as
                                      gold-ink: fill salmon, letters ink. */

  /* ── Surfaces ──────────────────────────────────────────────
     cream       page background
     cream-warm  raised panel / callout fill
     cream-deep  recessed strip, control bars, alternating rows
     surface     card white — the default "thing on the page"      */
  --ix-white:           #FFFFFF;   /* case unified */
  --ix-surface:         #FFFFFF;   /* was UNDEFINED — 8 refs with NO background at all */
  --ix-cream:           #FAF9F5;
  --ix-cream-warm:      #FDFCF8;   /* was UNDEFINED */
  --ix-cream-deep:      #F1EDE2;   /* was UNDEFINED */
  --ix-canvas:          #E8E3D6;   /* v1.0.3 · the page behind the workspace card.
                                      1.28:1 against white — enough to read as a
                                      separate plane, which --ix-cream (1.05) is not. */

  /* ── Tab rail · dark anchor (v1.0.3) ───────────────────────
     The T-A rail. Same teal as the Cadence Board's app bar, so
     the platform has ONE dark surface rather than two that drift.
     Ratios against --ix-rail: text-on 12.3 · text 6.8 · gold 5.1  */
  --ix-rail:            #1A3A3A;
  --ix-rail-lift:       #24504F;   /* active row bed */
  --ix-rail-text:       #B9C2C2;   /* resting label */
  --ix-rail-text-on:    #FFFFFF;   /* active label */
  --ix-rail-alert:      #F08A73;   /* 5.02:1 — salmon is only 4.16 on this bed */

  /* ── Text ──────────────────────────────────────────────────
     Never set a text colour with a literal. Contrast regressions
     start with a hex typed inline that nobody can grep for.       */
  --ix-text-dark:       #1E2A3A;
  --ix-text-mid:        #5A6478;
  --ix-text-light:      #6B7280;   /* v1.0.3: was #8A8E9A (3.27:1 on white — 61
                                      uses in the design CSS alone). Now 4.83:1. */
  --ix-text-invert:     #FFFFFF;   /* text ON teal/salmon/dark bars */

  /* ── Borders ───────────────────────────────────────────────*/
  --ix-border:          #E4E0D4;
  --ix-border-soft:     #EEE9DC;
  --ix-border-input:    #DDD9C8;

  /* ── Status ────────────────────────────────────────────────
     danger = destructive or failed. warn = needs attention but
     nothing broke. ok = succeeded. salmon is NOT in this family —
     it means "waiting on you", which is not a status.             */
  --ix-danger:          #C0392B;
  /* v1.0.1: darkened from #8A6D2F. At the old value, warn text on a
     --ix-gold-glow background measured 4.44:1 — under the 4.5:1 floor
     — and that pairing occurs in four places. Fixed in the token so
     every consumer inherits the fix rather than patching each one. */
  --ix-warn:            #6F5722;   /* was UNDEFINED — 13 refs */
  --ix-ok:              #1F7A5C;   /* was UNDEFINED — 20 refs */

  /* v1.0.4 · ON-DARK VARIANTS. --ix-danger and --ix-ok above are
     tuned for light surfaces and fail on a teal bar (3.4:1 and
     2.34:1). Use these two — and ONLY these two — for status text
     sitting on --ix-teal or another dark bed. Both measure 7.24:1
     on teal; the match is deliberate so a success and a failure in
     the same slot read as equally loud. Both fail on white. */
  --ix-danger-light:    #FFB4AD;   /* 7.24:1 on --ix-teal */
  --ix-ok-light:        #7FD8B4;   /* 7.24:1 on --ix-teal */

  /* ── Editing state ─────────────────────────────────────────
     Platform invariant: a control holding an unsaved selection
     shows the gold border. Owned here so every surface agrees.    */
  --ix-changed-border:  #C4A35A;
  --ix-changed-shadow:  rgba(196, 163, 90, 0.20);

  /* ── Type ──────────────────────────────────────────────────
     Display = Fraunces, used with restraint on headings.
     Body    = DM Sans.
     Mono    = DM Mono, for identifiers, counts, codes and dates —
               anything the eye should read as data, not prose.    */
  --ix-font-display:    'Fraunces', Georgia, serif;
  --ix-font-body:       'DM Sans', system-ui, sans-serif;  /* two stacks existed; longer one wins */
  --ix-font-mono:       'DM Mono', ui-monospace, monospace;
  --ix-text-tiny:       11px;      /* was UNDEFINED — 37 refs inheriting parent size */

  /* ── Radius ────────────────────────────────────────────────*/
  --ix-radius-sharp:    4px;       /* was UNDEFINED — 44 refs */
  --ix-radius-sm:       8px;
  --ix-radius:          12px;
  --ix-radius-pill:     20px;

  /* ── Shadow ────────────────────────────────────────────────*/
  --ix-shadow-sm:       0 1px 2px rgba(26, 58, 58, 0.07);   /* was UNDEFINED */
  --ix-shadow-md:       0 4px 24px rgba(91, 127, 255, 0.06);
  --ix-shadow-lg:       0 10px 34px rgba(26, 58, 58, 0.12); /* was UNDEFINED */
  --ix-shadow-gold:     0 0 0 3px rgba(196, 163, 90, 0.20);
  --ix-shadow-blue:     0 0 0 3px rgba(91, 127, 255, 0.20); /* was UNDEFINED */

  /* ── Motion ────────────────────────────────────────────────*/
  --ix-ease:            .15s ease;

  /* ── Stacking ──────────────────────────────────────────────
     Lightbox sits just under the modal overlay's 99999 so a modal
     opened from a lightbox still wins. See ix-modals v1.0.2 for
     why that number is hardcoded rather than tokenised there.     */
  --ix-z-dropdown:      5000;
  --ix-z-modal:         10000;
  --ix-z-lightbox:      99998;     /* was UNDEFINED */
}

/* ============================================================
   THE WILDCARD NEUTRALISER
   ============================================================
   title-admin-page-design carries:

     section.publisher-wrapper * { background-color: … !important;
                                   background-image: none !important }

   at specificity (0,1,1). It paints EVERY descendant of the wrapper,
   not just containers — including the label element INSIDE a button,
   which is the detail ix-header's header comment already warns about.

   Any surface injected into that wrapper loses its backgrounds. The
   Cadence Board burned four versions rediscovering this: containers
   were forced one at a time, the wildcard kept reaching the children
   nobody had written a rule for, and the app bar rendered as a row of
   white rectangles with the text hidden beneath them.

   Forcing elements one at a time cannot win, because the wildcard
   reaches anything you have not thought of yet — including whatever
   you add next month.

   So neutralise it wholesale, then re-apply only what is wanted.

   USAGE — put ix-surface-scope on the ROOT of any injected surface:

     <div class="my-board ix-surface-scope"> … </div>

   Then every background inside it must be re-applied at (0,2,0) or
   better. The pattern is a root-scoped selector:

     .my-board .my-appbar { background: var(--ix-teal) !important; }

   SPECIFICITY ARITHMETIC — verify before shipping, do not eyeball:
     section.publisher-wrapper *        (0,1,1)   the wildcard
     .ix-surface-scope *                (0,1,0)   LOSES. not enough.
     .ix-surface-scope[class] *         (0,2,0)   wins.
     .my-board .my-appbar               (0,2,0)   wins.
     .my-appbar                         (0,1,0)   LOSES.

   The [class] attribute selector below is what lifts the reset above
   the wildcard. It matches any element that has a class attribute,
   which is always true of the element it is written on — it exists
   purely to buy one specificity point. Do not remove it.
   ============================================================ */

.ix-surface-scope[class] * {
  background-color: transparent !important;
  background-image: none !important;
}

/* ── v1.0.2 — THE RESET MUST NOT EAT ITS OWN COMPONENTS ──
   v1.0.1's reset is (0,2,0). Every ix component below that paints a
   background was declared at (0,1,0), so the reset outranked them:
   inside a surface-scope, .ix-needs-you rendered as bare grey text
   and .ix-count as a bare number. A defence that strips the very
   components it exists to protect is worse than no defence, because
   it fails silently and only inside the surfaces that adopted it.

   Every ix component that paints is now qualified with [class],
   lifting it to (0,2,0) — a tie with the reset, resolved by source
   order since they are declared after it in this same file. Any ix
   component added later MUST carry [class] for the same reason.
   ── */

/* The scope root is itself a descendant of the wrapper, so it needs
   its own (0,2,0) to keep a background. */
.ix-surface-scope[class] {
  background-color: var(--ix-cream) !important;
}

/* Opt-out for a subtree that must keep whatever a parent stylesheet
   gives it — images, embeds, third-party widgets. */
.ix-surface-scope[class] .ix-keep-bg,
.ix-surface-scope[class] .ix-keep-bg * {
  background-color: revert !important;
  background-image: revert !important;
}

/* ============================================================
   TEXT-ON-DARK
   ============================================================
   Any element sitting on teal or salmon needs inverted text, and
   inherited colour is exactly what fails here — see the --ix-teal
   note at the top. Marking the container is more reliable than
   remembering to set colour on every child.
   ============================================================ */

.ix-on-dark,
.ix-on-dark * {
  color: var(--ix-text-invert);
}
.ix-on-dark .ix-muted {
  color: rgba(255, 255, 255, 0.55);
}

/* ============================================================
   ACCENT SPINE
   ============================================================
   A 3px left rail carrying scope or state. Established on the
   Cadence Board's issue cards and promoted here so obligations,
   intake rows, defect lists and anything else with a "state you can
   read down the edge" uses the same vocabulary.

   --now is the salmon one. It is the only spine that means
   "waiting on you", and it should be rare on any given screen.
   ============================================================ */

.ix-spine            { border-left: 3px solid var(--ix-border); }
.ix-spine--now       { border-left-color: var(--ix-salmon); }
.ix-spine--pending   { border-left-color: var(--ix-gold); }
.ix-spine--done      { border-left-color: var(--ix-teal); }
.ix-spine--info      { border-left-color: var(--ix-blue); }

/* ============================================================
   NEEDS-YOU BADGE
   ============================================================
   The salmon pill. NEXT, OVERDUE, BLOCKED, ACTION NEEDED.
   Mono, uppercase, tight — it reads as a system marker rather than
   a word someone typed. Text colour is set explicitly because
   inheriting it is what breaks.
   ============================================================ */

.ix-needs-you[class] {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  font-family: var(--ix-font-mono);
  font-size: 8.5px;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: var(--ix-radius-sharp);
  background-color: var(--ix-salmon) !important;
  color: var(--ix-text-invert) !important;
  white-space: nowrap;
}

/* ============================================================
   SECTION TITLE
   ============================================================
   The display face, used with restraint. Panel and column titles
   across T-A — Fraunces at a size that lets the eye land on the
   name before the chrome.
   ============================================================ */

.ix-section-title {
  font-family: var(--ix-font-display);
  font-size: 21px;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -.015em;
  color: var(--ix-teal);
  white-space: nowrap;
}
.ix-section-title--sm { font-size: 17px; }
.ix-section-title--lg { font-size: 26px; }

/* A count beside a section title. Mono, so it reads as data. */
.ix-count[class] {
  font-family: var(--ix-font-mono);
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1;
  color: var(--ix-text-light);
  background-color: var(--ix-cream) !important;
  border-radius: var(--ix-radius-pill);
  padding: 4px 8px;
}

@media (prefers-reduced-motion: reduce) {
  :root { --ix-ease: 0s; }
}
