/* =========================================================================
   Circly — shared design tokens. SINGLE SOURCE OF TRUTH for the --d-*
   palette (16 names, values lifted verbatim from public/css/app.css:8-12)
   and the four Vazirmatn font faces (copied verbatim from
   public/css/style.css:3-26).

   Extended 2026-08-23 with the scales this file originally declared out of
   scope: type, spacing, radius, elevation, stacking and motion. Every scale
   below was derived by MEASURING the existing stylesheets, not by importing
   a grid — the step values are the values already in use, so adopting them
   is close to a no-op visually. That is the point: this layer exists to stop
   the drift (38 font sizes and 202 raw hexes in app.css alone), not to
   restyle a shipped product.

   RULE: component CSS references these names only. A raw px, rem or hex in
   app.css / landing.css / style.css / admin.css is now a defect.
   ========================================================================= */

:root {
  --d-rail: #0b0d11;
  --d-side: #14171f;
  --d-main: #0f1216;
  --d-hover: #1b1f29;
  --d-active: #232838;
  --d-float: #080a0e;
  --d-head: #14171f;
  --d-text: #eef1f6;
  --d-dim: #9aa3b2;
  --d-faint: #656d7d;
  --d-mute: #4c5260;
  --d-accent: #7c5cff;
  --d-accent-hover: #6a49f2;
  --d-green: #3ddc84;
  --d-red: #ff5c6c;
  --d-divider: rgba(0, 0, 0, .32);
}

@font-face {
  font-family: 'Vazirmatn';
  src: url('../fonts/Vazirmatn-Regular.woff2') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: 'Vazirmatn';
  src: url('../fonts/Vazirmatn-Medium.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Vazirmatn';
  src: url('../fonts/Vazirmatn-SemiBold.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Vazirmatn';
  src: url('../fonts/Vazirmatn-Bold.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

/* =========================================================================
   Baseline keyboard focus + motion preference, for EVERY surface.

   app.css restored the focus ring for the shell (app.css:1064) and landing.css
   did the same for the landing page, but style.css — which dresses login,
   reset-password, invite, room, index and the 404 — had no :focus-visible rule
   at all. Login and password reset are the two highest-stakes keyboard forms in
   the product and neither showed where focus was.

   This is the floor, not the law: the selector is wrapped in :where() so it
   carries ZERO specificity and every existing app.css / landing.css rule keeps
   winning unchanged. Surfaces that already answer focus their own way are
   unaffected; surfaces that answered not at all now do.
   ========================================================================= */
:where(button, [href], input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--d-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* Motion preference, likewise applied everywhere rather than per-stylesheet.
   app.css carries an identical block for the shell; this one is what reaches
   the six style.css pages, which had none. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* =========================================================================
   TIER 1 — additional global colour. Every value below already appears as a
   literal in app.css; naming it here is the whole change. The counts are the
   number of raw occurrences each token retires.
   ========================================================================= */
:root {
  --d-white: #fff;                        /* 57× */
  --d-black: #000;                        /* 10× */
  --d-gold: #f0b232;                      /* 16× — boost / warning amber */
  --d-cyan: #3ec9ff;                      /* 6× — SECOND BRAND HUE. Partner in
                                             --lp-grad and the "changed" kind.
                                             Introduced by the 2026-08-10 landing
                                             redesign and never tokenised. */
  --d-accent-text: #a48cff;               /* accent AS TEXT on --d-main — 6.95:1.
                                             --d-accent itself is 3.4:1 and must
                                             never be used for body-size text. */
  --d-amber: #ffc65c;                     /* changelog "fixed" — distinct from
                                             --d-gold, which means boost. */

  /* --- TIER 2: aliases that RESOLVE PHANTOM NAMES ---------------------------
     app.css referenced --d-warn and --d-bg2 through var(--name, #hex). Neither
     was ever declared anywhere, so the inline fallback was doing 100% of the
     work and the token name was decoration. Declaring them here makes the name
     real and folds two near-miss literals into the palette:
       --d-warn  fallback was #faa61a — 10 units off --d-gold  #f0b232
       --d-bg2   fallback was #0f1218 —  2 units off --d-main  #0f1216
     The --d-warn fold is the only visible change (one em-badge, slightly less
     orange); --d-bg2 is imperceptible. */
  --d-warn: var(--d-gold);
  --d-bg2: var(--d-main);

  /* Accent + status tints. Always the accent at low alpha, never a new hue. */
  --d-accent-soft: rgba(124, 92, 255, .18);   /* 6× — pressed / mine */
  --d-accent-faint: rgba(124, 92, 255, .12);  /* 3× — hover wash */
  --d-red-soft: rgba(255, 92, 108, .16);      /* 2× — destructive hover */
  --d-gold-soft: rgba(240, 178, 50, .1);      /* 2× */
  --d-gold-edge: rgba(240, 178, 50, .35);     /* 2× */

  /* Scrims — behind modals and over media. */
  --d-scrim: rgba(0, 0, 0, .5);           /* 12× */
  --d-scrim-soft: rgba(0, 0, 0, .35);
  --d-scrim-heavy: rgba(0, 0, 0, .6);     /* 4× */

  /* White tints — the app's hairlines and hover washes on dark surfaces.
     Five steps because five are genuinely in use; do not add a sixth. */
  --d-tint-1: rgba(255, 255, 255, .06);   /* 6× */
  --d-tint-2: rgba(255, 255, 255, .08);   /* 8× — .07 and .09 fold in here */
  --d-tint-3: rgba(255, 255, 255, .12);
  --d-tint-4: rgba(255, 255, 255, .18);   /* 3× — .16 folds in here */
  --d-tint-5: rgba(255, 255, 255, .25);

  /* Wordle board. Domain colours, deliberately NOT the product palette — the
     game is a quotation of a known board and reads wrong recoloured. Named so
     they stop looking like stray hexes during a palette audit. */
  --w-hit: #6aaa64;
  --w-near: #c9b458;
  --w-miss: #3a3a3c;
  --w-key: #818384;
  --w-key-dim: #565758;
  --w-dim: #787c7e;
}

/* =========================================================================
   TIER 1 — type scale. app.css alone carried 38 distinct literal font sizes
   (48 across all stylesheets), ~200 of them packed into the 11px–15px band.
   Eleven steps replace all of them. Steps 100–600 ARE that dense band: each
   is load-bearing today, and each snap moves text by at most ~0.5px.
   Body copy is --fs-700. Nothing outside this ladder.
   ========================================================================= */
:root {
  --fs-100: .66rem;    /* 10.6px — badge counts, micro timestamps */
  --fs-200: .72rem;    /* 11.5px — hints, captions, edit hints */
  --fs-300: .78rem;    /* 12.5px — chips, secondary labels */
  --fs-400: .82rem;    /* 13.1px — dense lists, member rows, nav */
  --fs-500: .88rem;    /* 14.1px — buttons, inputs, form labels */
  --fs-600: .94rem;    /* 15.0px — dense body */
  --fs-700: 1rem;      /* 16px   — body: message text */
  --fs-800: 1.15rem;   /* 18.4px — dialog + section titles */
  --fs-900: 1.5rem;    /* 24px   — headings */
  --fs-1000: 2rem;     /* 32px   — display */
  --fs-1100: 3rem;     /* 48px   — hero (landing only) */

  /* Weights. Only these four have a real Vazirmatn face loaded above —
     anything else is synthesised by the browser and looks it.
     app.css previously used font-weight:800 at six sites with no 800 face
     loaded, so the browser synthesised the weight on top of the 700 face —
     heavier and softer-edged than real bold. Those six now use --fw-bold.
     Adding a weight here without shipping its .woff2 re-creates that bug. */
  --fw-regular: 400;
  --fw-medium: 500;
  --fw-semibold: 600;
  --fw-bold: 700;

  /* Line height. Persian sets taller than Latin — Vazirmatn's ascenders and
     the diacritics both want the extra leading, so --lh-normal is 1.55 and
     not the 1.4 a Latin-first system would pick. */
  --lh-tight: 1.2;     /* display + headings */
  --lh-snug: 1.35;     /* dense UI rows */
  --lh-normal: 1.55;   /* body / message text */
  --lh-relaxed: 1.75;  /* long-form prose: legal, changelog */
}

/* =========================================================================
   TIER 1 — spacing. Measured, not invented: 8/6/10/12/4 account for ~470 of
   the ~600 spacing literals in app.css. This ladder keeps every one of those
   and absorbs only the strays (1,3,5,7,9,11,18,22,24,32), so adopting it is
   a near-zero visual change. A textbook 4px grid would be more principled
   and would move ~250 declarations — deliberately not done here.
   ========================================================================= */
:root {
  --sp-1: 2px;    /* 44× — icon nudges, chip insets */
  --sp-2: 4px;    /* 71× — absorbs 3px, 5px */
  --sp-3: 6px;    /* 98× — absorbs 7px */
  --sp-4: 8px;    /* 130× — the default gap */
  --sp-5: 10px;   /* 94× — absorbs 9px, 11px */
  --sp-6: 12px;   /* 72× */
  --sp-7: 14px;   /* 31× */
  --sp-8: 16px;   /* 28× */
  --sp-9: 20px;   /* 39× — absorbs 18px, 22px */
  --sp-10: 28px;  /* absorbs 24px, 32px */
  --sp-11: 52px;  /* section rhythm */
}

/* =========================================================================
   TIER 1 — radius. 20 distinct literals become 8 steps. --r-pill and
   --r-circle are separate on purpose: a pill is a rectangle whose ends are
   round, a circle is an avatar. They are not interchangeable and 50% on a
   non-square element is a bug, not a pill.
   ========================================================================= */
:root {
  --r-1: 4px;      /* absorbs 1,2,3,5 — inline code, tiny chips */
  --r-2: 6px;      /* absorbs 7 — inputs, small buttons */
  --r-3: 8px;      /* absorbs 9 — the default: cards, chips, menus */
  --r-4: 10px;     /* absorbs 11,12 — popovers, panels */
  --r-5: 14px;     /* absorbs 16 — dialogs */
  --r-6: 20px;     /* absorbs 18,24 — sheets, large surfaces */
  --r-pill: 999px; /* absorbs 99px */
  --r-circle: 50%; /* avatars and status dots ONLY */
}

/* =========================================================================
   TIER 1 — elevation + stacking. Five shadows, and a z ladder to replace the
   ad-hoc 0/2/60/69/70/80/100/130/150/180/190/200/260/300/400 in app.css.
   `z-index: 69` sitting next to `z-index: 70` is the reason this exists.
   Nothing may invent a value between two rungs.
   ========================================================================= */
:root {
  --sh-1: 0 2px 8px rgba(0, 0, 0, .3);      /* resting menus */
  --sh-2: 0 4px 14px rgba(0, 0, 0, .45);    /* popovers */
  --sh-3: 0 8px 24px rgba(0, 0, 0, .5);     /* dialogs */
  --sh-4: 0 10px 30px rgba(0, 0, 0, .5);    /* mention picker, drawers */
  --sh-5: 0 20px 60px rgba(0, 0, 0, .6);    /* full-screen surfaces */
  --sh-divider: 0 1px 0 var(--d-divider);   /* header hairline */

  --z-base: 0;
  --z-raised: 2;        /* in-flow lift: hover cards, sticky rows */
  --z-sticky: 100;      /* channel header, composer bar */
  --z-dropdown: 200;    /* menus, mention picker, emoji tray */
  --z-overlay: 300;     /* drawers, side sheets, scrims */
  --z-modal: 400;       /* dialogs */
  --z-toast: 500;       /* toasts — must clear dialogs */
  --z-tooltip: 600;     /* nothing above this */
}

/* =========================================================================
   TIER 1 — motion. .12s already dominates (42×) and one easing curve is
   already doing all the real work (10×); this just names them. Every value
   here is still overridden to ~0 by the prefers-reduced-motion block below.
   ========================================================================= */
:root {
  --dur-1: 80ms;    /* press / active feedback */
  --dur-2: 120ms;   /* the default: hover, colour, border */
  --dur-3: 160ms;   /* small enter/exit */
  --dur-4: 240ms;   /* panels, drawers */
  --dur-5: 400ms;   /* full-surface transitions */

  --ease-standard: cubic-bezier(.2, .8, .2, 1);  /* the house curve */
  --ease-out: ease-out;
  --ease-in-out: ease-in-out;
}

/* =========================================================================
   SCROLLBARS — one styled treatment, everywhere.
   =========================================================================
   The app styled three scrollable regions by hand (.channel-list, .msg-list,
   .menu) with a raw hex, and left everything else — the whole admin panel,
   every settings sheet, every dialog body — on the operating system's default.
   The result was a dark product with a pale grey Windows scrollbar cutting
   through half its surfaces, which is exactly where a UI stops feeling like
   one thing.

   Declared here because tokens.css is the only stylesheet EVERY page loads, so
   this reaches app, admin, landing, login and pay from one place.

   Regions that deliberately hide their scrollbar (.server-rail, .ctrl-row) keep
   doing so: a class selector outranks the universal one below, in both the
   standard property and the -webkit pseudo-element. */
:root {
  --d-sb-thumb:       rgba(255, 255, 255, .14);
  --d-sb-thumb-hover: rgba(255, 255, 255, .26);
  --d-sb-track:       transparent;
  --d-sb-size:        10px;
}

/* Firefox and the standard property. `thin` rather than `auto` — a full-width
   scrollbar inside a 240px sidebar is a quarter of the content width. */
* { scrollbar-width: thin; scrollbar-color: var(--d-sb-thumb) var(--d-sb-track); }

/* Chromium and WebKit. Track stays transparent so the bar reads as part of the
   surface it sits on rather than as a channel cut into it. */
*::-webkit-scrollbar { inline-size: var(--d-sb-size); block-size: var(--d-sb-size); }
*::-webkit-scrollbar-track { background: var(--d-sb-track); }
*::-webkit-scrollbar-thumb {
  background: var(--d-sb-thumb);
  border-radius: 999px;
  /* A transparent border with background-clip is the only way to inset a
     -webkit thumb; padding and margin do nothing to it. */
  border: 2px solid transparent;
  background-clip: content-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--d-sb-thumb-hover); background-clip: content-box; }
*::-webkit-scrollbar-corner { background: transparent; }

/* A pointer sitting on a scrollbar is not hovering the page behind it, so the
   thumb must not depend on hover alone to be findable — it is visible at rest. */
@media (prefers-reduced-motion: reduce) {
  *::-webkit-scrollbar-thumb { transition: none; }
}
