/* ============================================================================
   POST FACTORY - THE TOKEN CONTRACT.

   One table. Every stylesheet reads from it, and a raw hex, px or ms value
   anywhere outside this file is a bug that ui-lint fails the build for.

   This replaces the :root block that lived inside index.html's <style>. Same
   idea, one improvement: the contract is now a FILE, so "which stylesheet may
   type a number" has a one-word answer instead of a line range.

   THREE THINGS THIS FILE DOES DIFFERENTLY FROM A NORMAL TOKEN SHEET, each
   because the studio is white-label and the alternative would break it:

   1. COLOUR IS A ROLE HERE, NOT A HEX. The contract names one accent, one
      danger, one warning, one success and a 7-step neutral - that discipline is
      the point and it holds. But the VALUES arrive from the brand pack at boot
      (app.js SHELL_CSS), because the studio ships to more than one company and
      shell-contrast.js measures every role against every ground for all three
      packs. Hard-coding one company's blue here would delete that. So the
      accent is declared as the role it plays and resolved from the pack.

   2. THE NEUTRAL RAMP IS DERIVED, NOT LISTED. The pack supplies two neutral
      inks (--ink, --muted) and three surfaces. The contract wants four inks and
      four surfaces, so the missing rungs are composed with color-mix from the
      pack's own values rather than typed. A derivation follows a brand swap; a
      literal does not. The mixes land within ~2 L* of the contract's stated
      greys on the default pack - checked, not guessed.

   3. MOTION IS THE INTERFACE'S OWN CLOCK. It used to be motion.js's, on the
      theory that the posts and the room they are made in should move together.
      That was overreach: a post's reveal genuinely wants 320ms, and a hover
      that takes 320ms is just slow. The posts keep motion.js. The interface
      gets 120/180 here, and motion-lint still owns the other side.

   WHAT IS IN USE, AND WHAT IS STILL AN ARGUMENT. The stylesheet reads this
   file and nothing else - the old vocabulary is gone, so every geometry rung,
   both lifts, the scrim and the motion pair are load-bearing and measured.

   Still unexercised - counted, not guessed:
     --t-meta   nothing is mono yet. It waits for the field overline, and it is
                the reason the sweep's type floor is 11 rather than 12.
     --good     nothing says "ready" in the contract's voice. The status line
                marks it in the ACCENT, which this file's own colour-jobs
                forbid - accent is selection and primary action, nothing else.
     --h-sm     no control is 32 yet; they are all --h-lg or --h-md.
     --t-h1     no screen has a first-level heading. The product name is --t-h2,
                because promoting it to 28px changes the measured header height
                and therefore the whole grid - a layout decision, not a
                substitution.
     --ink-700 --ink-300 --accent-press --accent-edge --danger-wash
     --danger-edge --warn-wash --inset --w-reg --w-med --w-semi --w-bold
                declared, not yet read. The ramp's two DERIVED rungs are both
                here: components use ink-900 and ink-500, which are the pack's
                own two inks, so nothing has yet asked color-mix for a step the
                pack does not supply. The derivations are therefore still
                unproven against a real ground on a real pack.

   TWO COLLISIONS THIS FILE HAS NOT RESOLVED, and both are the pack running out
   of values rather than the contract being wrong:

     --surface-3 AND --accent-wash both resolve to the pack's --ice. The
     contract wants a warm neutral for the fourth surface and a blue tint for
     the accent wash - two different colours - and the pack supplies one tint.
     So the neutral ramp's top step is currently a brand tint, which is why
     --ice is still read directly in five places instead of through a role: a
     placeholder canvas and a selected control would otherwise claim the same
     token for opposite jobs.

     --stroke AND --rule are a fourth and fifth edge weight. The contract
     states three - edge, edge-strong, edge-soft - and the shell uses five.
     Neither extra is lighter than edge-soft, so nothing is invisible, but
     "three weights, three jobs" is not true yet.

   And one structural fact worth stating plainly: --h-lg is the ONLY rung that
   clears the 40px touch floor, so a phone gets one control height and the
   other two steps are desktop-only by construction.
   ========================================================================== */

:root {
  /* ==== PRE-BOOT: NEUTRAL, NOT SOMEBODY'S BRAND =============================
     app.js overwrites every one of these from PACK before the first paint that
     matters, so their only job is the frame in between. They used to be
     Talenlio's own colours - which meant a customer's studio flashed another
     company's blue on the way in. Greys cannot be the wrong brand.
     These are the ONLY literals permitted in this block, and pack-lint holds
     them to being neutral. ==================================================*/
  --paper:#f4f5f8; --surface:#fff; --ink:#1a1c22; --muted:#5c6070;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --brand:#4a4f5e; --brandInk:#3a3f4c; --deep:#333742; --night:#15171d; --ice:#e9eaee;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --surface-paper:#fff; --surface-primary:#4a4f5e; --surface-tint:#e9eaee; --surface-deep:#15171d;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --pack-b400:#6b7182; --pack-b500:#4a4f5e; --pack-night:#15171d; --pack-tint:#e9eaee; --pack-white:#fff;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --line:#dcdee4; --edge:#9aa0ae; --amber:#a8791b; --bad:#8d2f2a;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --scrim-rgb:20,22,28;  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --sunk:#f1f2f5; --rule:rgba(20,22,28,.07); --stroke:rgba(20,22,28,.11); --stroke-hover:rgba(20,22,28,.2);  /* pack-lint-ok: brand-neutral pre-boot fallback */
  --shadow-stage:0 1px 1px rgba(20,22,28,.04),0 2px 6px rgba(20,22,28,.05),0 24px 48px -16px rgba(20,22,28,.16);  /* pack-lint-ok: brand-neutral pre-boot fallback */

  /* ==== NEUTRAL - 7 steps, warm. There is no eighth grey. ===================
     Four inks and four surfaces. The pack supplies ink-900, ink-500 and all
     three of its own surfaces; the two missing inks are mixed from them, so a
     dark-brand pack darkens the whole ramp instead of keeping two fixed greys.

     ink-300 is ~3:1 on white. It is a NON-TEXT role - a disabled glyph, an
     inactive dot - and the contract's 4.5:1 floor means it may never carry a
     word. ==================================================================*/
  --ink-900:var(--ink);
  --ink-700:color-mix(in srgb, var(--ink) 82%, var(--surface));
  --ink-500:var(--muted);
  --ink-300:color-mix(in srgb, var(--muted) 64%, var(--surface));
  --surface-0:var(--surface);
  --surface-1:var(--paper);
  --surface-2:var(--sunk);
  --surface-3:var(--ice);

  /* ==== EDGES - borders must be visible or they are not borders =============
     Three weights, three jobs, and the reason the interface stopped needing a
     shadow to prove a box exists. --edge is the pack's MEASURED 3:1 control
     boundary and arrives at boot; the other two are composed from the pack's
     scrim so they darken with a dark brand.
       edge         a control's own boundary - 3:1, the one that must be read
       edge-strong  that boundary under the pointer
       edge-soft    a divider between sections - seen, not read. THE FLOOR.
     Nothing lighter than edge-soft is a border. A #F1F1F1 hairline on white is
     1.06:1 - visually absent - which is what forces a component to lean on a
     shadow to exist, and is the root cause of "not solid". ==================*/
  --edge-strong:var(--stroke-hover);
  --edge-soft:var(--line);

  /* ==== MEANING - one accent, one danger, one warning, one success ==========
     No fifth colour. Colour has jobs and decoration is not one of them, and
     none of these four may be the ONLY carrier of a meaning - each pairs with a
     word, a position or a mark.
       accent  selection and the primary action, and nothing else
       danger  blocked
       warn    approaching a limit
       good    ready

     TWO OF THE FOUR ARE BORROWED, AND SAYING SO IS THE POINT.

     --warn reads the pack's --amber, which is the SAME ink the post draws its
     underline in. The contract's own instruction is that amber belongs to the
     post and not to the interface, so this is a role sharing a value with a
     role it should be distinct from - not a separation, a coincidence that the
     packs happen to survive. It is honest about it rather than dressed up.

     --good is worse: no pack supplies a success ink at all, so it is the one
     literal in this section. That reads acceptably on a blue brand and will
     read foreign on a warm one - a cold forest green marking "ready" in an
     amber studio is exactly the kind of thing a brand swap is supposed to
     prevent.

     Both are the same missing piece: the pack layer owes the shell a measured
     warn and a measured good, next to the controlEdge and error it already
     supplies. When it has them, these two lines become var() like the others
     and shell-contrast starts measuring them per pack. Until then they are
     named debt, not a solved problem. ======================================*/
  --accent:var(--brand);
  --accent-press:var(--night);
  --accent-wash:var(--ice);
  --accent-edge:color-mix(in srgb, var(--accent) 34%, var(--surface));
  --danger:var(--bad);
  --danger-wash:color-mix(in srgb, var(--danger) 6%, var(--surface));
  --danger-edge:color-mix(in srgb, var(--danger) 26%, var(--surface));
  --warn:var(--amber);
  --warn-wash:color-mix(in srgb, var(--warn) 9%, var(--surface));
  --good:#1B7048;  /* pack-lint-ok: semantic success, not identity - see note above */

  /* A LABEL ON A PHOTO NEEDS A GROUND, and it is one ground at one strength.
     Three components each picked their own - .65, .72 and .75 - which is three
     answers to a question nobody asked twice, and the sort of thing that starts
     at three values and ends at fifteen. Composed from the pack's scrim so it
     darkens with a dark brand. Not an elevation: it is the surface a caption
     sits ON when the surface underneath is somebody's photograph. */
  --scrim:rgba(var(--scrim-rgb), .72);

  /* ==== RADIUS - 3 steps, small. r-3 is only for the outermost surface. =====
     Tighter than what this shipped with (6/8/12). A tool for making exact
     artefacts should read as machined, and 16 or 20 reads as consumer app.
     The pill is reserved for the two controls that are genuinely pills: a
     switch track and a progress bar. ========================================*/
  --r-1:4px; --r-2:6px; --r-3:10px; --r-pill:999px;

  /* ==== SPACE - 4px base. There is no 10, 14, 18, 22. ======================
     Seven steps. Section rhythm uses exactly three of them and nothing else:
     --s-5 between sections, --s-3 between rows inside one, --s-2 between a
     label and its control. ==================================================*/
  --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px; --s-6:32px; --s-7:48px;

  /* ==== TYPE - 7 sizes with fixed roles ====================================
       meta  mono ONLY: counters, dimensions, group labels, the field overline
       cap   the smallest proportional label
       ui    every label and every control - the working size
       body  running text: hints, notes, the studio's quiet voice
       h3    a field's typed content: the user's words are the product
       h2    a section title
       h1    once per screen at most

     META AND CAP ARE NOT TWO RUNGS. They are one rung in two typefaces, which
     is why half a pixel separates them and why that is not the rounding error
     it looks like: a mono face at 11px sets optically larger than a
     proportional face at 11px, because its glyphs are wider and its x-height
     sits higher in the em. The half pixel is the compensation that makes a
     mono counter and a sans label read as the same size on the same row. Pick
     between them by TYPEFACE - mono is meta, proportional is cap - never by
     wanting something slightly smaller. Six perceptual sizes, seven names.

     ONE NAMED EXCEPTION. The contract says --t-meta:10.5px. The width sweep
     fails any type under 11px, so 10.5 could not pass the studio's own gate.
     11px is the smallest legal value, so that is what it is - and the pairing
     above survives it, because 11 mono against 11.5 proportional keeps the
     same optical relationship 10.5 against 11.5 had. The 0.5px is the whole
     deviation, and it is upward. ============================================*/
  /* THE MONO FACE. --t-meta is a mono-only rung and the contract never named a
     family for it, so for two sessions "mono only" was a rule nothing followed.
     A SYSTEM stack, deliberately: it is the one font guaranteed present, it
     costs no request on a studio that already waits for one webfont before its
     first paint, and it is brand-neutral - a pack chooses the voice the POSTS
     speak, and this is the voice the instrument speaks. It is also the honest
     way to get fixed-width digits: font-variant-numeric asks a proportional
     face to fake a monospace, and a face without the feature simply declines. */
  --face-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  --t-meta:11px;   /* contract says 10.5px - see note: 11 is the sweep's floor */
  --t-cap:11.5px; --t-ui:13px; --t-body:14px;
  --t-h3:16px; --t-h2:20px; --t-h1:28px;
  --w-reg:400; --w-med:550; --w-semi:650; --w-bold:750;

  /* ==== CONTROL HEIGHT - every interactive element snaps to one of three ====
     TWO NAMED EXCEPTIONS, both forced by the contract's own accessibility
     floor: interactive targets are at least 32px on desktop and 40px on touch.

       stated 28 -> 32   28px is below the desktop floor. It cannot ship.
       stated 32 -> 36   with sm clamped to 32, keeping md at 32 would leave
                         two real rungs wearing three names. 36 is the next
                         step on the 4px grid and keeps the ladder honest.

     --h-lg is 40 exactly as stated, which is also the touch floor - so on a
     phone every control takes --h-lg and the floor is met by construction
     rather than by a mobile-only override. ==================================*/
  --h-sm:32px;   /* contract says 28px - below the 32px desktop target floor */
  --h-md:36px;   /* contract says 32px - see note: 32/32/40 is a 2-rung ladder */
  --h-lg:40px;

  /* ==== ELEVATION - exactly two. Nothing else lifts. =======================
     lift-1 is contact: a thing resting on the surface below it. lift-2 is
     separation: a thing over the page, and only the picker and the Look panel
     are that. --inset is the recess a track or a well sits in.
     Composed from the pack's scrim rather than a fixed near-black, so a dark
     brand casts its own shadow. The alphas are the contract's. ==============*/
  --lift-1:0 1px 2px rgba(var(--scrim-rgb), .06);
  --lift-2:0 8px 24px rgba(var(--scrim-rgb), .12), 0 2px 6px rgba(var(--scrim-rgb), .06);
  --inset:inset 0 1px 1px rgba(var(--scrim-rgb), .04);
  /* AND ONE MORE, FOR THE ONE THING THAT IS NOT CHROME. Two lifts govern the
     interface. The post is not interface - it is the artefact the studio
     exists to produce, and it is the only object on screen that has to read as
     a physical thing lying on a surface. That takes a contact edge so it sits
     rather than floats, and a wide soft cast so the ground reads as BEHIND it;
     --lift-2 at 8px/24px makes it look pasted on instead.

     It is not a general elevation and ui-lint will not let it be used as one:
     the rule allows --stage on #preview and nowhere else. If a second element
     ever wants it, that element is competing with the post, which is the one
     thing Part 6's priority order forbids. Composed from the pack's own key,
     so a brand swap recolours the cast with everything else. */
  --stage:var(--shadow-stage);

  /* ==== MOTION - two durations, one curve ==================================
     The duration and the curve travel together, so a transition reads
     `var(--fast)` and cannot arrive with the wrong easing. Faster than the
     320ms this inherited from the post renderer: the interface is worked in
     continuously, and 180ms is the longest a control should take to answer.
     prefers-reduced-motion switches both off at the bottom of index.html. ===*/
  --fast:120ms cubic-bezier(.2,.6,.2,1);
  --base:180ms cubic-bezier(.2,.6,.2,1);

  /* ==== WRITTEN AT RUNTIME =================================================
     ui.js measures the header and the room actually left for the post. These
     are the values for the frame before it runs; nothing else may type them.
     NOT here: --bar, the phone export bar's reserve. It takes a different
     value per breakpoint, so it is a layout variable rather than a token, and
     it stays declared with THE LAYOUT where its overrides can be read beside
     it. A token that changes at 1024px is not a token. ======================*/
  --header-h:57px; --stage-h:60vh; --stage-w:100%;

}
