/* ═══════════════════════════════════════════
   JETONOMY DESIGN TOKENS
   The ONE place every --jt-* custom property is declared.
   Theme-adaptive: inherits from the host theme / WP theme.json,
   falls back to neutral defaults.
   ═══════════════════════════════════════════

   Why this is its own stylesheet, loaded as handle `jetonomy-tokens`:

   Blocks (Login, Navigation, Feed, Compose) render on ANY page — a
   page-builder canvas, a widget area, a landing page — while jetonomy.css
   only loads on /community/ routes. The tokens therefore cannot live in
   jetonomy.css, or a block on a landing page renders unstyled. They used to
   be duplicated into blocks.css as a parallel --jtb-* layer for exactly this
   reason; that duplication is what this file replaces. Both `jetonomy` and
   `jetonomy-blocks` declare this handle as a dependency, so there is one
   declaration and it reaches every page a Jetonomy surface can appear on —
   and a landing page with one login widget does not download the ~90KB
   community stylesheet to get its colours.

   DECLARE ON :root ONLY — never `:root, .jt-app`.

   :root is <html>, an ancestor of every element, so adding `.jt-app` buys no
   extra reach. What it DOES buy is a bug: a .jt-app nested inside another
   .jt-app (the Login block carries the class and can render inside the
   community sidebar) RE-DECLARES the whole token set at its own scope instead
   of inheriting it. Same reasoning for dark mode: reassign on the class the
   host theme puts on <body> so every descendant inherits, with no per-block,
   per-component rule anywhere. Adding a component to this file's selector list
   is always the wrong fix — see the CSS Token Rules in CLAUDE.md. */

  :root {
    /* Typography — BuddyNext first, then WP theme.json, then inherit for classic themes */
    --jt-font: var(--font-body, var(--wp--preset--font-family--body, inherit));
    --jt-font-heading: var(--font-display, var(--wp--preset--font-family--heading, inherit));
    --jt-font-mono: var(--wp--preset--font-family--monospace, ui-monospace, SFMono-Regular, Menlo, monospace);

    /* Colors — adopt the active theme's own brand token first so the plugin
       reads as native (and follows the theme's dark mode automatically when the
       theme flips its tokens), then WP theme.json, then the popular page-builder
       themes, then a neutral fallback. Verified host-theme brand-token map:
         BuddyX / BuddyX Pro 5.1+ → --bx-color-accent (flip on [data-bx-mode="dark"])
         Reign 8.0+               → --reign-colors-theme
         BuddyNext                → --brand
         Any "primary"-slug theme → --wp--preset--color--primary (TT3, classic themes)
         Astra                    → --ast-global-color-0
         Kadence                  → --global-palette1
         Blocksy                  → --theme-palette-color-1
         GeneratePress / "accent" → --wp--preset--color--accent
       Canonical recipe: wp-plugin-development skill, ux-foundation
       "Host-Theme Token Adoption". A var() chain picks the first DEFINED token,
       and these are mutually exclusive per active theme, so order only sets
       precedence for the rare theme exposing two. */
    /* Adopt the host theme's brand colour. Owners cannot be made to run BuddyX or
       Reign, so the chain walks the tokens the popular themes actually expose,
       then WP's own presets, then the Appearance -> Accent default. An explicit
       accent in Appearance is emitted inline by palette_css() and outranks all
       of this. Verified against 11 themes; --nv-primary-accent (Neve) was being
       missed, which is why Neve sites rendered the fallback instead of their brand. */
    --jt-accent: var(--bx-color-accent, var(--reign-colors-theme, var(--brand, var(--wp--preset--color--primary, var(--ast-global-color-0, var(--global-palette1, var(--theme-palette-color-1, var(--nv-primary-accent, var(--wp--preset--color--accent, #0073aa)))))))));

    /* Readable text ON --jt-accent, derived from whatever colour we ended up
       adopting. This CANNOT be computed in PHP: --jt-accent usually resolves to a
       theme token the browser only learns at runtime, so the server never sees it.
       Hardcoding white failed WCAG AA on 7 of 11 themes measured — including Reign,
       whose lavender put white text at 2.13 on the "Create free account" button.
       Pull L out of the accent, snap it past the threshold, chroma 0 => pure
       black/white. 0.57 is not a guess: sweeping 0.40–0.80 against the real
       accents of 11 themes, 0.56–0.58 is the only band where all 11 clear AA, and
       0.57 centres it for themes we have not seen. Older engines keep the plain
       white fallback below (no worse than today). */
    --jt-accent-fg: var(--jt-white);
    --jt-accent-hover-fg: var(--jt-white);

    --jt-text: var(--bx-color-fg, var(--text-1, var(--wp--preset--color--contrast, #1a1a1a)));
    /* Muted text is the theme's own text colour pulled toward the page
       background, so it stays a shade of the host palette in any theme. Two
       rules, both measured (2.0.1):
       - Mix toward --jt-bg, never `transparent`. A translucent colour
         composites with whatever surface it lands on, so the same token read
         4.0:1 on the page and 2.96:1 on an unread / selected / subtle row.
         An opaque mix has one value everywhere.
       - 92% / 88%, not 70% / 65%. Host themes already ship mid-grey body text
         (BuddyX #505050 = 8.06:1, Reign #404855 = 9.7:1), so a 30% fade put
         breadcrumbs, timestamps, card stats and outline-button labels at
         3.2-4.0:1. At 92/88 the worst measured theme (BuddyX) holds
         6.5 / 5.8 on the page and >= 5.0 on the accent-tinted unread row.
         The step between levels is small on mid-grey themes; size and weight
         carry the rest of the hierarchy. */

    --jt-bg: var(--bx-color-bg-elevated, var(--bg, var(--wp--preset--color--base, #ffffff)));


    /* Semantic colors — BuddyNext first, then hardcoded fallback */
    --jt-success: var(--green, #16a34a);
    --jt-success-light: var(--green-bg, #dcfce7);
    --jt-warn: var(--amber, #ca8a04);
    --jt-warn-light: var(--amber-bg, #fef9c3);
    --jt-danger: var(--red, #dc2626);
    --jt-danger-light: var(--red-bg, #fee2e2);

    /* Status colour used AS TEXT on its own tint (pill, badge, column title,
       callout heading). The base colours above are fills - #16a34a on its
       tint is 2.6:1, #ca8a04 2.4:1. The -strong shade is the same hue mixed
       40% toward black (dark mode: toward white, below), which measures
       >= 5.6:1 on the tint and on the page in both modes. Never put the base
       status colour on its tint as text; use the -strong token. */

    /* Solid danger fill that carries text (count badges, Delete buttons).
       Dark mode lightens --jt-danger to #ef4444 so it reads as TEXT on a dark
       surface, and white on that is 3.76:1. The fill is derived here, on
       :root, from the light value, so it does not follow that flip: white on
       it is >= 5:1 in both modes. --jt-danger-fg is the text on it. */
    --jt-danger-solid: #b91c1c;
    --jt-danger-solid: color-mix(in srgb, var(--jt-danger) 85%, black);
    --jt-danger-fg: var(--jt-white);

    /* Trust level colors - pastel fills; text on them uses --jt-ink
       (white on #f472b6 was 2.65:1, dark ink is >= 7.5:1 on all six). */
    --jt-tl0: #9ca3af;
    --jt-tl1: #60a5fa;
    --jt-tl2: #34d399;
    --jt-tl3: #a78bfa;
    --jt-tl4: #f472b6;
    --jt-tl5: #fbbf24;

    /* Badge tiers */
    --jt-badge-bronze: #cd7f32;
    --jt-badge-silver: #9ca3af;
    --jt-badge-gold: #f59e0b;

    /* Radius — BuddyNext first, then WP custom, then hardcoded.
       ONE system, four steps plus circles. Pick by ROLE, never a literal:
         --jt-radius-full  every button (all sizes, .jt-btn-sm included),
                           pill, tab pill, tag, chip, badge, count bubble
         --jt-radius-lg    cards, panels, modals, the composer, covers
         --jt-radius       form controls (input, select, textarea, search
                           field), menus, popovers, inner boxes, code
         --jt-radius-sm    small inner marks: thumbnails, checkboxes,
                           focus-ring corners, inline code
         50%               avatars, status dots, round icon buttons
       Inputs read --jt-radius explicitly so the theme's own input radius
       never decides it. */
    --jt-radius: var(--r-md, var(--wp--custom--border-radius, 8px));
    --jt-radius-sm: var(--r-sm, calc(var(--jt-radius) * 0.5));
    --jt-radius-lg: var(--r-lg, calc(var(--jt-radius) * 1.5));
    --jt-radius-full: var(--r-full, 9999px);

    /* Surface — a card is a distinct surface that sits on the page, not the
       page colour itself. BuddyNext (the Community OS) owns --surface/--canvas;
       read it first so cards match exactly when running on it, with a matching
       standalone fallback (clean white card lifting off a faintly tinted
       canvas). Shared Wbcom standard: cards read --jt-surface, never --jt-bg. */

    /* Elevation — soft, layered shadows matching BuddyNext's --bn-shadow-*.
       Shared standard: components pick a token, never a literal box-shadow, so
       elevation is uniform across BuddyNext, Jetonomy, MediaVerse, Listora, and
       Career Board. BuddyNext-first, with a matching standalone fallback. */
    --jt-shadow-xs: var(--bn-shadow-xs, 0 1px 2px rgba(16, 24, 40, 0.04));
    --jt-shadow-sm: var(--bn-shadow-sm, 0 1px 2px rgba(16, 24, 40, 0.04), 0 2px 4px rgba(16, 24, 40, 0.04));
    --jt-shadow-md: var(--bn-shadow-md, 0 2px 4px rgba(16, 24, 40, 0.04), 0 8px 16px rgba(16, 24, 40, 0.06));
    --jt-shadow-lg: var(--bn-shadow-lg, 0 8px 16px rgba(16, 24, 40, 0.06), 0 24px 48px rgba(16, 24, 40, 0.08));

    /* Spacing scale — used across every component for uniform rhythm.
       Do not add `px` values directly to components; pick a token. */
    --jt-space-1: 4px;
    --jt-space-2: 8px;
    --jt-space-3: 12px;
    --jt-space-4: 16px;
    --jt-space-5: 20px;
    --jt-space-6: 24px;
    --jt-space-8: 32px;
    --jt-space-10: 40px;
    --jt-space-12: 48px;

    /* Landing offset for deep-linked anchors (#reply-{id} from notifications,
       emails, JSON-LD). Host themes commonly pin a sticky header that CSS
       cannot measure, so a target scrolled flush to y=0 lands UNDER it — the
       reply is on screen but its author row is covered. The default clears a
       typical sticky header (the reference themes run 88-130px); a theme or
       owner with taller/no chrome overrides this one token rather than
       patching every anchor. Erring large is deliberate: too small hides
       content, too large only adds harmless breathing room above the target. */
    --jt-scroll-offset: 140px;

    /* Typography scale — rem-based so OS/theme zoom is respected.
       Components pick a token, never a literal rem value.
       Heading ROLES are fixed to steps so the order holds at every width
       (2.0.1: a topic H1 rendered 19px under a 21px "Replies" H2 on phones):
                     desktop   phone (<= 640)
         h1 (page)   3xl 24    2xl 20
         h2 (section) xl 18    lg  17
         h3           lg 17    base 15
       Hierarchy below h3 comes from weight and colour, not more sizes. */
    --jt-text-2xs: 0.6875rem;   /* 11px — pill, caption */
    --jt-text-xs:  0.75rem;     /* 12px — meta, tag */
    --jt-text-sm:  0.8125rem;   /* 13px — secondary */
    --jt-text-base: 0.9375rem;  /* 15px — body, card title mobile */
    --jt-text-md:  1rem;        /* 16px — body desktop */
    --jt-text-lg:  1.0625rem;   /* 17px — h3, h2 phone */
    --jt-text-xl:  1.125rem;    /* 18px — h2 section title */
    --jt-text-2xl: 1.25rem;     /* 20px — h1 phone */
    --jt-text-3xl: 1.5rem;      /* 24px — h1 page title */

    /* Line heights */
    --jt-leading-tight: 1.2;
    --jt-leading-snug:  1.35;
    --jt-leading-normal: 1.5;

    /* Tap target minimum — apply via min-height on interactive elements */
    --jt-tap: 40px;

    /* Transitions */
    --jt-ease: cubic-bezier(0.22, 1, 0.36, 1);
    --jt-dur: 0.15s;

    /* Constants — absolute values that do NOT adapt to the active theme */
    --jt-white: #ffffff;                 /* text/icons on colored backgrounds */
    --jt-ink: #111827;                   /* text on light pastel fills (trust-level chips) */
    --jt-code-bg: #1e1e1e;              /* code block bg — always dark (syntax highlighter) */
    --jt-code-fg: #d4d4d4;              /* code block text — always light */

    /* Warning variants */
  }

  /* Derived tokens - every value computed FROM --jt-text / --jt-bg /
     --jt-accent / a status colour.

     A custom property that contains var() is resolved on the element that
     DECLARES it, and descendants inherit the finished value. Declared on
     :root alone, these were computed once from the LIGHT base tokens, so when
     dark mode reassigned --jt-bg / --jt-text on <body> every surface, tint,
     muted text and border still carried the root's value: on Reign dark the
     cards painted #0a0a0a and the Messages panel #1e1e1e beside the theme's
     blue-slate page (2.0.1 design read). Listing the dark scopes here makes
     them re-resolve where the dark base tokens live; the dark block below
     only reassigns base tokens and the few mixes whose RECIPE changes. This
     is not the `.jt-app` re-declaration the header warns about: .jt-dark and
     [data-theme="dark"] sit on <body>/<html>, never on a nested surface. */
  :root,
  .jt-dark,
  [data-theme="dark"] {
    --jt-accent-hover: #2563EB;
    --jt-accent-hover: color-mix(in srgb, var(--jt-accent) 85%, black);
    --jt-accent-light: #EFF6FF;
    --jt-accent-light: color-mix(in srgb, var(--jt-accent) 10%, white);
    --jt-accent-muted: #DBEAFE;
    --jt-accent-muted: color-mix(in srgb, var(--jt-accent) 15%, white);
    --jt-text-secondary: #4B5563;
    --jt-text-secondary: color-mix(in srgb, var(--jt-text) 92%, var(--jt-bg));
    --jt-text-tertiary: #6B7280;
    --jt-text-tertiary: color-mix(in srgb, var(--jt-text) 88%, var(--jt-bg));
    --jt-bg-subtle: #F9FAFB;
    --jt-bg-subtle: color-mix(in srgb, var(--jt-text) 3%, var(--jt-bg));
    --jt-bg-muted: #F3F4F6;
    --jt-bg-muted: color-mix(in srgb, var(--jt-text) 6%, var(--jt-bg));
    --jt-bg-hover: #F9FAFB;
    --jt-bg-hover: color-mix(in srgb, var(--jt-text) 4%, var(--jt-bg));
    --jt-border: rgba(0,0,0,0.1);
    --jt-border: color-mix(in srgb, var(--jt-text) 10%, transparent);
    --jt-border: var(--bx-color-border, color-mix(in srgb, var(--jt-text) 10%, transparent));
    --jt-border-strong: rgba(0,0,0,0.18);
    --jt-border-strong: color-mix(in srgb, var(--jt-text) 18%, transparent);
    --jt-success-strong: #15803d;
    --jt-success-strong: color-mix(in srgb, var(--jt-success) 60%, black);
    --jt-warn-strong: #795302;
    --jt-warn-strong: color-mix(in srgb, var(--jt-warn) 60%, black);
    --jt-danger-strong: #991b1b;
    --jt-danger-strong: color-mix(in srgb, var(--jt-danger) 60%, black);
    --jt-accent-strong: #1e40af;
    --jt-accent-strong: color-mix(in srgb, var(--jt-accent) 60%, black);
    --jt-surface: var(--surface, var(--jt-bg));
    --jt-canvas: var(--canvas, color-mix(in srgb, var(--jt-text) 2%, var(--jt-bg)));
    --jt-warn-border: #ffe082;
    --jt-warn-border: color-mix(in srgb, var(--jt-warn) 50%, white);
  }

  /* Derive readable text on the adopted accent. Split from the block above
     because relative colour syntax must be feature-gated: engines without it
     would drop the declaration and fall back to --jt-white, which is exactly
     the current (broken-on-light-accents) behaviour - never worse.

     The threshold is 0.7, not the mathematical crossover. White and black reach
     equal WCAG contrast at roughly L 0.62, so a "pick whichever scores higher"
     rule flips to black for ordinary brand colours - #1d76da, a plain blue,
     computes to L 0.57065 and lost the old 0.57 comparison by 0.00065, painting
     black text on a blue button. Both choices technically pass there; only one
     of them looks like a working button. 0.7 keeps white on every normal brand
     blue, green, purple and red, and hands black to the colours that genuinely
     need it - yellows, ambers and pastels, which sit above 0.75. */
  @supports (color: oklch(from red l c h)) {
    :root,
    .jt-dark,
    [data-theme="dark"] {
      --jt-accent-fg: oklch(from var(--jt-accent) clamp(0, (0.7 - l) * 1000, 1) 0 h);
      /* Hover is the accent mixed 85% toward black, i.e. strictly darker, so the
         black/white choice can flip against it. Derive it from the hover colour
         rather than reusing --jt-accent-fg and hoping. */
      --jt-accent-hover-fg: oklch(from var(--jt-accent-hover) clamp(0, (0.7 - l) * 1000, 1) 0 h);
    }
  }

  /* Dark mode — class-driven by the theme ONLY. The host theme decides
     whether its visitors see dark or light, and Jetonomy mirrors that
     decision via the .jt-dark class. We deliberately do NOT honor the
     browser's `prefers-color-scheme: dark` — a forum embedded in a
     light-themed site should stay light even on an OS-dark visitor;
     anything else creates a jarring contrast against the surrounding
     theme chrome and gets reported as a bug. The host theme's dark
     toggle is the single source of truth.

     Reassigned on the class itself — the class the host theme puts on <body> —
     NOT on `.jt-dark .jt-app`. Every Jetonomy surface is a descendant of that
     element, so all of them inherit: the app, the blocks wherever they render,
     and the BuddyPress integration surfaces (.jt-bp-forum, .jt-bp-recent,
     .jt-bp-topic-row) that sit inside BP-owned templates outside .jt-app. A
     `.jt-dark [class*="jt-bp-"]` escape hatch used to enumerate that last group
     specifically; inheriting from <body> covers it, so it is gone.

     [data-theme="dark"] is honoured alongside .jt-dark because blocks.css
     already supported it while jetonomy.css did not — a theme that signals dark
     only via the attribute got a half-dark plugin. One list, both signals. */
  .jt-dark,
  [data-theme="dark"] {
    /* Adopt the host theme's DARK palette, the same way light mode adopts
       its light one, so a card reads as a lifted piece of the theme's own
       page and not a neutral near-black dropped onto it (Reign's page is
       blue-slate #23262f; the old fixed #171717 sat beside it as a second,
       unrelated dark).
       --jt-page-bg the theme's dark PAGE colour: BuddyX/BuddyX Pro
                  --bx-color-bg, Reign --reign-site-body-bg-color, BuddyNext
                  --bg, else a neutral. WP's --wp--preset--color--base is
                  deliberately NOT read here: a theme that toggles dark
                  without flipping its presets would hand back white.
       --jt-bg    the elevated surface: the theme's own elevated token when it
                  has one (BuddyX), else the page lifted 6% toward the text
                  colour - same hue, a step lighter, which is what an
                  elevated dark surface is.
       Every tint, muted text and border is derived from these in the block
       above, so they follow the hue too. */
    --jt-page-bg: var(--bx-color-bg, var(--reign-site-body-bg-color, var(--bg, #121212)));
    --jt-text: var(--bx-color-fg, #e5e5e5);
    --jt-bg: #1d1d1d;
    --jt-bg: var(--bx-color-bg-elevated, color-mix(in srgb, var(--jt-text) 6%, var(--jt-page-bg)));
    /* Muted text pulls further toward the surface than in light mode: the
       dark text colour is near-white, so 92/88 (the light recipe) left no
       visible step. 86 / 76 holds >= 8 / 6.6:1 on the lifted surface and
       4.6:1 on the accent-tinted pinned row (68 measured 3.98 there). */
    --jt-text-secondary: #b8b8b8;
    --jt-text-secondary: color-mix(in srgb, var(--jt-text) 86%, var(--jt-bg));
    --jt-text-tertiary: #9e9e9e;
    --jt-text-tertiary: color-mix(in srgb, var(--jt-text) 76%, var(--jt-bg));
    --jt-border: rgba(255, 255, 255, 0.1);
    --jt-border: var(--bx-color-border, color-mix(in srgb, var(--jt-text) 12%, transparent));
    --jt-success-light: #052e16;
    --jt-success-light: color-mix(in srgb, var(--jt-success) 18%, var(--jt-bg));
    --jt-warn-light: #422006;
    --jt-warn-light: color-mix(in srgb, var(--jt-warn) 18%, var(--jt-bg));
    --jt-danger-light: #450a0a;
    --jt-danger-light: color-mix(in srgb, var(--jt-danger) 18%, var(--jt-bg));
    /* Danger text on a dark surface needs a lighter red than the
       light-mode #dc2626 to pass WCAG AA (#dc2626 on #171717 is 3.71:1, fails
       the 4.5 threshold). #ef4444 (Tailwind red-500) lands at 5.33:1 and stays
       recognisable as red. Used on .jt-more-dropdown__item--danger (Block user),
       destructive confirm buttons, and any other "danger" foreground in dark. */
    --jt-danger: #ef4444;
    /* Accent tints must invert in dark mode: the default light-mode
       recipe mixes accent with white, producing a near-white/mint wash
       (--jt-accent-muted ≈ #DBEAFE). On a dark panel that lands as a
       jarringly bright patch for unread notifications, pinned rows,
       vote hover, composer focus ring, tag hover, and accent badges.
       Re-derive both tints against the dark surface so they stay subtle
       tinted backgrounds that only a little lighter than the panel,
       not patches that scream. */
    --jt-accent-light: color-mix(in srgb, var(--jt-accent) 12%, var(--jt-bg));
    --jt-accent-muted: color-mix(in srgb, var(--jt-accent) 18%, var(--jt-bg));
    /* -strong shades flip toward white so status text stays readable on
       the dark tints (>= 7:1). */
    --jt-success-strong: color-mix(in srgb, var(--jt-success) 60%, white);
    --jt-warn-strong: color-mix(in srgb, var(--jt-warn) 60%, white);
    --jt-danger-strong: color-mix(in srgb, var(--jt-danger) 60%, white);
    --jt-accent-strong: color-mix(in srgb, var(--jt-accent) 60%, white);
  }
