/* ============================================================
   a2e — design tokens
   sRGB hex is the source of truth.

   Palette v2, 2026-07-30. Seven brand colours across four hue
   families. The v1 set (amber/orange/cyan/mint/peri/sky) had no
   magenta family at all, and it forced every bright colour to
   carry the same text colour — which in turn forced them all into
   one luminance band, which is why no two of them could sit side
   by side.

   Four measured decisions behind this file:

   1. mint is the accent. It is the brightest colour in the set
      (0.753) and reads best on dark ground: 15.51 on --bg,
      11.99 on --surface. The previous accent (sky) gives 13.67 / 10.56.

   2. Text colour is no longer one rule. Six colours carry --c-navy;
      violet carries --c-text. The split sits near luminance 0.2.
      Pretending otherwise is what kept the palette flat.

   3. Gradients are one-directional. Mixing toward white AND black
      pulls both ends away from the base colour, so the worst
      contrast point lands at an end — and on mid-luminance colours
      (rose/violet/peri) BOTH text colours fail at once. Mixing in
      one direction only anchors the worst point at the base colour,
      which makes gradient amplitude independent of readability.
      Bright colours lighten upward and carry navy; violet darkens
      downward and carries body text.

   4. violet is #6A43D9, which is #7C4DFF darkened 16%. The original
      cleared neither text colour (navy 3.84, text 4.03). That was
      never a gradient problem — the hex itself could not carry text.

   Layer note: this file assigns its own layers, so import it
   WITHOUT a layer() wrapper. Tokens land in a2e-tokens, utilities
   in a2e-utilities. Wrapping the import would nest both.
   ============================================================ */

/* @font-face 不参与层叠，不必分层。放在这里是为了让「引入 tokens.css」
   一步到位拿到字体——字体族名写在下面 --font-* 里，声明却在另一个文件，
   分开引很容易只引一半。 */
@import url("fonts.css");

@layer a2e-tokens {
  :root {
    /* ---- brand palette ----
       Contrast figures are against the colour's own text colour. */
    --c-mint:   #41FFA4;   /* 0.753 · accent   · navy 14.14 AAA */
    --c-amber:  #FFD15C;   /* 0.676 · warn     · navy 12.79 AAA */
    --c-sky:    #6AE4FF;   /* 0.658 · success  · navy 12.46 AAA */
    --c-orange: #FF9859;   /* 0.444 · unbound  · navy  8.71 AAA */
    --c-peri:   #6A9FFF;   /* 0.351 · info     · navy  7.06 AAA */
    --c-rose:   #FF4D6D;   /* 0.277 · danger   · navy  5.75 AA  */
    --c-violet: #6A43D9;   /* 0.121 · alt      · text  5.15 AA  */

    --c-navy:     #061133;  /* 0.007 · text colour for the six bright ones */
    --c-navy-mid: #233584;  /* 0.046 · depth gradient, middle stop */

    /* !! Two text-colour groups, not one rule:
          mint/amber/sky/orange/peri/rose  →  --c-navy
          violet                           →  --c-text
          Only violet clears 3:1 against the bright colours
          (mint 4.70 / sky 4.14 / amber 4.25), so it is also the
          only colour that may sit directly beside them. The other
          18 bright pairs run 1.03–2.46 and still need dark ground
          between them.

       !! violet is background-only. As coloured text on dark ground it
          gives 3.30 on --bg and 2.55 on --surface — neither clears AA.
          The other six all work as dark-ground text (rose is the
          floor at 6.31 / 4.88). */

    /* ---- neutral ramp ----
       Three levels, and the gaps between them are the point. Deep-dark
       UI has very little room: --bg to --surface is only 1.29 and that
       is already as much as this range allows. Deriving --surface from
       --c-void as well (e.g. black 20%) gives 1.03 — the panel then
       vanishes into the page. Measured, not guessed.

       The ramp is a true grey: R=G=B at every step. It was blue-tinted
       (#0B0D14 / #1E2331 / #E8EBF2 / #98A0B8) through v2.0. Each step was
       replaced by the grey of the SAME relative luminance, so every
       contrast figure in this file is unchanged — the substitution moves
       each ratio by less than 0.05, i.e. below the precision they are
       quoted at. Re-verified, not assumed.

       !! Keep them equal-luminance. Picking a "nicer" grey by eye is what
       would actually break the numbers.

       The blue is now only where it is meant: --c-navy, which is the
       text colour riding on the bright brand colours, and the depth
       gradient. Neutral surfaces no longer compete with the accent. */
    --c-ink:   color-mix(in srgb, var(--c-void), black 55%);  /* #060606 · page ground */
    --c-void:  #0D0D0D;    /* gradient stops and glass tint derive from this */
    --c-panel: #232323;    /* raised surface */
    --c-text:  #EBEBEB;
    --c-mute:  #A1A1A1;

    /* ---- semantic ---- */
    --bg:        var(--c-ink);     /* text 16.99 · mute 7.78 */
    --surface:   var(--c-panel);   /* text 13.13 · mute 6.01 · vs --bg 1.29 */
    --surface-2: color-mix(in srgb, var(--c-panel), var(--c-text) 6%);
                                   /* #2F2F2F · text 11.19 · mute 5.12 · vs --surface 1.17 */
    --text:      var(--c-text);
    --text-mute: var(--c-mute);

    --accent:  var(--c-mint);   --accent-on:  var(--c-navy);   /* 14.14 AAA */
    --success: var(--c-sky);    --success-on: var(--c-navy);   /* 12.46 AAA */
    --warn:    var(--c-amber);  --warn-on:    var(--c-navy);   /* 12.79 AAA */
    --danger:  var(--c-rose);   --danger-on:  var(--c-navy);   /*  5.75 AA  */
    --info:    var(--c-peri);   --info-on:    var(--c-navy);   /*  7.06 AAA */
    --alt:     var(--c-violet); --alt-on:     var(--c-text);   /*  5.15 AA  */

    /* orange carries no semantic. It is for category colour and
       chart series, where a sixth hue is needed but no state is meant. */

    /* ---- neutral line ----
       Structural dividers and borders. Separate from --glass-border,
       which is accent-tinted and belongs to glass panels only. */
    --line:      color-mix(in srgb, transparent, var(--c-text) 12%);
    --line-soft: color-mix(in srgb, transparent, var(--c-text) 7%);

    /* ---- opaque elevation ----
       --glass-shadow belongs to glass panels and is tinted with --c-navy.
       An opaque popover (dropdown menu, popover) needs its own, and it has
       to be neutral now that the ramp is: a navy-tinted shadow sitting on a
       grey panel reads as a colour cast. Black, not --c-navy.

       Shadow is load-bearing here, not decoration: --surface on --bg is
       1.29, so a floating panel cannot be told from the page by fill alone.
       It always ships with the --line ring as well.

       --z-pop is the one stacking level the component layer owns. Anything
       above it (modal, toast) is composited by the app shell, which is why
       no --z-modal exists — inventing one here would be a guess about a
       stack this file cannot see. */
    --shadow-pop: 0 12px 32px color-mix(in srgb, transparent, black 55%);
    --z-pop: 20;  /* @kind other */

    /* ---- one-directional tonal gradient ----
       Amplitude is free to change: the worst contrast point is
       anchored at the base colour, not at a gradient end.
       Reference designs measure ~24%; 30% reads slightly stronger. */
    --tone-amt: 30%;
    /* Direction of the tonal gradient. 180deg suits vertical faces;
       .a2e-tone--x restates 90deg for horizontal tracks. */
    --tone-angle: 180deg;  /* @kind other */

    /* ---- cross-hue gradients ----
       Three usable, plus depth. --grad-fresh is mint→sky since cyan
       left the palette. The magenta family cannot form one:
       rose→violet spans 149 levels per channel and clears neither
       text colour (navy 3.01 / text 2.69), so rose and violet only
       ever take tonal gradients.

       All four are 180deg. A horizontal cross-hue face needs a separate
       90deg token — direction cannot be made a parameter, because var()
       inside a custom property is substituted where the property is
       *declared*, not where it is used: a descendant redefining some
       --grad-dir never reaches these. No such consumer exists today, so
       no -x tokens are declared. */
    --grad-warm:  linear-gradient(180deg, var(--c-amber), var(--c-orange));  /* navy  8.71 */
    --grad-fresh: linear-gradient(180deg, var(--c-mint),  var(--c-sky));     /* navy 12.46 */
    --grad-cool:  linear-gradient(180deg, var(--c-peri),  var(--c-sky));     /* navy  7.06 */
    --grad-depth: linear-gradient(180deg, var(--c-navy), var(--c-navy-mid), var(--c-sky));
    /* --grad-depth carries no text: it spans luminance 0.007→0.658
       and no single text colour clears AA across it. */

    /* ---- radius ----
       Revised 2026-08-03. The previous rule was "every component face is
       --r-xs, the larger steps are unused". The landing-page design
       (A2E Nav.dc.html) does not follow it and was not going to: counted
       over that file, --r-sm appears 49 times, --r-md 33, --r-lg 7, and
       --r-xs only on gallery tiles. The rule is now the semantic ladder
       the design actually uses.

       The ladder is by ELEMENT SIZE, not by nesting depth: the smaller
       the face, the smaller the radius. A 34px chip with an 18px radius
       reads as a pill; a 340px panel with a 6px radius reads as a box. */
    --r-xs:   6px;    /* image tiles inside a gallery — the one place a
                         near-square corner is wanted, so adjacent tiles
                         read as one grid rather than as loose cards    */
    --r-sm:  12px;    /* buttons(non-pill), inputs, tabs, badges, chips,
                         menu items, small labels                      */
    --r-md:  18px;    /* cards, feature tiles, floating cards, avatar
                         wall tiles                                    */
    --r-lg:  26px;    /* large panels, dropdown menus, sheets, the
                         showreel frame                                */
    --r-xl:  34px;    /* app icon, hero, oversized tiles               */
    --r-full: 9999px; /* pills, avatars, switch tracks, dots           */

    /* ---- spacing (4px base) ---- */
    --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
    --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;

    /* ---- type ---- */
    --font-sans:    "Lexend Deca", "Noto Sans SC", system-ui, sans-serif;
    --font-display: "Quicksand", "Noto Sans SC", system-ui, sans-serif;
    --font-num:     "JetBrains Mono", ui-monospace, monospace;

    --lh-tight: 1.25;  /* @kind font */
    --lh-body:  1.65;  /* @kind font */   /* explicit — never rely on `normal` in CJK+Latin mixed text */

    --fs-xs: .75rem;  --fs-sm: .875rem; --fs-base: 1rem;   --fs-md: 1.125rem;
    --fs-lg: 1.25rem; --fs-xl: 1.5rem;  --fs-2xl: 1.875rem;
    --fs-3xl: 2.375rem; --fs-4xl: 3rem; --fs-5xl: 3.75rem;

    /* ---- glow ----
       Measured ceiling: at 18% the halo still leaves --text-mute at
       5.31 (AA) for anything sitting on it. 25% drops it to 4.24 —
       0.26 short. --text survives even 35% (6.56), so the binding
       constraint is secondary text, not body text. */
    --glow:       0 0 64px color-mix(in srgb, transparent, var(--accent) 18%);
    --glow-tight: 0 0 28px color-mix(in srgb, transparent, var(--accent) 22%);

    /* ---- frosted glass ----
       Tint sits at 80% rather than v1's 76% to buy room for the
       accent wash: 80% + 8% wash = 7.48 worst case (AAA). A 76%
       tint cannot hold AAA under any wash — 6% already drops to 6.80.
       Worst case is always a pure-white backdrop. */
    --glass-tint:   80%;
    --glass-wash:    8%;
    --glass-bg:
      linear-gradient(180deg,
        color-mix(in srgb, transparent, var(--c-mint) var(--glass-wash)),
        color-mix(in srgb, transparent, var(--c-sky)  var(--glass-wash))),
      linear-gradient(180deg,
        color-mix(in srgb, transparent, var(--c-void) var(--glass-tint)),
        color-mix(in srgb, transparent, var(--c-void) var(--glass-tint)));
    --glass-blur:   20px;
    --glass-sat:    140%;
    --glass-border: color-mix(in srgb, transparent, var(--accent) 22%);
    --glass-lift:   inset 0 1px 0 color-mix(in srgb, transparent, var(--accent) 16%);
    --glass-shadow: 0 8px 32px color-mix(in srgb, transparent, var(--c-navy) 55%);
  }
}

/* ============================================================
   Utilities. This layer is no longer empty — the tonal gradient
   turned out to be the one declaration that repeats everywhere.
   ============================================================ */
@layer a2e-utilities {

  /* One-directional tonal gradient. --tone is the colour,
     --tone-amt the amplitude.

     .a2e-tone      bright colours: lighten upward, base at the
                    bottom → carries --c-navy
     .a2e-tone--dk  dark colours:   base at the top, darken
                    downward → carries --c-text

     Uses background-image so a component can still set its own
     background-color as an opaque fallback underneath. */
  .a2e-tone {
    background-image: linear-gradient(var(--tone-angle),
      color-mix(in srgb, var(--tone, var(--accent)), white var(--tone-amt)),
      var(--tone, var(--accent)));
  }

  .a2e-tone--dk {
    background-image: linear-gradient(var(--tone-angle),
      var(--tone, var(--alt)),
      color-mix(in srgb, var(--tone, var(--alt)), black var(--tone-amt)));
  }

  /* Horizontal elements (progress fills, bars) need 90deg — a 180deg
     gradient is invisible on a 10px-tall track. The angle is restated
     rather than rewritten through --tone-angle: a custom property set
     under a utility class is a class-local, not a design-system token,
     and the token registry rejects it. --tone-angle stays in :root. */
  .a2e-tone--x {
    background-image: linear-gradient(90deg,
      color-mix(in srgb, var(--tone, var(--accent)), white var(--tone-amt)),
      var(--tone, var(--accent)));
  }

  .a2e-tone--dk.a2e-tone--x {
    background-image: linear-gradient(90deg,
      var(--tone, var(--alt)),
      color-mix(in srgb, var(--tone, var(--alt)), black var(--tone-amt)));
  }

  /* Frosted glass. Both fallback branches rewrite custom properties
     rather than concrete declarations, so a component can compose
     this class and inherit them instead of restating them. */
  .a2e-glass {
    background: var(--g-bg, var(--glass-bg));
    -webkit-backdrop-filter: var(--g-filter, blur(var(--glass-blur)) saturate(var(--glass-sat)));
            backdrop-filter: var(--g-filter, blur(var(--glass-blur)) saturate(var(--glass-sat)));
    border: 1px solid var(--g-edge, var(--glass-border));
    box-shadow: var(--g-lift, var(--glass-lift)), var(--glass-shadow);
    /* 玻璃永远铺在面板尺度的东西上（导航条、弹层、Toast），不是按钮
       尺度，所以跟卡片同档 --r-md。2026-08-03 从 --r-xs 改回来。 */
    border-radius: var(--r-md);
  }

  /* Required for accessibility, and it is also what older browsers
     get — without it they render a see-through, unreadable panel. */
  @media (prefers-reduced-transparency: reduce) {
    .a2e-glass {
      --g-bg: var(--surface);
      --g-filter: none;  /* @kind other */
    }
  }
  @supports not (backdrop-filter: blur(1px)) {
    .a2e-glass {
      --g-bg: var(--surface);
      --g-filter: none;  /* @kind other */
    }
  }

  /* Gradient border via padding-box/border-box. 1px has no banding,
     which makes this the cheapest way to use a cross-hue gradient. */
  .a2e-edge {
    border: 1px solid transparent;
    background:
      linear-gradient(var(--edge-fill, var(--surface)), var(--edge-fill, var(--surface))) padding-box,
      var(--edge-grad, var(--grad-fresh)) border-box;
  }

  /* Grain. 8-bit gradients step every 4–5px on faces over ~320px,
     which reads as banding. This breaks the steps up. */
  .a2e-grain { position: relative; }
  .a2e-grain::after {
    content: "";
    position: absolute;
    inset: 0;
    opacity: .16;
    pointer-events: none;
    border-radius: inherit;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  }

  /* Numeric data: credit balances, usage & billing tables, queue
     progress, player timecodes, media specs. */
  .a2e-num {
    font-family: var(--font-num);
    font-size: .96em;            /* JetBrains x-height .550 vs Lexend .525 */
    font-variant-numeric: tabular-nums;
  }

  /* Quicksand runs ~3% small beside Lexend Deca. */
  .a2e-display { font-family: var(--font-display); font-size: 1.03em; }
}
