/* app.css */
/* =========================================================
   أثر — Base
   Reset, bilingual typography, layout, utilities.
   Reads only layer-2 semantic tokens. Zero hardcoded colour.
   ========================================================= */
/* =========================================================
   أثر — Design Tokens
   ---------------------------------------------------------
   Three layers, in order. Components may ONLY read layer 2
   and 3. Never reference a layer-1 primitive outside this file.

     1. Primitives  --athar-surface-container   raw values
     2. Semantic    --color-surface             role, swaps per theme
     3. Component   defined in components/*.css

   Dark is the default: :root holds the dark values because the
   Athar logo is built for a dark ground, and because the design
   system is specified in dark. Light is an explicit opt-in via
   [data-theme="light"] — theme-init sets the attribute before
   first paint, so there is no flash.
   ========================================================= */

/* ---------------------------------------------------------
   LAYER 1a — The dark palette (Athar Commerce OS)

   The design system's own tokens, transcribed from the project's
   design file at the repository root.
   They are the authority for dark mode and nothing here is
   derived or eyeballed: if a value needs to change, it changes
   in the design file first and is copied across.

   The set is tonal rather than flat. Six ground steps carry the
   whole hierarchy — canvas, band, card, menu, and the two
   extremes — so a dashboard separates its panels by tone and not
   by drawing a border around everything.

   Every fill has an on-* partner: the ink that was measured
   against it. A fill used without its partner is how a bright
   teal button ends up with white text nobody can read.
   --------------------------------------------------------- */
:root {
  /* Grounds — container levels, deepest to brightest */
  --athar-surface-container-lowest: #0b1015;
  --athar-background: #0f1419;
  --athar-surface: #0f1419;
  --athar-surface-dim: #0f1419;
  --athar-surface-container-low: #171c21;
  --athar-surface-container: #1b2025;
  --athar-surface-container-high: #252a30;
  --athar-surface-container-highest: #30353b;
  --athar-surface-bright: #353a3f;
  --athar-surface-variant: #30353b;

  /* Ink on those grounds */
  --athar-on-background: #dee3ea;
  --athar-on-surface: #dee3ea;
  --athar-on-surface-variant: #bacac6;
  --athar-inverse-surface: #dee3ea;
  --athar-inverse-on-surface: #2c3136;

  /* Lines */
  --athar-outline: #859490;
  --athar-outline-variant: #3b4a47;

  /* Primary — the teal cyan of the mark. The operational core:
     conversion triggers, positive metrics, active navigation. */
  --athar-primary: #45e5d3;
  --athar-on-primary: #003732;
  --athar-primary-container: #00c9b7;
  --athar-on-primary-container: #004f47;
  --athar-inverse-primary: #006a60;
  --athar-surface-tint: #37ddca;
  --athar-primary-fixed: #5ffae6;
  --athar-primary-fixed-dim: #37ddca;
  --athar-on-primary-fixed: #00201c;
  --athar-on-primary-fixed-variant: #005048;

  /* Secondary — copper bronze. Tier badges, premium locks,
     revenue totals. Never a call to action. */
  --athar-secondary: #f8ba87;
  --athar-on-secondary: #4c2701;
  --athar-secondary-container: #6a3f17;
  --athar-on-secondary-container: #e8ac7a;
  --athar-secondary-fixed: #ffdcc2;
  --athar-secondary-fixed-dim: #f8ba87;
  --athar-on-secondary-fixed: #2e1500;
  --athar-on-secondary-fixed-variant: #673d15;

  /* Tertiary — deep cyan / petrol. Structural support: chart
     fills, category flags, hover gradients, secondary clusters. */
  --athar-tertiary: #84dbf6;
  --athar-on-tertiary: #003642;
  --athar-tertiary-container: #67bfda;
  --athar-on-tertiary-container: #004c5d;
  --athar-tertiary-fixed: #b3ebff;
  --athar-tertiary-fixed-dim: #7cd2ee;
  --athar-on-tertiary-fixed: #001f27;
  --athar-on-tertiary-fixed-variant: #004e5f;

  /* Error */
  --athar-error: #ffb4ab;
  --athar-on-error: #690005;
  --athar-error-container: #93000a;
  --athar-on-error-container: #ffdad6;
}

/* ---------------------------------------------------------
   LAYER 1b — Brand ramps

   The palette above is specified for dark only. Light mode is
   still the same brand, so the three brand hues are also written
   as ramps: the same colours mixed toward white and toward the
   ground, with the deep steps chosen so ink on paper clears AA
   rather than merely looking right.

     teal   the primary, anchored on #00c9b7
     cyan   the tertiary petrol, anchored on #1b829b
     brass  the secondary copper, anchored on #d49a6a

   -500 is the brand value in each. Steps above it are for dark
   grounds, steps below for paper.
   --------------------------------------------------------- */
:root {
  --athar-teal-200: #a8f2e9;
  --athar-teal-300: #5ce6d7;
  --athar-teal-400: #22d8c6;
  --athar-teal-500: #00c9b7;
  --athar-teal-600: #00a898;
  --athar-teal-700: #0a7d73;
  --athar-teal-800: #0a5c55;

  --athar-cyan-200: #b7e0ea;
  --athar-cyan-300: #7ec8da;
  --athar-cyan-400: #2ba3be;
  --athar-cyan-500: #1b829b;
  --athar-cyan-600: #136a80;
  --athar-cyan-700: #0e5265;
  --athar-cyan-800: #0a3a49;

  --athar-brass-200: #f0d5bc;
  --athar-brass-300: #e6bd97;
  --athar-brass-500: #d49a6a;
  --athar-brass-600: #ac7a4f;
  /* The reading step. A diamond mark and the word beside it do not
     need the same value: one is a shape, the other has to clear 4.5
     on all three paper grounds, the sunken one included. */
  --athar-brass-700: #7d5535;

  /* Paper — the light ground. A clean cool slate, a shade off white.
     50 is the container and sidebar surface, 100 the page canvas. */
  --athar-paper-50:  #ffffff;
  --athar-paper-100: #f8fafc;
  --athar-paper-200: #eaecef;
  --athar-paper-300: #dde0e4;
  --athar-paper-400: #c2c7ce;

  --athar-neutral-100: #e8eaec;
  --athar-neutral-300: #aab1b8;
  --athar-neutral-500: #79818a;
  --athar-neutral-700: #4a525b;
  --athar-neutral-900: #171a1d;

  /* Status outside the palette.

     Success is the primary teal by design — a finished task and a
     rising trendline are the same signal. Error is the palette's
     own. Warning is the one hue the system does not ship: amber
     sits a step off the copper secondary on purpose, because a
     warning that looks like a premium badge is a warning nobody
     reads. */
  --athar-amber-300: #f5c86b;
  --athar-amber-600: #a2681c;
  --athar-red-600: #ba1a1a;
}

/* ---------------------------------------------------------
   LAYER 1c — Type, space, shape, motion
   --------------------------------------------------------- */
:root {
  /* ---- Typography primitives ----
     Almarai everywhere — Arabic and Latin out of the same file,
     both drawn by Boutros, so a bilingual line does not change
     voice halfway across.

     It replaced IBM Plex Sans Arabic on the storefront and Cairo
     on the dashboard (owner's instruction, 2026-09-09). Plex and
     Cairo are the two faces every generated Arabic template
     reaches for first, and the platform had one of each.

     Almarai ships four cuts and no more: 300, 400, 700, 800. The
     weight tokens below name those and nothing else. A browser
     asked for 600 in a family that has none picks 700 by itself;
     writing 700 here only says out loud what was going to happen,
     and leaves 800 free for the one step above bold.

     --font-figures is the one thing Almarai cannot do, and it was
     measured rather than assumed. Its digits are proportional and
     it carries no tnum feature at all: at 40px the 1 is 14.20px
     wide against the 8 at 21.24px, and setting either
     font-variant-numeric: tabular-nums or "tnum" 1 moves the
     string not one hundredth of a pixel (measured in Chrome on
     the running app, 2026-09-09). A right-aligned column of
     riyals in that face does not line up — the decimal wanders by
     a digit-width per row, which is exactly the thing a merchant
     scanning revenue needs it not to do.

     So figures are set in the platform's monospace instead: every
     rule that already paired --font-latin with tabular-nums now
     reads this token, and that pairing was already the codebase's
     marker for "this is a number to be compared".

     Neither this nor --font-mono downloads anything. A second
     webfont for the sake of the digits is a network round trip
     and a second voice on the page; the system monospace is on
     every machine and its digits are equal-width by definition. */
  --font-display-ar: "Almarai", "Segoe UI", Tahoma, sans-serif;
  --font-body-ar: "Almarai", "Segoe UI", Tahoma, sans-serif;
  --font-latin: "Almarai", "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-figures: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;
  --font-mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;


  --fw-regular: 400;
  --fw-medium: 400;
  --fw-semibold: 700;
  --fw-bold: 700;
  --fw-black: 800;

  /* Fluid scale, matched to the design system's ramp. Each clamp
     runs from the mobile size to the desktop one:

       display-lg   34 -> 48    --fs-h1
       headline-xl  26 -> 32    --fs-h2
       headline-md  22          --fs-h3 / --fs-xl
       title-lg     18          --fs-h4 / --fs-lg
       body-lg      16          --fs-base
       body-md      14          --fs-sm
       label-md     13          --fs-xs
       label-sm     11          --fs-eyebrow */
  --fs-eyebrow: 0.6875rem;
  --fs-xs: 0.8125rem;
  --fs-sm: 0.875rem;
  --fs-base: 1rem;
  --fs-lg: 1.125rem;
  --fs-xl: 1.375rem;
  --fs-h4: 1.125rem;
  --fs-h3: clamp(1.25rem, 1.05rem + 0.7vw, 1.375rem);
  --fs-h2: clamp(1.625rem, 1.28rem + 1.5vw, 2rem);
  --fs-h1: clamp(2.125rem, 1.55rem + 2.5vw, 3rem);


  /* Generous by RTL necessity, not by taste: Arabic diacritics and
     ligatures clip against a tight baseline, so body sits at 1.6
     rather than the 1.45 a Latin-only system would take. */
  --lh-tight: 1.25;
  --lh-heading: 1.32;
  --lh-snug: 1.45;
  --lh-normal: 1.6;
  --lh-relaxed: 1.9;

  --tracking-tight: -0.02em;
  --tracking-normal: 0;
  --tracking-wide: 0.06em;
  --tracking-eyebrow: 0.04em;

  /* ---- Space — 8px architecture, 4px sub-rhythm ---- */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;
  --space-32: 8rem;
  --space-48: 12rem; /* 192px — the largest step. Nothing on the
                        marketing pages spends it since the section
                        padding came back down to --space-24. */

  /* The same scale under the design system's own names, so a spec
     that says space-lg and a stylesheet that says --space-6 are
     provably the same measurement. */
  --space-xxs: var(--space-1);
  --space-xs: var(--space-2);
  --space-sm: var(--space-3);
  --space-md: var(--space-4);
  --space-lg: var(--space-6);
  --space-xl: var(--space-8);
  --space-2xl: var(--space-12);
  --space-3xl: var(--space-16);

  --grid-margin-desktop: 2rem;
  --grid-margin-mobile: 1rem;
  --grid-gutter: 1.25rem;

  /* ---- Density ----
     The space scale answers "how far apart", and every panel was
     reaching into it for the same answer: --space-6 inside a card,
     --space-4 when the card was in a table. Two numbers doing one
     job, chosen per file, is how a dashboard ends up with cards
     that breathe differently on adjacent screens.

     These three name the job instead. A panel is the unit a
     high-end shop is built out of — a card, a form well, a summary
     box — and it has one resting inside measure, one for a phone or
     a dense table, and one for the panel a page is built around. */
  --space-panel: 1.75rem;
  --space-panel-tight: 1.125rem;
  --space-panel-loose: 2.5rem;

  /* ---- Radius ----
     Rounded, not pill. Buttons, inputs and dropdowns hold 8px —
     corporate gravitas rather than a chat app. Cards and modules
     scale to 16px. Full radii are spent only on live status pills,
     which is what tells a status apart from a click target. */
  --radius-xs: 0.25rem;
  --radius-sm: 0.5rem;
  --radius-md: 0.75rem;
  --radius-lg: 1rem;
  --radius-xl: 1.5rem;
  --radius-pill: 9999px;
  --radius-premium: 1rem;

  /* ---- Motion ---- */
  --dur-fast: 160ms;
  --dur-base: 240ms;
  --dur-slow: 320ms;
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);

  /* Indeterminate motion — a spinner and a progress bar that do not
     know how long they have. Linear, because an eased spinner reads
     as stuttering, and slow enough that it is not a distraction next
     to a form somebody is still reading. */
  --dur-spin: 760ms;
  --dur-crawl: 1400ms;
  --ease-linear: linear;
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);

  /* ---- Layout ---- */
  --container-max: 1180px;
  --container-wide: 1400px;
  --header-height: 72px;
  --rail-width: 56px;

  /* ---- Grain ----
     A single tile of film grain, laid over the whole page at very
     low opacity. Flat digital colour is the tell of a generated
     interface; a little noise gives the surface a texture the eye
     reads as printed. */
  --texture-grain: 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.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
}

/* ---------------------------------------------------------
   LAYER 2 — Semantic · DARK (default)

   Where the palette becomes roles. Two rules hold the whole
   thing together.

   **Hierarchy is carried by the container levels, not by
   borders.** canvas -> band -> surface -> raised -> highest is
   five steps of one ground, and a panel is told apart from the
   page by the step it sits on. Lines are a refinement on top of
   that, never the thing doing the work.

   **Every fill names its own ink.** --color-primary is a bright
   teal and white on it is 1.6:1, unreadable. So --color-on-primary
   exists and is the only ink allowed on a primary fill. Same for
   secondary, tertiary, success and error. --color-text-on-brand
   stays white and now means one thing only: ink for a scrim, a
   photograph, or a fill dark enough to take it.
   --------------------------------------------------------- */
:root,
:root.dark,
:root[data-theme="dark"] {
  /* ---- Grounds ---- */
  --color-canvas: var(--athar-background);
  --color-canvas-deep: var(--athar-surface-container-lowest);
  --color-surface: var(--athar-surface-container);
  --color-surface-raised: var(--athar-surface-container-high);
  --color-surface-sunken: var(--athar-surface-container-lowest);

  /* One step above raised: a hovered row, a tooltip, the thing that
     has to sit on top of something already lifted. */
  --color-surface-highest: var(--athar-surface-container-highest);

  --color-line: var(--athar-outline-variant);
  --color-line-strong: var(--athar-outline);
  --color-line-gold: rgba(248, 186, 135, 0.34);

  /* ---- Ink ---- */
  --color-text: var(--athar-on-surface);
  --color-text-secondary: var(--athar-on-surface-variant);
  --color-text-muted: var(--athar-outline);
  --color-text-on-brand: #ffffff;

  /* ---- The on-* family ----
     One per fill, each measured against the fill it names:
     on-primary clears 8.4:1 on --color-primary, on-secondary 11.5:1
     on --color-gold, on-error 7.9:1 on --color-danger. */
  --color-on-primary: var(--athar-on-primary);
  --color-on-secondary: var(--athar-on-secondary);
  --color-on-tertiary: var(--athar-on-tertiary);
  --color-on-success: var(--athar-on-primary);
  --color-on-warning: var(--athar-on-secondary);
  --color-on-error: var(--athar-on-error);

  /* ---- Primary — the operational core ---- */
  --color-primary: var(--athar-primary);
  --color-primary-hover: var(--athar-primary-fixed);
  --color-primary-soft: rgba(0, 201, 183, 0.14);
  --color-primary-text: var(--athar-primary);

  /* The deep end of the brand, for a surface that carries brand
     colour without asking to be clicked: an avatar, a chat bubble
     the visitor wrote. Dark enough to take white. */
  --color-primary-deep: var(--athar-inverse-primary);
  --color-text-on-deep: #ffffff;

  /* ---- Growth — a rising number, a positive delta ---- */
  --color-growth: var(--athar-primary-container);
  --color-growth-soft: rgba(0, 201, 183, 0.14);
  --color-growth-text: var(--athar-primary);

  /* ---- Premium — the copper. Tier badges and nothing else. ---- */
  --color-gold: var(--athar-secondary);
  --color-gold-soft: rgba(248, 186, 135, 0.14);
  --color-gold-text: var(--athar-secondary);

  /* ---- Entry tier — the petrol tertiary ----
     The plan row wears the artwork: petrol for the tier you start
     on, teal for the one most stores end on, copper for the top. */
  --color-entry: var(--athar-tertiary-container);
  --color-entry-soft: rgba(132, 219, 246, 0.14);
  --color-entry-text: var(--athar-tertiary);
  --color-line-entry: rgba(132, 219, 246, 0.34);

  /* ---- Status ---- */
  --color-success: var(--athar-primary-container);
  --color-success-soft: rgba(0, 201, 183, 0.14);
  --color-warning: var(--athar-amber-300);
  --color-warning-soft: rgba(245, 200, 107, 0.16);
  --color-danger: var(--athar-error);
  --color-danger-soft: rgba(255, 180, 171, 0.16);

  /* ---- Delta — the two directions a KPI can move ----
     Status colours answer "did something break". A delta answers
     "is this up or down", and the top of the dashboard reads faster
     when the two questions do not share a palette. Emerald sits far
     enough off the brand teal that a rising figure is not read as a
     link; coral is warmer than --color-danger, which stays reserved
     for something that actually went wrong. */
  --color-delta-up: #10b981;
  --color-delta-up-soft: rgba(16, 185, 129, 0.1);
  --color-delta-down: #f43f5e;
  --color-delta-down-soft: rgba(244, 63, 94, 0.1);

  /* ---- Chart ----
     The lead series is electric violet #8b5cf6 by the owner's
     specification, and it is written here as well as in chart.js
     because the CSS bars the panel falls back to have to be the same
     colour as the line they stand in for. The petrol second series
     is still read from here at draw time. */
  --color-chart-primary: #8b5cf6;
  --color-chart-petrol: var(--athar-tertiary);
  --color-chart-glow: rgba(139, 92, 246, 0.55);

  --color-focus: var(--athar-primary-container);

  /* Scrim behind the mobile drawer. Not black: the canvas dimmed,
     so the page reads as pushed back rather than switched off. */
  --color-scrim: rgba(6, 10, 13, 0.66);

  /* ---- Hero wash ----
     The three stops a page's opening section is lit with. Kept here
     rather than in a page file because every marketing page opens
     the same way and they must not drift apart. Deliberately weaker
     than it looks written down: on a near-black ground even 10% of
     a saturated teal reads as a coloured panel. */
  --hero-wash-brand: rgba(0, 201, 183, 0.10);
  --hero-wash-deep: rgba(0, 106, 96, 0.30);
  --hero-wash-top: rgba(255, 255, 255, 0.02);

  /* ---- The alternating band ----
     A long page is a dozen sections of the same shape, and on one
     flat canvas they read as a single column with headings in it.
     This is the ground that breaks the run — one container step, no
     border. A band that announces itself competes with the content
     it is separating, which is the opposite of the job. */
  --color-canvas-band: var(--athar-surface-container-low);

  /* The anchor — the third ground, and the one that gives the page a
     pulse. The band above is a change of light; this is a change of
     room. It is the palette's deep petrol rather than more graphite,
     so a section wearing it reads as brand rather than as a darker
     rectangle, and it holds the same value in both themes. Spend it
     on two or three sections. A fourth turns the rhythm back into a
     texture. */
  --color-canvas-anchor: var(--athar-on-tertiary);
  --color-anchor-line: rgba(132, 219, 246, 0.18);

  --grain-opacity: 0.05;

  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.42);
  --shadow-lg: 0 24px 64px rgba(0, 0, 0, 0.5);
  --shadow-glow: 0 0 0 1px rgba(248, 186, 135, 0.16), 0 20px 56px rgba(0, 0, 0, 0.45);

  /* The ink a shadow is cast in, and how much of it this ground can
     take. Dark casts in black, because the canvas is already close
     to it; paper casts in the neutral the text is set in, because a
     black shadow on warm paper reads as a hole.

     --shadow-soft and --shadow-deep below are written once, off
     these two, so the light theme restates two values instead of
     eight — and so a shadow can never drift between themes. */
  --shadow-ink: 0 0 0;
  --shadow-strength: 1;

  /* Depth here is chromatic, not opaque: the ground does the lifting
     and these two put light on the edges. The rim is the inner
     highlight a lit card catches along its top edge; the bloom is
     the diffused teal a primary control throws under the cursor.
     Both are off by default and spent deliberately. */
  --rim-light: inset 0 1px 0 0 rgba(0, 201, 183, 0.25);
  --shadow-bloom: 0 0 20px rgba(0, 201, 183, 0.35);

  /* A focus ring, as a shadow rather than an outline: it follows the
     border-radius, and it can sit next to a shadow in the same
     property without either one clipping the other. */
  --color-focus-soft: rgba(0, 201, 183, 0.2);

  color-scheme: dark;
}

/* ---------------------------------------------------------
   LAYER 2 — Depth

   Two elevations, written once and tinted by the theme.

   Real depth is two things at once: a tight contact shadow that
   says the surface is touching the page, and a wide ambient one
   that says there is air under it. Stacked, they read as a lifted
   object; either alone reads as a filter.

   --shadow-soft  a panel at rest
   --shadow-deep  the same panel picked up: a hover, a menu, a dialog
   --------------------------------------------------------- */
:root {
  --shadow-soft:
    0 1px 2px rgb(var(--shadow-ink) / calc(0.22 * var(--shadow-strength))),
    0 4px 10px rgb(var(--shadow-ink) / calc(0.16 * var(--shadow-strength))),
    0 12px 28px rgb(var(--shadow-ink) / calc(0.14 * var(--shadow-strength)));

  --shadow-deep:
    0 2px 4px rgb(var(--shadow-ink) / calc(0.26 * var(--shadow-strength))),
    0 10px 24px rgb(var(--shadow-ink) / calc(0.26 * var(--shadow-strength))),
    0 28px 64px rgb(var(--shadow-ink) / calc(0.3 * var(--shadow-strength)));

  --ring-focus: 0 0 0 3px var(--color-focus-soft);

  /* ---- Gradients ----

     --gradient-sheen is light, not colour: a highlight along the top
     edge and a shade along the bottom, as if the surface had a
     thickness. White and black alpha only, so it lays over any fill
     — primary, copper, a merchant's own shop colour — without
     touching that fill's contrast against its label. It is an
     overlay, always the first layer of a two-layer background.

     --gradient-primary is the filled CTA. It runs primary into the
     brand teal and stops there: the design file's petrol end lands
     at 4.39:1 against --color-on-primary, a hair under AA at the
     14px bold a button is set in, and a gradient whose far corner
     fails is a gradient that fails. The petrol still gets its run in
     --gradient-brand below, which carries no small text. */
  --gradient-sheen: linear-gradient(
    180deg,
    rgb(255 255 255 / 0.16) 0%,
    rgb(255 255 255 / 0.03) 46%,
    rgb(0 0 0 / 0.07) 100%
  );

  --gradient-primary: linear-gradient(
    165deg,
    var(--color-primary-hover) 0%,
    var(--color-primary) 46%,
    var(--color-growth) 100%
  );

  --gradient-gold: linear-gradient(
    165deg,
    var(--athar-secondary-fixed) 0%,
    var(--athar-secondary) 100%
  );

  /* The emblem's own ramp — teal down to petrol — spent on the trace
     rail, the mark, and one word per page. Put it on a button as
     well and it stops being a signature. */
  --gradient-brand: linear-gradient(
    180deg,
    var(--athar-primary) 0%,
    var(--athar-primary-container) 46%,
    var(--athar-cyan-500) 100%
  );
  --gradient-trace: var(--gradient-brand);
  --gradient-word: linear-gradient(
    100deg,
    var(--athar-primary-fixed) 0%,
    var(--athar-primary-container) 50%,
    var(--athar-tertiary) 100%
  );

  /* A panel lit from above: the raised surface at the top edge
     falling to the flat one. Two steps of the same neutral, so it
     reads as light rather than as colour. */
  --gradient-surface: linear-gradient(
    180deg,
    var(--color-surface-raised) 0%,
    var(--color-surface) 100%
  );
}

/* ---------------------------------------------------------
   LAYER 2 — Semantic · LIGHT (explicit opt-in)

   The design system is specified for dark. Light is the same
   brand rebuilt on paper: the brand ramps from layer 1b taken at
   their deep steps, because a colour that reads as luminous on a
   #0f1419 ground reads as a highlighter on white.

   Every on-* token is restated. On paper the brand fills are dark
   and the ink is white — the exact inverse of the rule above —
   and a token left unset here would silently put dark ink on a
   dark fill.
   --------------------------------------------------------- */
:root[data-theme="light"] {
  --color-canvas: var(--athar-paper-100);
  --color-canvas-deep: var(--athar-paper-200);
  --color-surface: var(--athar-paper-50);
  --color-surface-raised: var(--athar-paper-50);
  --color-surface-sunken: var(--athar-paper-200);
  --color-surface-highest: var(--athar-paper-50);

  --color-line: var(--athar-paper-300);
  --color-line-strong: var(--athar-paper-400);
  --color-line-gold: rgba(172, 122, 79, 0.42);

  --color-text: var(--athar-neutral-900);
  --color-text-secondary: var(--athar-neutral-700);
  --color-text-muted: var(--athar-neutral-500);
  --color-text-on-brand: #ffffff;

  /* Inverted from dark: the fills are deep here, so the ink is white. */
  --color-on-primary: #ffffff;
  --color-on-secondary: #ffffff;
  --color-on-tertiary: #ffffff;
  --color-on-success: #ffffff;
  --color-on-warning: #ffffff;
  --color-on-error: #ffffff;

  --color-primary: var(--athar-teal-700);
  --color-primary-hover: var(--athar-teal-800);
  --color-primary-soft: rgba(0, 201, 183, 0.12);
  --color-primary-text: var(--athar-teal-800);

  --color-primary-deep: var(--athar-cyan-700);
  --color-text-on-deep: #ffffff;

  --color-growth: var(--athar-teal-700);
  --color-growth-soft: rgba(0, 201, 183, 0.12);
  --color-growth-text: var(--athar-teal-800);

  --color-gold: var(--athar-brass-600);
  --color-gold-soft: rgba(212, 154, 106, 0.18);
  /* Darker than the fill above it, the same way growth-text is darker
     than growth. A diamond mark and the word beside it do not need
     the same value: one is a shape and the other has to be read. */
  --color-gold-text: var(--athar-brass-700);

  --color-entry: var(--athar-cyan-600);
  --color-entry-soft: rgba(27, 130, 155, 0.10);
  --color-entry-text: var(--athar-cyan-600);
  --color-line-entry: rgba(27, 130, 155, 0.34);

  --color-success: var(--athar-teal-700);
  --color-success-soft: rgba(0, 201, 183, 0.12);
  --color-warning: var(--athar-amber-600);
  --color-warning-soft: rgba(162, 104, 28, 0.12);
  --color-danger: var(--athar-red-600);
  --color-danger-soft: rgba(186, 26, 26, 0.10);

  /* Delta on paper. The specified pair is tuned for a #111827 card and
     neither colour survives the trip to white — #10b981 reads 2.5:1
     there and #f43f5e 3.7:1, both under the 4.5:1 that eyebrow-sized
     text needs. Same two hues, taken to the depth paper wants, with
     the vibrance left in the tint behind them. */
  --color-delta-up: #047857;
  --color-delta-up-soft: rgba(4, 120, 87, 0.1);
  --color-delta-down: #be123c;
  --color-delta-down-soft: rgba(190, 18, 60, 0.1);

  --color-chart-primary: #6d28d9;
  --color-chart-petrol: var(--athar-cyan-600);
  --color-chart-glow: rgba(109, 40, 217, 0.28);

  --color-focus: var(--athar-teal-700);

  --color-scrim: rgba(23, 26, 29, 0.42);

  /* Paper takes the wash the other way round: the tint has to sit
     above the canvas rather than below it, so these are lighter and
     the top stop is a real colour instead of a white lift. */
  --hero-wash-brand: rgba(0, 201, 183, 0.10);
  --hero-wash-deep: rgba(27, 130, 155, 0.14);
  --hero-wash-top: rgba(255, 255, 255, 0.55);

  /* Paper's band. Cooler than the canvas rather than darker, so the
     step reads as a change of light and not as a grey rectangle. */
  --color-canvas-band: #f1f4f8;

  /* Same anchor, deliberately unchanged. A dark section on paper is
     the strongest move the page has; making it lighter here would
     spend the move and buy nothing. */
  --color-canvas-anchor: var(--athar-on-tertiary);
  --color-anchor-line: rgba(132, 219, 246, 0.20);

  --grain-opacity: 0.03;

  --shadow-sm: 0 1px 2px rgba(23, 26, 29, 0.06);
  --shadow-md: 0 8px 24px rgba(23, 26, 29, 0.07);
  --shadow-lg: 0 24px 64px rgba(23, 26, 29, 0.09);
  --shadow-glow: 0 0 0 1px rgba(172, 122, 79, 0.22), 0 20px 48px rgba(23, 26, 29, 0.08);

  /* Paper takes about a quarter of the ink the dark ground does, and
     takes it in the neutral the text is set in rather than in black.
     --shadow-soft and --shadow-deep are not restated: they read
     these two and re-tint themselves. */
  --shadow-ink: 23 26 29;
  --shadow-strength: 0.3;

  /* The rim and the bloom are dark-mode effects. Neon edge light on
     paper reads as a rendering artefact, so both are off. */
  --rim-light: none;
  --shadow-bloom: none;

  --color-focus-soft: rgba(10, 125, 115, 0.24);

  color-scheme: light;
}


/* ---------------------------------------------------------
   Reset
   --------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  position: relative;
  margin: 0;
  background: var(--color-canvas);
  color: var(--color-text);
  font-family: var(--font-body-ar);
  font-size: var(--fs-base);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Film grain over the whole page. Perfectly flat digital colour is
   the tell of a generated interface; a little tooth makes the
   surface read as printed. Never interactive, never in print. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  background-image: var(--texture-grain);
  opacity: var(--grain-opacity);
  mix-blend-mode: overlay;
}
@media print {
  body::after { display: none; }
}

img,
svg,
video { max-width: 100%; display: block; }

button,
input,
select,
textarea { font: inherit; color: inherit; }

ul, ol { margin: 0; padding: 0; list-style: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------------------------------------------------------
   Typography — bilingual
   Arabic and Latin do not share a rhythm. Latin display text
   gets negative tracking to close its wide sidebearings;
   Arabic gets none, because letter-spacing breaks the joins
   between glyphs. Every rule below is split on that fact.
   --------------------------------------------------------- */
:root[data-lang="en"] body { font-family: var(--font-latin); }

h1, h2, h3, h4, h5 {
  margin: 0 0 var(--space-4);
  font-family: var(--font-display-ar);
  font-weight: var(--fw-bold);
  line-height: var(--lh-tight);
  color: var(--color-text);
}

:root[data-lang="en"] h1,
:root[data-lang="en"] h2,
:root[data-lang="en"] h3,
:root[data-lang="en"] h4,
:root[data-lang="en"] h5 {
  font-family: var(--font-latin);
  letter-spacing: var(--tracking-tight);
}

h1 { font-size: var(--fs-h1); }
h2 { font-size: var(--fs-h2); }
h3 { font-size: var(--fs-h3); line-height: var(--lh-snug); }
h4 { font-size: var(--fs-h4); font-weight: var(--fw-bold); line-height: var(--lh-snug); }

p {
  margin: 0 0 var(--space-4);
  color: var(--color-text-secondary);
}

a {
  color: var(--color-primary-text);
  text-decoration: none;
  transition: color var(--dur-fast) var(--ease-out);
}
a:hover { color: var(--color-growth-text); }

strong { font-weight: var(--fw-bold); color: var(--color-text); }

code {
  font-family: var(--font-mono);
  font-size: 0.875em;
  letter-spacing: 0;
}

/* Numerals stay Latin and tabular in both languages so figures in
   tables and KPI tiles align on the decimal. */
.u-figure {
  font-family: var(--font-figures);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: var(--tracking-tight);
}

/* Emphasis inside a headline. Brass text over a brass rule, the way
   a copy editor would mark a word — not a gradient fill. The full
   logo ramp stays reserved for the trace rail and the wordmark. */
.u-word-mark {
  color: var(--color-gold-text);
  text-decoration: underline;
  text-decoration-color: var(--color-line-gold);
  text-decoration-thickness: 2px;
  text-underline-offset: 0.2em;
  text-decoration-skip-ink: none;
}

/* ---------------------------------------------------------
   Eyebrow
   A section label, not decoration: it names the section for
   screen readers and for scanning. Latin gets caps + tracking;
   Arabic has no case, so it earns its rank from the gold node
   instead of from letterforms it does not have.
   --------------------------------------------------------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0 0 var(--space-4);
  font-family: var(--font-body-ar);
  font-size: var(--fs-eyebrow);
  font-weight: var(--fw-bold);
  color: var(--color-gold-text);
}
.eyebrow::before {
  content: "";
  width: 5px;
  height: 5px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--color-gold);
}
:root[data-lang="en"] .eyebrow {
  font-family: var(--font-latin);
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
}

/* A gold hairline. The only decorative rule in the system, and
   only where a real boundary exists. */
.hairline {
  height: 1px;
  border: 0;
  margin: var(--space-10) 0;
  background: linear-gradient(
    to left,
    transparent,
    var(--color-line-gold) 20%,
    var(--color-line-gold) 80%,
    transparent
  );
}

/* ---------------------------------------------------------
   Layout
   --------------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--space-6);
}

/* One beat, and the ground carries the emphasis.

   Band and anchor used to take --space-32 while the canvas took
   --space-24, on the theory that a tinted section should also breathe
   wider. What that bought was arithmetic: three different silences for
   the same event, and no reader can hear a rhythm in that. So every
   full section is one number — .section, .section--band and
   .section--anchor all resolve to the same padding, and a tinted
   section says "this is a different thing" with its ground alone.

   That number is 96px (owner's spec, 2026-09-08). It was raised to
   192px on 2026-09-06 and measured badly: the padding came to about
   half the height of every section on the page — 384px of seam around
   a 300px card in the maker's note — so the page read as air with
   content in it. 96px is where it was before that, and the number the
   rest of this comment was written about.

   **The seam is twice the number.** Vertical padding does not collapse
   between siblings that carry a background, so the space a reader sees
   between two sections is the sum of the two that meet: 96px each read
   as a 192px seam, and 192px each reads as 384px. 192px of seam is a
   fifth of a 900px viewport, which reads as a break; 384px was a third
   of it, which reads as the page having ended. The phone step below
   exists for the same reason, one size down.

   The strip keeps half the section's value, as it always has. */
.section { padding-block: var(--space-24); }
.section--tight { padding-block: var(--space-16); }

/* The thin band — a marquee, a row of facts. It wears the same tint
   as a full band but is punctuation, not a section, so it takes the
   short step and never the section one. Before this existed the three
   strips on the home page carried 18px, 0px and 32px, all of them
   chosen by hand and none of them the same. */
.section--strip { padding-block: var(--space-12); }

/* Below the tablet break every step drops one rung: 96·64·48 becomes
   64·48·32. Remove this block to run the desktop ladder at every
   width. */
@media (max-width: 900px) {
  .section { padding-block: var(--space-16); }
  .section--tight { padding-block: var(--space-12); }
  .section--strip { padding-block: var(--space-8); }
}

/* Two sections on the same ground read as one long column, and the
   home page had two such seams. The tint separates wherever the
   ground changes; where it does not change, a hairline does — which
   is the complement of the rule below, not a contradiction of it. */
.section + .section:not(.section--band):not(.section--anchor),
.section--band + .section--band {
  border-block-start: 1px solid var(--color-line);
}
.section--anchor + .section:not(.section--band):not(.section--anchor),
.section--band + .section:not(.section--band):not(.section--anchor) {
  border-block-start: 0;
}

/* The alternating ground. No border: the tint is the separation, and
   a hairline on top of it turns one soft step into two hard ones. */
.section--band {
  background: var(--color-canvas-band);
}

/* The anchor — a section that changes the room rather than the light.
   It is dark in both themes, so it restates the local colour tokens
   instead of asking every component inside it to know where it is:
   a card, a heading and a muted caption keep their own rules and
   simply resolve against a different ground.

   Two per page, and far apart. Three was the old budget and the home
   page spent all three inside five sections of each other, with a
   single canvas section between each — which is how the loudest move
   the layout has turned into a flicker. */
.section--anchor {
  background: var(--color-canvas-anchor);

  --color-text: #eef4f6;
  --color-text-secondary: rgba(216, 232, 237, 0.76);
  /* 0.62, not the 0.56 this started at: on this ground that lands at
     4.39:1, which is under AA for anything not set large. 0.62 clears
     it at 5.06:1 and is still visibly the quietest text here. */
  --color-text-muted: rgba(199, 219, 226, 0.62);

  /* Surfaces lift out of the anchor, never off the page palette —
     a graphite card on a teal-navy ground reads as a mistake. The
     anchor holds one value in both themes, so these do too. */
  --color-surface: #00404e;
  --color-surface-raised: #0a4f5e;
  --color-surface-sunken: #002a34;
  --color-surface-highest: #125d6d;

  --color-line: var(--color-anchor-line);
  --color-line-strong: rgba(139, 197, 209, 0.3);

  /* The palette's own primary and secondary, not their container
     steps: a fill that reads as brand on the graphite canvas is a
     shade too deep to carry a link on this petrol ground. */
  --color-primary-text: var(--athar-primary);
  --color-gold-text: var(--athar-secondary);

  /* Casting in black over a dark ground would show as a smudge, so
     shadows here are nearly off and depth comes from the surface step. */
  --shadow-ink: 4 22 30;
  --shadow-strength: 0.85;
}

.stack > * + * { margin-top: var(--space-4); }
.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}
.grid {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
}

/* ---------------------------------------------------------
   Focus — one visible ring everywhere, keyboard only
   --------------------------------------------------------- */
:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
  border-radius: var(--radius-xs);
}

.skip-link {
  position: absolute;
  inset-inline-start: var(--space-4);
  top: var(--space-4);
  z-index: 100;
  padding: var(--space-3) var(--space-5);
  border-radius: var(--radius-sm);
  background: var(--color-surface-raised);
  color: var(--color-text);
  transform: translateY(-200%);
  transition: transform var(--dur-base) var(--ease-out);
}
.skip-link:focus-visible { transform: translateY(0); }

/* ---------------------------------------------------------
   Reveal on scroll
   One short fade with a few pixels of travel, once. shell.js adds
   .is-revealed as an element enters; with reduced motion, or with
   JavaScript off, the element is simply visible.
   --------------------------------------------------------- */
/* --reveal-delay lets a row of siblings arrive in order rather than
   all at once. They enter the viewport on the same frame, so without
   it a grid of cards is one block appearing, which is a flash and not
   a movement. Default zero: an element that does not set it behaves
   exactly as it did before. */
:root.js [data-reveal] {
  opacity: 0;
  translate: 0 14px;
  transition:
    opacity var(--dur-slow) var(--ease-out),
    translate var(--dur-slow) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}
:root.js [data-reveal].is-revealed {
  opacity: 1;
  translate: 0 0;
}
@media (prefers-reduced-motion: reduce) {
  :root.js [data-reveal] { opacity: 1; translate: 0 0; }
}

/* ---------------------------------------------------------
   Utilities
   --------------------------------------------------------- */
.u-muted { color: var(--color-text-muted); }
.u-secondary { color: var(--color-text-secondary); }
.u-lead { font-size: var(--fs-lg); line-height: var(--lh-relaxed); }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* components/brand.css */
/* =========================================================
   Component — Brand lockup

   The mark is raster artwork (the owner's logo, keyed to
   transparency), so its colour is fixed and cannot follow the
   theme. That is correct: a logo that changes colour with the
   interface is no longer a logo. Only the wordmark beside it
   takes a token, because it is type, not artwork.
   ========================================================= */
.brand {
  --brand-mark: 38px;
  --brand-word: 1rem;

  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  text-decoration: none;
  color: var(--color-text);
}

.brand__mark {
  height: var(--brand-mark);
  width: auto;
  flex: none;
  /* The artwork was drawn to sit on a dark ground; on paper it keeps
     its own weight, so it gets no filter — only a hair of contrast
     lift so the deepest blues do not muddy against warm white. */
  transition: transform var(--dur-base) var(--ease-out);
}
.brand:hover .brand__mark { transform: translateY(-1px); }

/* Latin wordmark. Wide tracking and a heavy weight, matching the
   lockup the owner supplied. Solid, never gradient. */
.brand__wordmark {
  font-family: var(--font-latin);
  font-size: var(--brand-word);
  font-weight: var(--fw-bold);
  letter-spacing: 0.17em;
  line-height: 1;
  color: var(--color-text);
  /* the tracking adds a trailing gap; pull it back so the lockup
     stays optically centred */
  margin-inline-end: -0.17em;
}

.brand--mark .brand__wordmark { display: none; }

.brand--sm { --brand-mark: 30px; --brand-word: 0.82rem; }
.brand--lg { --brand-mark: 60px; --brand-word: 1.45rem; }

@media (max-width: 420px) {
  .brand { --brand-mark: 32px; --brand-word: 0.86rem; }
}

/* The mark is the way home from every page. On a phone it is also
   a 32px target sitting beside a 48px burger. */
@media (pointer: coarse) {
  a.brand { min-block-size: 44px; }
}

/* components/button.css */
/* =========================================================
   Component — Button
   The brand gradient is deliberately absent here. It belongs to
   the trace, the logo, and one word per page. A solid button
   next to a gradient trace reads as restraint; two gradients
   read as a template.
   ========================================================= */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--color-text);
  --btn-border: transparent;
  --btn-pad-block: 0.7rem;
  --btn-pad-inline: 1.4rem;

  /* Two layers: an optional sheen over the fill. A flat button that
     is only its fill is the tell of a template; the same fill with
     a highlight on its top edge reads as an object with thickness.
     Off by default — the sheen is spent on filled buttons only,
     where there is a surface for the light to fall on. */
  --btn-sheen: none;
  --btn-shadow: none;

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--btn-pad-block) var(--btn-pad-inline);
  border: 1px solid var(--btn-border);
  border-radius: var(--radius-sm);
  background: var(--btn-sheen), var(--btn-bg);
  box-shadow: var(--btn-shadow);
  color: var(--btn-fg);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  line-height: 1.2;
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    transform var(--dur-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
}

/* The ring follows the radius, which an outline does not. */
.btn:focus-visible {
  outline: none;
  box-shadow: var(--ring-focus), var(--btn-shadow);
}

.btn__icon {
  width: 1.05em;
  height: 1.05em;
  flex: none;
}

/* Primary — the one action a screen is asking for.

   The fill is the brand ramp rather than a flat colour, which is
   what the design system asks a conversion trigger to look like.
   Gradients are safe here because both ends were measured against
   the same ink: --color-on-primary clears 8.4:1 on the light stop
   and 6.4:1 on the dark one, so there is no corner of the button
   where the label stops being readable.

   The sheen still lies over that fill rather than replacing it —
   white and black alpha only, so it moves the top and bottom
   edges without touching either contrast figure. */
.btn--primary {
  --btn-bg: var(--gradient-primary);
  --btn-fg: var(--color-on-primary);
  --btn-sheen: var(--gradient-sheen);
  --btn-shadow: var(--shadow-soft);
}
.btn--primary:hover {
  --btn-bg: var(--color-primary-hover);
  /* --shadow-bloom is `none` in light mode, and `box-shadow: <list>,
     none` is invalid as a whole — so on paper the primary button was
     losing its hover shadow entirely rather than losing the glow.
     The halo is written here instead, in a form that resolves in
     both themes. */
  --btn-shadow:
    var(--shadow-deep),
    0 0 18px color-mix(in srgb, var(--color-primary) 34%, transparent);
}

/* Secondary — a real action that is not the page's one ask.
   A frame in the brand colour over a faint wash of it: it reads as a
   button from across the screen without competing with the filled
   primary beside it. */
.btn--secondary {
  --btn-bg: color-mix(in srgb, var(--color-primary) 8%, transparent);
  --btn-fg: var(--color-text);
  --btn-border: color-mix(in srgb, var(--color-primary) 50%, transparent);
}
.btn--secondary:hover {
  --btn-bg: var(--color-primary-soft);
  --btn-border: var(--color-primary);
  --btn-fg: var(--color-primary-text);
}

/* Ghost — the quiet register: going back, a side option, a toolbar.
   It used to be bare grey text, which merchants read as a label rather
   than something to press (owner, 2026-09-16). Now a neutral frame on
   a surface one step up: still the quietest button, but a button. */
.btn--ghost {
  --btn-bg: var(--color-surface-raised);
  --btn-fg: var(--color-text);
  --btn-border: var(--color-line-strong);
}
.btn--ghost:hover {
  --btn-bg: var(--color-primary-soft);
  --btn-border: var(--color-growth);
}

/* Danger — what cannot be taken back: delete, remove, cancel a
   renewal. A red frame and wash at rest, solid red under the pointer,
   so it is never mistaken for the row's ordinary verb. */
.btn--danger {
  --btn-bg: var(--color-danger-soft);
  --btn-fg: var(--color-danger);
  --btn-border: color-mix(in srgb, var(--color-danger) 45%, transparent);
}
.btn--danger:hover {
  --btn-bg: var(--color-danger);
  --btn-fg: var(--color-on-error);
  --btn-border: var(--color-danger);
}

/* Gold — reserved for upgrade / premium. If it appears twice on
   one screen, one of them is wrong. */
.btn--gold {
  --btn-bg: var(--color-gold-soft);
  --btn-fg: var(--color-gold-text);
  --btn-border: var(--color-line-gold);
}
.btn--gold:hover {
  --btn-bg: var(--color-gold);
  --btn-fg: var(--color-on-secondary);
  --btn-border: var(--color-gold);
  --btn-sheen: var(--gradient-sheen);
  --btn-shadow: var(--shadow-soft);
}

.btn--lg {
  --btn-pad-block: 0.95rem;
  --btn-pad-inline: 1.9rem;
  font-size: var(--fs-base);
}
.btn--sm {
  --btn-pad-block: 0.45rem;
  --btn-pad-inline: 0.9rem;
  font-size: var(--fs-xs);
}
.btn--block { width: 100%; }

/* Icon-only — square, and it must still carry an aria-label */
.btn--icon {
  --btn-pad-block: 0.6rem;
  --btn-pad-inline: 0.6rem;
  --btn-border: var(--color-line);
}
.btn--icon:hover { --btn-border: var(--color-line-strong); }

/* ---------------------------------------------------------
   Action — the row's own verb

   For the one thing a table row exists to do: open it. It is
   louder than a ghost and quieter than the page's primary
   button, which is the register a list of rows needs — twenty
   filled blue buttons down a column is a list where nothing
   is more important than anything else.

   The soft primary wash rather than a solid fill for the same
   reason: it reads as a control at a glance without competing
   with the one filled button the screen actually has.
   --------------------------------------------------------- */
.btn--action {
  --btn-bg: var(--color-primary-soft);
  --btn-fg: var(--color-primary-text);
  --btn-border: transparent;
  font-weight: var(--fw-bold);
}
.btn--action:hover {
  --btn-bg: var(--color-primary);
  --btn-fg: var(--color-on-primary);
  --btn-shadow: var(--shadow-soft);
  /* 1.02, not more: a button that grows in a dense row pushes
     its neighbours in the eye even when the layout holds. */
  transform: scale(1.02);
}
.btn--action:active { transform: scale(0.99); }

/* A row action under a finger has nothing to hover from, and a
   scale that only fires on tap reads as a stutter. */
@media (hover: none) {
  .btn--action:hover { transform: none; }
}

/* ---------------------------------------------------------
   Touch floor

   A finger does not aim as well as a cursor. On a coarse
   pointer every button clears 48px of height, and the page's
   primary ask goes full width once the row can no longer hold
   two buttons side by side.
   --------------------------------------------------------- */
@media (pointer: coarse) {
  .btn { min-block-size: 48px; }
  .btn--sm { min-block-size: 44px; }
}

/* components/busy.css */
/* =========================================================
   Component — Busy

   What a slow request looks like.

   Until now it looked like nothing. A merchant uploading a 40 MB
   clip, or a shopper on a Syrian mobile connection pressing «أكمل
   الطلب», pressed a button that did not change, waited on a page
   that did not change, and pressed it again — which is how a
   checkout becomes two orders and an upload becomes two files.

   The whole system hangs off one attribute, `aria-busy="true"`,
   which is what the platform is already obliged to set for screen
   readers. js/busy.js sets it on the form and on the button that
   submitted it, and everything below follows from that: no state
   class to keep in sync, no second vocabulary, and a control that
   is announced as busy is also drawn as busy.

   Everything animated here is a transform or an opacity on a
   pseudo-element, so nothing below reflows the page or repaints a
   layer the browser cannot promote. The reduced-motion rule in
   app.css freezes every animation on the platform, so both
   indicators have a still form that still reads as «working» —
   otherwise reduced motion would mean no feedback at all, which is
   the bug this file exists to fix.
   ========================================================= */

/* ---------------------------------------------------------
   The button

   The label is not removed and the button is not resized: the
   label goes transparent in place and a ring is drawn over it.
   Swapping the text for a spinner re-measures the button, which
   moves the row it sits in at the exact moment the person is
   still looking at where they clicked.

   Not [disabled]: a disabled submit button is dropped from the
   payload it was about to send, so a form that reads which button
   was pressed loses that. It is taken out of reach with
   pointer-events instead, and js/busy.js refuses the second
   submit outright.
   --------------------------------------------------------- */
.btn[aria-busy="true"] {
  color: transparent;
  pointer-events: none;
  cursor: progress;
}

/* The icon inside a button paints in currentColor, which is now
   transparent — but an <svg> with its own fill would still show
   through the ring, so the whole label is hidden as one. */
.btn[aria-busy="true"] > * {
  opacity: 0;
}

.btn[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 1.15em;
  height: 1.15em;
  margin-block-start: -0.575em;
  margin-inline-start: -0.575em;
  border-radius: var(--radius-pill);

  /* Half a ring drawn, half left open — the gap is what makes the
     rotation legible; a closed ring spinning looks still.

     Two borders coloured and two left transparent, rather than one
     translucent ring under one opaque arc: no colour function is
     involved, so there is nothing here a browser can fail to parse
     and leave the merchant with an invisible spinner.

     --btn-fg rather than currentColor, because currentColor is the
     transparent the label was just set to. */
  border: 2px solid transparent;
  border-block-start-color: var(--btn-fg);
  border-inline-end-color: var(--btn-fg);

  animation: athar-spin var(--dur-spin) var(--ease-linear) infinite;
  will-change: transform;
}

@keyframes athar-spin {
  to { transform: rotate(1turn); }
}

/* ---------------------------------------------------------
   The form

   A thread of light along the top edge, crawling. It is drawn on
   the form itself rather than on the page, so the person's eye
   does not have to leave the thing they are waiting on — and it
   costs one composited pseudo-element rather than a fixed overlay
   that would have to sit above the grain layer.

   The fields go quiet but stay readable: a form that greys out
   entirely while it uploads hides the very data the person is
   waiting to see accepted.
   --------------------------------------------------------- */
form[aria-busy="true"] {
  position: relative;
  cursor: progress;
}

form[aria-busy="true"] .field__control,
form[aria-busy="true"] .field__check,
form[aria-busy="true"] .field__file {
  opacity: 0.62;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}

form[aria-busy="true"]::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  border-radius: var(--radius-pill);
  background: var(--color-primary-soft);
  pointer-events: none;
}

form[aria-busy="true"]::after {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  width: 34%;
  height: 2px;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
  pointer-events: none;

  animation: athar-crawl var(--dur-crawl) var(--ease-in-out) infinite;
  will-change: transform;
}

/* Travels the width of the form and back, in transform only. The
   percentages are of the bar's own 34%, so the two ends land flush
   against the form's edges at any width. */
@keyframes athar-crawl {
  0% { transform: translateX(0); }
  50% { transform: translateX(194%); }
  100% { transform: translateX(0); }
}

/* RTL runs the other way: translateX is physical, and a progress
   bar that crawls away from the reading edge reads as rewinding. */
[dir="rtl"] form[aria-busy="true"]::after {
  animation-name: athar-crawl-rtl;
}

@keyframes athar-crawl-rtl {
  0% { transform: translateX(0); }
  50% { transform: translateX(-194%); }
  100% { transform: translateX(0); }
}

/* ---------------------------------------------------------
   A busy region that is neither

   A panel refreshing in place, a table reloading a page of rows.
   Same attribute, same vocabulary, no spinner: the content dims
   and stops taking clicks, which is all a region needs to say.
   --------------------------------------------------------- */
[data-busy-region][aria-busy="true"] {
  opacity: 0.55;
  pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
}

/* ---------------------------------------------------------
   Reduced motion

   app.css cuts every animation on the platform to 0.01ms, so both
   indicators above stop dead — the spinner freezes at whatever
   angle it started on and the bar parks at the inline start. Left
   alone, that is a loading state that does not load.

   So under reduced motion they become still marks instead: a
   complete ring rather than a stalled three-quarter one, and a
   full-width bar rather than a stalled third of one. Neither
   moves, both say the same thing, and aria-busy is carrying the
   message to anybody not looking at either.
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .btn[aria-busy="true"]::after {
    border-color: var(--btn-fg);
  }

  form[aria-busy="true"]::after {
    width: 100%;
    opacity: 0.7;
  }
}

/* A link that is navigating.

   Same ring the buttons get, and the same reason it is not a
   width change: the row must not move while the next page is
   still loading. */
a.btn[aria-busy="true"] {
  color: transparent;
  pointer-events: none;
  cursor: progress;
}

/* components/badge.css */
/* =========================================================
   Component — Badge
   Status, plan tier, and delta. Colour alone never carries the
   meaning: every badge also carries a word.
   ========================================================= */
.badge {
  --badge-bg: var(--color-primary-soft);
  --badge-fg: var(--color-primary-text);

  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0.25rem 0.65rem;
  border-radius: var(--radius-pill);
  background: var(--badge-bg);
  color: var(--badge-fg);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 1.5;
  white-space: nowrap;
}

.badge--success { --badge-bg: var(--color-success-soft); --badge-fg: var(--color-success); }
.badge--warning { --badge-bg: var(--color-warning-soft); --badge-fg: var(--color-warning); }
.badge--danger  { --badge-bg: var(--color-danger-soft);  --badge-fg: var(--color-danger); }
.badge--growth  { --badge-bg: var(--color-growth-soft);  --badge-fg: var(--color-growth-text); }
.badge--neutral { --badge-bg: var(--color-surface-sunken); --badge-fg: var(--color-text-secondary); }

/* Entry tier — the first paid plan, and nothing else */
.badge--entry {
  --badge-bg: var(--color-entry-soft);
  --badge-fg: var(--color-entry-text);
  box-shadow: inset 0 0 0 1px var(--color-line-entry);
}

/* Gold tier — the highest plan, and nothing else */
.badge--gold {
  --badge-bg: var(--color-gold-soft);
  --badge-fg: var(--color-gold-text);
  box-shadow: inset 0 0 0 1px var(--color-line-gold);
}

.badge__dot {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--radius-pill);
  background: currentColor;
}

.badge__figure {
  font-family: var(--font-figures);
  font-variant-numeric: tabular-nums;
}

/* Pill used as a hero eyebrow — larger, quieter */
.badge--hero {
  --badge-bg: var(--color-surface-raised);
  --badge-fg: var(--color-text-secondary);
  padding: 0.45rem 1rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  box-shadow: inset 0 0 0 1px var(--color-line);
}

/* Small — a row of them under a card's note, where the standard
   size would outweigh the name above it. */
.badge--sm {
  padding: 0.1rem var(--space-2);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
}

/* components/card.css */
/* =========================================================
   Component — Card
   ========================================================= */
.card {
  --card-bg: var(--color-surface);
  --card-border: var(--color-line);
  --card-pad: var(--space-panel);
  --card-shadow: var(--shadow-soft);

  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--card-pad);
  border: 1px solid var(--card-border);
  border-radius: var(--radius-premium);
  background: var(--card-bg);
  box-shadow: var(--card-shadow);
  transition:
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out),
    transform var(--dur-base) var(--ease-out);
}

.card--interactive:hover {
  --card-border: var(--color-line-strong);
  --card-shadow: var(--shadow-deep);
  transform: translateY(-2px);
}

/* Raised — for the one card that carries the page's weight. The
   teal glow is the only ambient light in the system. */
.card--raised {
  --card-bg: var(--color-surface-raised);
  /* The glow is the gold hairline and the ambient teal; the depth
     under it is what makes it sit above its neighbours rather than
     merely glowing on the same plane. */
  --card-shadow: var(--shadow-glow), var(--shadow-deep), var(--rim-light);
}

/* Premium — the gold hairline earns its place only on a plan or
   feature that actually costs more. */
.card--premium {
  --card-border: var(--color-line-gold);
  position: relative;
  overflow: hidden;
}
.card--premium::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--color-gold);
  opacity: 0.7;
}

.card__title {
  margin: 0;
  font-size: var(--fs-h4);
  color: var(--color-text);
}
.card__body {
  margin: 0;
  color: var(--color-text-secondary);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
}
.card__footer {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-4);
}

/* KPI tile — the dashboard's smallest unit of meaning */
.card--kpi { --card-pad: var(--space-5); gap: var(--space-2); }
.card--kpi .card__title {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--color-text-secondary);
}
.card__figure {
  font-family: var(--font-figures);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-tight);
  color: var(--color-text);
}
.card--kpi.is-active {
  --card-bg: var(--color-primary);
  --card-border: var(--color-primary);
}
.card--kpi.is-active .card__title,
.card--kpi.is-active .card__figure { color: var(--color-on-primary); }

/* The line under a KPI: what the figure is measured over, or why there is
   no figure. Muted and small — it is context for the number, never a second
   number competing with it. */
.card__note {
  margin: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--color-text-muted);
}

/* components/trace.css */
/* =========================================================
   Component — Trace  ·  «الأثر»
   ---------------------------------------------------------
   The signature of the system, and the only place the full logo
   gradient is spent.

   A hairline rail down the reading edge (the right, in RTL). A
   single head travels it as you scroll; everything it has passed
   is gradient, everything ahead is a faint line. It is the brand
   name rendered as behaviour: a mark left behind by movement.

   Gold stops mark the section boundaries and light as you reach
   them, so the rail also answers "where am I on this page".
   ========================================================= */
/* The rail is pinned to the reading edge of the viewport — the right
   in RTL, the left in LTR — outside the content column, so it never
   competes with the text for width. */
.trace-rail {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 40;
  width: var(--rail-width);
  pointer-events: none;
}

.trace {
  position: sticky;
  top: 0;
  display: flex;
  justify-content: center;
  height: 100vh;
  height: 100dvh;
}

.trace__track {
  position: relative;
  width: 2px;
  margin-block: var(--space-16);
  border-radius: var(--radius-pill);
  background: var(--color-line-strong);
}

.trace__fill {
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 100%;
  background: var(--gradient-trace);
  transform: scaleY(var(--trace-progress, 0));
  transform-origin: top;
  transition: transform 120ms linear;
}

.trace__head {
  position: absolute;
  inset-inline-start: 50%;
  top: calc(var(--trace-progress, 0) * 100%);
  translate: -50% -50%;
  width: 10px;
  height: 10px;
  border-radius: var(--radius-pill);
  background: var(--color-growth);
  box-shadow:
    0 0 0 5px var(--color-growth-soft),
    0 0 18px 2px var(--color-growth-soft);
  transition: top 120ms linear;
}

.trace__stop {
  position: absolute;
  inset-inline-start: 50%;
  translate: -50% -50%;
  width: 6px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-line-strong);
  transition:
    background var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.trace__stop.is-lit {
  background: var(--color-gold);
  box-shadow: 0 0 0 3px var(--color-gold-soft);
}

/* Below the rail breakpoint the trace becomes a progress bar at
   the top of the viewport — same idea, same gradient, no column. */
.trace-bar {
  position: fixed;
  inset-block-start: 0;
  inset-inline: 0;
  z-index: 60;
  height: 3px;
  display: none;
  background: var(--gradient-trace);
  transform: scaleX(var(--trace-progress, 0));
  transform-origin: right;
}
:root[dir="ltr"] .trace-bar { transform-origin: left; }

/* Below the point where the rail would crowd the content column it
   is dropped entirely and the bar takes over. */
@media (max-width: 1180px) {
  .trace-rail { display: none; }
  .trace-bar { display: block; }
}

@media (prefers-reduced-motion: reduce) {
  .trace__fill,
  .trace__head { transition: none; }
}

/* components/section-head.css */
/* =========================================================
   Component — Section head

   The block that opens a section: eyebrow, title, lead. Lived in
   pages/home.css until a second page needed it (م2), which is the
   point at which the constitution says to promote it.
   ========================================================= */
.section-head {
  max-width: 56ch;
  margin-bottom: var(--space-12);
}

/* Arabic headlines here run two lines at most; balancing them keeps
   a single word from being left alone on the second. */
.section-head__title {
  margin: 0 0 var(--space-4);
  text-wrap: balance;
}
.section-head__lead {
  margin: 0;
  font-size: var(--fs-lg);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

/* A centred head, for the sections that lead a grid rather than sit
   beside a picture. */
.section-head--center {
  max-width: 46rem;
  margin-inline: auto;
  text-align: center;
}
.section-head--center .eyebrow { justify-content: center; }

/* components/checks.css */
/* =========================================================
   Component — Check list

   A short list of what something includes. The tick is drawn from
   two borders on a pseudo-element, so it inherits colour and needs
   no icon file.
   ========================================================= */
.checks {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-block: var(--space-6) var(--space-8);
}
.checks__item {
  position: relative;
  padding-inline-start: var(--space-8);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--color-text-secondary);
}
.checks__item::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.25em;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-pill);
  background: var(--color-growth-soft);
}
.checks__item::after {
  content: "";
  position: absolute;
  inset-inline-start: 6px;
  top: 0.55em;
  width: 6px;
  height: 3px;
  border-left: 1.7px solid var(--color-growth);
  border-bottom: 1.7px solid var(--color-growth);
  rotate: -45deg;
}

/* components/plan-card.css */
/* =========================================================
   Component — Plan card and the billing switch

   Promoted out of pages/pricing.css when the home page needed the
   same four cards under its own heading (م15). Two pages showing
   the same prices in two different cards is how a table and a page
   start disagreeing about what a plan costs, and the constitution
   (§4.2) says the second use is the moment to lift.

   The switch is a segmented control and the prices are both already
   in the markup — see the note on the [data-cycle] rule below — so
   nothing here writes a number and a visitor with JavaScript off
   still reads a complete monthly page.
   ========================================================= */

/* ---------------------------------------------------------
   Cycle switch

   A segmented control, not a toggle switch: two written words
   beat a slider whose left and right have to be learned — and
   in RTL a slider's direction is a coin toss.
   --------------------------------------------------------- */
.pr-cycle {
  display: inline-flex;
  gap: var(--space-1);
  padding: var(--space-1);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
}

.pr-cycle__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-6);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-secondary);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}
.pr-cycle__btn:hover { color: var(--color-text); }
.pr-cycle__btn.is-on {
  background: var(--color-surface-raised);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}

.pr-cycle__save { padding-block: 2px; }

/* ---------------------------------------------------------
   Plan cards
   --------------------------------------------------------- */
/* stretch, not start. The tiers carry four, five and six features, so
   on `start` each card ended wherever its own list ran out and the row
   closed on a staircase — four different bottom edges under four price
   lines that are meant to be read across. Stretching costs a little
   white space inside the shorter cards and buys one baseline for the
   row, which is what makes it read as a comparison. */
.pr-plans__grid {
  display: grid;
  gap: var(--space-5);
  grid-template-columns: repeat(4, 1fr);
  align-items: stretch;
}

/* Three, when the free tier has been lifted out of the row and stood on
   its own. It is the shape the reference is drawn for, and the one the
   tuck below is honest in: with three cards the chosen tier really is
   the middle one, so the two beside it lean in symmetrically instead of
   shingling from one end. */
.pr-plans__grid--trio { grid-template-columns: repeat(3, 1fr); }

.pr-card {
  position: relative;
  gap: var(--space-2);
  height: 100%;
}

/* ---------------------------------------------------------
   The tier accent

   One colour per tier, taken in order off the mark's own ramp —
   blue at the foot, teal in the middle, brass at the top — and spent
   at three different strengths, which is the part that matters. Three
   equal accents would say "three equally special things" and delete
   the recommendation the row exists to make. So the entry tier gets a
   rule at its head and nothing else, the top tier gets the rule and a
   hairline, and the chosen one keeps the full border, the raised
   surface and the only filled button in the row.

   --tier is the single property all of that reads, so a tier's colour
   is set in one place and its badge, its rule and its border cannot
   drift apart.
   --------------------------------------------------------- */
/* overflow:hidden is a fix, not a style.

   The tier rule below is a 2px bar pinned to inset-block-start: 0 and
   stretched inset-inline: 0. The card is rounded; the bar is not, and
   nothing was clipping it — so it ran straight out to both top
   corners and squared them off, and the card read as having a
   straight top edge on rounded sides. The flag badge in the corner
   had the same problem and was carrying hand-written corner radii to
   hide it.

   Clipping to the border box fixes both at the source. */
.pr-card {
  --tier: transparent;
  overflow: hidden;
}

.pr-card--entry { --tier: var(--color-entry); }
.pr-card--featured {
  --tier: var(--color-growth);
  --card-border: var(--color-growth);
}
.pr-card--premium { --tier: var(--color-gold); }

/* The rule at the head of the card, in the tier's colour. It was the
   premium shell's alone; every tier carries one now, and .card--premium
   in card.css is left as it is because four other pages use its gold. */
.pr-card--entry::before,
.pr-card--featured::before,
.pr-card--premium::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  height: 2px;
  background: var(--tier);
  opacity: 0.75;
}

/* The flag sits in the corner, inside the card.

   It used to hang above the top edge, which cost the premium shell its
   overflow clip (so the gold rule had to be inset by a corner radius to
   stop it escaping) and left a badge floating on the band with nothing
   under it. Inside the corner it is attached to the thing it labels,
   the clip goes back, and the rule can run edge to edge again.

   The corners are the tab's whole shape: the outer one follows the
   card's own radius and the inner one is rounded, the two along the
   card's edges square. */
.pr-card--premium { overflow: hidden; }

.pr-card__flag {
  position: absolute;
  inset-block-start: 0;
  inset-inline-end: 0;
  z-index: 1;
  padding: 0.3rem 0.7rem;
  border-start-start-radius: 0;
  border-start-end-radius: calc(var(--radius-premium) - 1px);
  border-end-end-radius: 0;
  border-end-start-radius: var(--radius-md);
}

.pr-card__name {
  margin: var(--space-2) 0 0;
  font-size: var(--fs-h4);
  color: var(--color-text);
}

.pr-card__pitch {
  margin: 0;
  min-height: 3.4em;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--color-text-secondary);
}

.pr-card__price {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
}

.pr-card__cur {
  font-size: var(--fs-xl);
  font-weight: var(--fw-semibold);
  color: var(--color-text-secondary);
}
.pr-card__amount {
  font-size: var(--fs-h2);
  font-weight: var(--fw-bold);
  line-height: 1;
  color: var(--color-text);
}
.pr-card__per {
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* The monthly price, struck beside the yearly month it is compared with. */
.pr-card__was {
  align-self: center;
  font-size: var(--fs-lg);
  color: var(--color-text-muted);
  text-decoration-thickness: 1.5px;
}

.pr-card__save {
  margin: var(--space-2) 0 0;
  min-height: 2.6em;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--color-text-muted);
}

.pr-card__cta { margin-top: var(--space-5); }

.pr-card__list {
  margin-block: var(--space-5) 0;
  gap: var(--space-2);
}
.pr-card__list .checks__item { font-size: var(--fs-xs); }

/* ---------------------------------------------------------
   «جميع ميزات الباقة»

   The button is hidden until pricing.js marks the grid as folding, so
   without JavaScript the card simply lists everything. One press opens
   every card in the row: the cards stretch to one height, and a single
   long card beside three short ones would leave the row ragged.

   auto on the button's top margin sits every button on the card's
   foot, so the four read across as one line whatever the lists hold.
   --------------------------------------------------------- */
.pr-card__all { display: none; }

.pr-plans__grid.is-folding .pr-card__list { margin-block-end: var(--space-4); }
.pr-plans__grid.is-folding:not(.is-open) .pr-card__more { display: none; }

.pr-plans__grid.is-folding .pr-card__all {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-block-start: auto;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-growth);
  border-radius: var(--radius-md);
  background: var(--color-growth-soft);
  color: var(--color-growth-text);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  line-height: var(--lh-snug);
  text-align: center;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease-out);
}
.pr-card__all:hover { background: color-mix(in srgb, var(--color-growth) 26%, transparent); }
.pr-card__all:focus-visible {
  outline: 2px solid var(--color-growth);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.pr-card__all::after {
  content: "";
  inline-size: 0.45rem;
  block-size: 0.45rem;
  border-inline-end: 1.5px solid currentColor;
  border-block-end: 1.5px solid currentColor;
  rotate: 45deg;
  translate: 0 -2px;
  transition: rotate var(--dur-fast) var(--ease-out);
}
.pr-card__all[aria-expanded="true"]::after { rotate: -135deg; translate: 0 2px; }

.pr-card__all-close,
.pr-card__all[aria-expanded="true"] .pr-card__all-open { display: none; }
.pr-card__all[aria-expanded="true"] .pr-card__all-close { display: inline; }

/* The switch. Both prices are in the markup; the grid's state
   decides which one is on the page, so the language toggle
   never has to rewrite a number. */
.pr-plans__grid[data-cycle="monthly"] [data-price="yearly"],
.pr-plans__grid[data-cycle="yearly"] [data-price="monthly"] {
  display: none;
}

.pr-plans__foot {
  margin: var(--space-10) 0 0;
  text-align: center;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------
   Centred switch

   The pricing page sets its switch under a left-aligned hero and
   inherits that alignment. A section that leads a grid centres its
   head, and the control above the grid has to agree with it.
   --------------------------------------------------------- */
.pr-cycle--center {
  display: flex;
  width: fit-content;
  margin-inline: auto;
}

/* ---------------------------------------------------------
   The stacked grid — one card forward, the rest tucked behind it

   Four cards of equal weight give the eye nowhere to land, and the
   tier most stores actually want is in the middle of the ramp. So the
   chosen card keeps its true size and its true place, and the others
   step back a fraction *and slide toward it*, so their outer edges
   close on it and the row reads as a hand of cards rather than as a
   row of four equal boxes.

   The slide is the half people actually see. Scale alone at this size
   is about eleven pixels on a 268px card, which reads as a rendering
   accident before it reads as depth; it is the closing gap along the
   featured card's edge that says which one is in front.

   --tuck is set per card in the markup: how many places that card sits
   from the featured one, signed by which side it is on. It is a signed
   distance and not a flat direction because the lead is not in the
   middle of four cards — see the note in the markup. The sign is read
   off source order, which is screen order only in LTR, so --tuck-step
   carries the flip and one property turns the whole row around.

   transform, not scale-the-font: the reveal uses the separate
   `translate` property, so the two compose instead of fighting. And
   the origin is the top edge, so the cards stay flush along their
   heads while their feet lift — a centre origin floats the smaller
   ones in the middle of the row with a gap above each.

   Hovering or tabbing into any card brings it out of the shelf at
   full size, so the ones standing back are still the first-class
   thing they describe and not a frame around the featured one.

   The gesture is a lift and not just an untuck, and that is the whole
   reason it is written this way. The featured card already rests at
   full size in its own place with the deep shadow on it — which is
   exactly what the old hover rule set — so hovering the one card the
   row is built around did nothing at all. Every neighbour answered
   the pointer and the tier being sold sat there dead. Six pixels up
   is a change the featured card can also make.
   --------------------------------------------------------- */
.pr-plans__grid--stacked {
  /* 18px against a 20px gap: with the 4px each side that the scale
     already takes off, the inner edge travels 14px and leaves a
     six-pixel seam. Thirty, the reference's figure, was measured
     against a narrower gutter and buries the seam entirely. */
  --tuck-step: 18px;
}
[dir="ltr"] .pr-plans__grid--stacked { --tuck-step: -18px; }

.pr-plans__grid--stacked > .pr-card {
  transform: translateX(calc(var(--tuck, 0) * var(--tuck-step))) scale(0.96);
  transform-origin: top center;
  z-index: 1;
  transition:
    transform var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.pr-plans__grid--stacked > .pr-card--featured {
  transform: none;
  z-index: 2;
  box-shadow: var(--shadow-lg);
}
.pr-plans__grid--stacked > .pr-card:hover,
.pr-plans__grid--stacked > .pr-card:focus-within {
  transform: translateY(-6px);
  z-index: 3;
  box-shadow: var(--shadow-lg);
}

@media (prefers-reduced-motion: reduce) {
  .pr-plans__grid--stacked > .pr-card { transform: none; transition: none; }
  .pr-plans__grid--stacked > .pr-card:hover,
  .pr-plans__grid--stacked > .pr-card:focus-within { transform: none; }
}

/* ---------------------------------------------------------
   Responsive
   --------------------------------------------------------- */
@media (max-width: 1080px) {
  .pr-plans__grid { grid-template-columns: repeat(2, 1fr); }
  .pr-card__pitch { min-height: 0; }
  .pr-card__save { min-height: 0; }

  /* Four stacked cards fold into two rows here, and a tuck measured along
     one row of four shoves the second row sideways into the gutter. The
     trio keeps its single row below, so it keeps its lean. */
  .pr-plans__grid--stacked:not(.pr-plans__grid--trio) > .pr-card { transform: none; }

  /* Three cards in two columns is one orphan, and the trio has the
     whole width to itself once its row has stopped being two. */
  .pr-plans__grid--trio { grid-template-columns: repeat(3, 1fr); }
}

/* The trio gives up earlier than the four-up grid does: three columns
   inside a 760px window is a 224px card, which is narrower than the
   price line wants.

   Two things have to go with the column, and both were missed the first
   time. A card left to fill the column came out 757px wide — one card
   the width of the page, which is not a card. And the tuck outlived the
   row it was tucking into: between here and the 720px rule below, three
   stacked cards were still scaled to 96% and shifted 18px off the
   gutter, with nothing beside them to explain why. */
@media (max-width: 860px) {
  .pr-plans__grid--trio {
    grid-template-columns: 1fr;
    justify-items: center;
  }
  .pr-plans__grid--trio > .pr-card {
    inline-size: 100%;
    max-inline-size: 24rem;
    transform: none;
  }
}

@media (max-width: 720px) {
  .pr-plans__grid { grid-template-columns: 1fr; }
  .pr-card__flag { inset-inline-start: var(--space-5); }

  /* Stacked one to a column there is no row to tuck into, and a card
     that is 96% wide and shifted 18px off the gutter just reads as a
     rendering mistake. */
  .pr-plans__grid--stacked > .pr-card { transform: none; }
}

@media (max-width: 520px) {
  .pr-cycle { width: 100%; }
  .pr-cycle__btn {
    flex: 1;
    justify-content: center;
    padding-inline: var(--space-3);
  }
}

/* components/link-arrow.css */
/* =========================================================
   Component — Arrow link

   A link that carries its own direction. The chevron is two borders
   rotated into place, so RTL and LTR need no separate glyph — only a
   different angle.
   ========================================================= */
.link-arrow {
  position: relative;
  display: inline-flex;
  align-items: center;
  padding-inline-end: 1.15em;
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--color-primary-text);
}
.link-arrow::after {
  content: "";
  position: absolute;
  inset-inline-end: 0.2em;
  top: 50%;
  width: 0.42em;
  height: 0.42em;
  border-right: 1.6px solid currentColor;
  border-bottom: 1.6px solid currentColor;
  translate: 0 -50%;
  rotate: -45deg;
  transition: translate var(--dur-fast) var(--ease-out);
}
:root[dir="rtl"] .link-arrow::after { rotate: 135deg; }
.link-arrow:hover::after { translate: 0.22em -50%; }
:root[dir="rtl"] .link-arrow:hover::after { translate: -0.22em -50%; }

/* An inline link cannot take a 44px box without tearing a hole in
   the paragraph it sits in, so it takes the height in padding and
   gives the line back the same amount in a negative margin. The
   text stays where it was; the target is twice the size. */
@media (pointer: coarse) {
  .link-arrow {
    padding-block: var(--space-3);
    margin-block: calc(var(--space-3) * -1);
  }
}

/* components/chip.css */
/* =========================================================
   Component — Chip

   A small standalone label used inside panels and mocks.
   ========================================================= */
.chip {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
}

/* Small — for chips inside a mock, where the standard size would
   outweigh the text it sits under. */
.chip--sm {
  padding: var(--space-1) var(--space-3);
  font-size: var(--fs-xs);
  font-weight: var(--fw-regular);
  background: var(--color-surface-sunken);
}

/* Live — a chip that says the figures beside it are read from the database
   rather than drawn. The dot is the whole signal; it does not pulse, because
   a blinking light on a marketing page is a notification nobody asked for. */
.chip--live {
  gap: var(--space-2);
  border-color: var(--color-growth);
  color: var(--color-growth-text);
}
.chip--live::before {
  content: "";
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--color-growth);
}

/* Not shipped yet. Muted rather than tinted: a chip that says «soon»
   should read as a note, not as a feature already earned. */
.chip--soon {
  border-style: dashed;
  color: var(--color-text-muted);
}

/* components/strip.css */
/* =========================================================
   Component — Fact strip

   A row of short facts on the band ground. Four on a wide screen,
   two on a tablet, one on a phone. Used by the home page, /ai-agent
   and /video-commerce, which is why everything below is written
   once here rather than three times in the page files.

   It used to be the sunken surface between two hairlines, which on
   the home page put a hard-edged grey slab directly under the hero.
   The band is the same idea at a quarter of the strength, and the
   rules above and below it were only there to stop the old ground
   from vibrating against the canvas.

   The facts are tiles now rather than four lines sharing one ground.
   Set as bare text they made a paragraph: the eye crossed all four
   in one sweep and kept none of them. A tile per fact makes the row
   count itself, and each promise stops being a phrase in a sentence
   and becomes a thing with an edge.

   The tint is olive, a hue neither brand ramp carries. It is written
   here rather than promoted to tokens.css because one component uses
   it, and it is written as a translucent fill over the band rather
   than as an opaque colour — the tile is the band seen through glass,
   not a slab laid on top of it. The wash on .strip is what the glass
   has to look at: with nothing uneven behind it, a translucent
   surface and a solid one are the same picture.
   ========================================================= */
.strip {
  position: relative;
  isolation: isolate;
  background: var(--color-canvas-band);

  /* The olive, as channels, and the three strengths it is used at:
     the tile fill, the tile edge, and the wash the tiles sit over.
     One set for both themes, which is not the usual answer — a tint
     this weak normally has to be raised on paper to stay visible —
     but the olive is dark enough that 0.30 darkens white and lightens
     #171c21 by about the same amount, and the two tiles came out
     matched when they were measured against each other. */
  --strip-olive: 124 141 78;
  --strip-tile: rgb(var(--strip-olive) / 0.30);
  --strip-tile-line: rgb(var(--strip-olive) / 0.55);
  --strip-wash: rgb(var(--strip-olive) / 0.14);
}
/* Two pools rather than one flat overlay, and set under the tiles
   rather than over them, so each translucent fill is sitting on a
   gradient instead of on a constant. */
.strip::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(60% 120% at 18% 0%, var(--strip-wash), transparent 70%),
    radial-gradient(55% 120% at 82% 100%, var(--strip-wash), transparent 70%);
  pointer-events: none;
}

.strip__inner {
  display: grid;
  gap: var(--space-4);
  padding-block: var(--space-8);
  grid-template-columns: 1fr;
}
/* These four lines are the page's first concrete promises, and at
   13px in the secondary ink they read as a caption — the eye took
   them for legal small print and skipped them. Body size and the
   primary ink, which is what a promise is set in. */
.strip__item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--strip-tile-line);
  border-radius: var(--radius-lg);
  background: var(--strip-tile);
  box-shadow: var(--shadow-soft);
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  color: var(--color-text);
}
/* The lit top edge: one hairline of white fading out at both ends.
   It is most of the difference between a tile that reads as glass
   and one that reads as a coloured rectangle. */
.strip__item::before {
  content: "";
  position: absolute;
  inset-block-start: 0;
  inset-inline: var(--space-5);
  block-size: 1px;
  background: linear-gradient(90deg, transparent, rgb(255 255 255 / 0.16), transparent);
  pointer-events: none;
}
/* Guarded, the way the auth card guards its own glass: a browser
   without backdrop-filter keeps the translucent fill and the lit
   edge, which is the tile minus one refinement rather than a
   broken tile. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .strip__item {
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    backdrop-filter: blur(10px) saturate(140%);
  }
}
/* The diamond grows with the text it marks and drops the veil — at
   0.85 over a tinted band the brass was doing neither job. */
.strip__mark {
  width: 9px;
  height: 9px;
  flex: none;
  rotate: 45deg;
  background: var(--color-gold);
}

/* Paper takes the same three values and drops one thing: the lit
   edge. A hairline of white at 16% is invisible on a pale sage tile,
   so on this ground the tile earns its lift from the border and the
   shadow alone. */
:root[data-theme="light"] .strip__item::before { display: none; }

@media (min-width: 720px) {
  .strip__inner { grid-template-columns: repeat(2, 1fr); }
}
/* Four equal columns forced the two longest facts onto a second line
   while the two shortest sat in half-empty tiles. The columns are
   content-sized instead: each tile is as wide as its own sentence, and
   the row divides the width the way the copy actually falls.

   No `white-space: nowrap`. Auto columns already hold one line each
   wherever the row has the width for it, so the only question is
   where that width starts. Measured, in English, which is the longer
   of the two languages here: the four tiles want 1104px between them,
   so the row opens at 1160 rather than at the 1080 it used to. The
   forty pixels between the two numbers are the scrollbar and a margin
   for a font that has not finished loading.

   Under 1160 the strip is two columns, where a tile is half the page
   wide and no sentence has to break. Nothing wraps at any width; it
   only ever changes how many tiles stand in a row. */
@media (min-width: 1160px) {
  .strip__inner {
    grid-template-columns: repeat(4, auto);
    gap: var(--space-2);
  }
  .strip__item { padding-inline: var(--space-4); }
}

/* components/showcase.css */
/* =========================================================
   Component — Showcase row

   Copy on one side, a drawn stage on the other. The flipped
   variant swaps them, so a page of several rows makes the eye
   cross it instead of running down one gutter.
   ========================================================= */
.showcase__inner {
  display: grid;
  gap: var(--space-12);
  align-items: center;
}
.showcase__copy .section-head__title { margin-bottom: var(--space-4); }

.showcase__stage {
  position: relative;
  padding-block-end: var(--space-16);
}

@media (min-width: 980px) {
  .showcase__inner { grid-template-columns: 1fr 1.1fr; }
  .showcase--flip .showcase__copy { order: 2; }
  .showcase--flip .showcase__stage { order: 1; }
}

/* components/closer.css */
/* =========================================================
   Component — Closing call to action

   The last band on a marketing page: a gold hairline, one heading,
   one line, two buttons, one reassurance.
   ========================================================= */
.closer { padding-block: var(--space-8) var(--space-24); }
.closer__inner {
  max-width: 44rem;
  text-align: center;
}
.closer__rule { margin-bottom: var(--space-16); }
.closer__title {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-h2);
  text-wrap: balance;
}
.closer__lead {
  margin: 0 auto var(--space-8);
  max-width: 42ch;
  color: var(--color-text-secondary);
}
.closer__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.closer__note {
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}

/* components/faq.css */
/* =========================================================
   Component — FAQ block

   The accordion itself is components/accordion.css; this is only
   the column width it sits in and the line under it.
   ========================================================= */
.faq__inner { max-width: 52rem; }
.faq__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-8) 0 0;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
}

/* The call to action under the last answer. It carries its own top
   gap, so the line that follows it sits closer than it would after
   the accordion alone. */
.faq__cta {
  display: flex;
  margin: var(--space-8) 0 0;
}
.faq__cta + .faq__foot { margin-top: var(--space-5); }

/* components/table.css */
/* =========================================================
   Component — Table
   Rows are data, not cards. Horizontal rules only, and the
   figures column stays Latin + tabular so numbers align.
   ========================================================= */
.table-wrap {
  width: 100%;
  overflow-x: auto;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
  text-align: start;
}

.table__head th {
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-line);
  background: var(--color-surface-sunken);
  color: var(--color-text-secondary);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-align: start;
  white-space: nowrap;
}

.table__row { transition: background var(--dur-fast) var(--ease-out); }
.table__row:hover { background: var(--color-primary-soft); }
.table__row + .table__row td { border-top: 1px solid var(--color-line); }

.table td {
  padding: var(--space-4) var(--space-5);
  color: var(--color-text-secondary);
  vertical-align: middle;
}

.table__cell--key {
  color: var(--color-text);
  font-weight: var(--fw-medium);
}

.table__cell--figure {
  font-family: var(--font-figures);
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
  text-align: end;
  white-space: nowrap;
}

/* components/platform-toggle.css */
/* =========================================================
   Component — Platform toggles (language + theme)

   Two small square controls that travel together wherever the
   shell goes: header, drawer, footer, auth card. The theme button
   shows the mode it will switch you to, not the one you are in —
   a control should be labelled by its action.
   ========================================================= */
.platform-toggles {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.platform-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition:
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    background var(--dur-fast) var(--ease-out);
}
.platform-toggle:hover {
  border-color: var(--color-line-strong);
  background: var(--color-surface-raised);
  color: var(--color-text);
}

.platform-toggle--lang {
  width: auto;
  padding-inline: var(--space-3);
  font-family: var(--font-latin);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: var(--tracking-wide);
}

.platform-toggle__icon { width: 17px; height: 17px; }

/* Only one of the two icons is ever drawn: sun while dark is on
   (press it and you get light), moon while light is on. */
:root .platform-toggle__icon--moon,
:root[data-theme="light"] .platform-toggle__icon--sun { display: none; }
:root .platform-toggle__icon--sun,
:root[data-theme="light"] .platform-toggle__icon--moon { display: block; }

/* Theme and language are the two controls a visitor reaches for on
   a phone with one hand. 34px was a miss waiting to happen. */
@media (pointer: coarse) {
  .platform-toggle {
    min-inline-size: 44px;
    min-block-size: 44px;
  }
}

/* components/announcement.css */
/* =========================================================
   Component — Announcement bar

   The usual version of this is a saturated gradient strip. Here it
   is the deepest surface in the system with a brass hairline under
   it: quieter than the page it sits above, which is what makes the
   one link in it readable as a link.
   ========================================================= */
.announce {
  position: relative;
  background: var(--color-canvas-deep);
  border-bottom: 1px solid var(--color-line-gold);
  font-size: var(--fs-xs);
}
.announce[hidden] { display: none; }

.announce__inner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 40px;
  padding-block: var(--space-2);
}

.announce__pip {
  width: 6px;
  height: 6px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--color-growth);
  box-shadow: 0 0 0 4px var(--color-growth-soft);
}

.announce__text {
  margin: 0;
  color: var(--color-text-secondary);
}

.announce__link {
  position: relative;
  flex: none;
  color: var(--color-gold-text);
  font-weight: var(--fw-bold);
  padding-inline-end: 1.1em;
}
.announce__link:hover { color: var(--color-gold); }

/* The arrow is drawn from two borders, not typed, so switching
   language flips a rotation instead of swapping a glyph. It always
   points along the reading direction: right in LTR, left in RTL. */
.announce__link::after {
  content: "";
  position: absolute;
  inset-inline-end: 0.15em;
  top: 50%;
  width: 0.42em;
  height: 0.42em;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  translate: 0 -50%;
  rotate: -45deg;
  transition: translate var(--dur-fast) var(--ease-out);
}
:root[dir="rtl"] .announce__link::after { rotate: 135deg; }
.announce__link:hover::after { translate: 0.2em -50%; }
:root[dir="rtl"] .announce__link:hover::after { translate: -0.2em -50%; }

.announce__close {
  margin-inline-start: auto;
  display: inline-flex;
  padding: var(--space-1);
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--dur-fast) var(--ease-out);
}
.announce__close:hover { color: var(--color-text); }
.announce__close svg { width: 14px; height: 14px; }

@media (max-width: 600px) {
  .announce__text { line-height: var(--lh-snug); }
  .announce__link { display: none; }
}

/* The dismiss target, not the glyph, is what a thumb has to hit. */
.announce__close {
  min-inline-size: 44px;
  min-block-size: 44px;
}

/* components/header.css */
/* =========================================================
   Component — Site header

   Sticky, translucent, and hairline-thin. It gains a shadow only
   once the page has actually scrolled under it (shell.js adds
   .is-stuck), so at rest it reads as part of the page instead of
   a bar bolted on top of it.
   ========================================================= */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--color-canvas) 88%, transparent);
  backdrop-filter: blur(14px) saturate(1.1);
  -webkit-backdrop-filter: blur(14px) saturate(1.1);
  border-bottom: 1px solid transparent;
  transition:
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.site-header.is-stuck {
  border-bottom-color: var(--color-line);
  box-shadow: var(--shadow-sm);
}

.site-header__inner {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  min-height: var(--header-height);
}

/* ---------------------------------------------------------
   Nav
   --------------------------------------------------------- */
.site-nav {
  display: none;
  align-items: center;
  gap: var(--space-6);
  margin-inline-end: auto;
}

.site-nav__item { position: relative; }

.site-nav__link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  border: 0;
  background: none;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--color-text-secondary);
  cursor: pointer;
  white-space: nowrap;
  transition: color var(--dur-fast) var(--ease-out);
}
.site-nav__link:hover,
.site-nav__link[aria-expanded="true"] { color: var(--color-text); }

.site-nav__chevron {
  width: 10px;
  height: 7px;
  flex: none;
  transition: rotate var(--dur-fast) var(--ease-out);
}
.site-nav__link[aria-expanded="true"] .site-nav__chevron { rotate: 180deg; }

/* ---------------------------------------------------------
   Solutions menu
   --------------------------------------------------------- */
.site-menu {
  position: absolute;
  inset-block-start: calc(100% + var(--space-3));
  /* Anchored to the trigger's leading edge, growing inward. Centering
     it on the trigger pushed the panel past the viewport edge in RTL,
     where the nav sits near the right margin. */
  inset-inline-start: 0;
  z-index: 20;
  width: min(720px, calc(100dvw - var(--space-8)));
  padding: var(--space-3);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
  animation: site-menu-in var(--dur-base) var(--ease-out);
}
.site-menu[hidden] { display: none; }

@keyframes site-menu-in {
  from { opacity: 0; translate: 0 -6px; }
  to   { opacity: 1; translate: 0 0; }
}

.site-menu__grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-1);
}

.site-menu__item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  transition: background var(--dur-fast) var(--ease-out);
}
.site-menu__item:hover { background: var(--color-surface-raised); }

.site-menu__title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--color-text);
}
.site-menu__note {
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--color-text-muted);
}

/* ---------------------------------------------------------
   Actions
   --------------------------------------------------------- */
.site-header__actions {
  display: none;
  align-items: center;
  gap: var(--space-3);
}

/* ---------------------------------------------------------
   Burger — two bars, because three is a drawing of a menu and
   two is enough to read as one.
   --------------------------------------------------------- */
.site-header__burger {
  display: inline-flex;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  margin-inline-start: auto;
  width: 40px;
  height: 40px;
  padding: 0 9px;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-sm);
  background: none;
  cursor: pointer;
}
.site-header__burger span {
  display: block;
  height: 1.6px;
  border-radius: var(--radius-pill);
  background: var(--color-text);
}
.site-header__burger span:last-child { width: 62%; }

/* ---------------------------------------------------------
   Drawer
   --------------------------------------------------------- */
.site-drawer__scrim {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: var(--color-scrim);
  backdrop-filter: blur(2px);
  animation: scrim-in var(--dur-base) var(--ease-out);
}
.site-drawer__scrim[hidden] { display: none; }

@keyframes scrim-in { from { opacity: 0; } to { opacity: 1; } }

.site-drawer {
  position: fixed;
  inset-block: 0;
  inset-inline-start: 0;
  z-index: 80;
  display: flex;
  flex-direction: column;
  width: min(340px, 88vw);
  padding: var(--space-5);
  background: var(--color-surface);
  border-inline-end: 1px solid var(--color-line);
  overflow-y: auto;
  animation: drawer-in var(--dur-slow) var(--ease-out);
}
.site-drawer[hidden] { display: none; }

@keyframes drawer-in {
  from { transform: translateX(var(--drawer-from, 100%)); }
  to   { transform: translateX(0); }
}
:root[dir="ltr"] .site-drawer { --drawer-from: -100%; }

.site-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: var(--space-5);
}
.site-drawer__close {
  display: inline-flex;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text-secondary);
  cursor: pointer;
}
.site-drawer__close:hover { color: var(--color-text); }
.site-drawer__close svg { width: 20px; height: 20px; }

.site-drawer__body {
  display: flex;
  flex-direction: column;
}
.site-drawer__body .hairline { margin-block: var(--space-3); }

.site-drawer__link {
  padding: var(--space-3) 0;
  font-size: var(--fs-base);
  font-weight: var(--fw-medium);
  color: var(--color-text-secondary);
}
.site-drawer__link:hover { color: var(--color-text); }

.site-drawer__foot {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-6);
}

/* ---------------------------------------------------------
   Breakpoint — the drawer exists only below the point where
   the full nav stops fitting.
   --------------------------------------------------------- */
@media (min-width: 1024px) {
  .site-nav,
  .site-header__actions { display: flex; }
  .site-header__burger { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .site-menu,
  .site-drawer,
  .site-drawer__scrim { animation: none; }
}

/* ---------------------------------------------------------
   Mobile CTA + touch targets

   Under 1024px the header's action block is hidden, so the one
   ask on the page scrolled away with the hero. This keeps a
   single primary button in the sticky bar and gives the burger
   the 48px a thumb needs.
   --------------------------------------------------------- */
.site-header__cta {
  display: flex;
  align-items: center;
  margin-inline-start: auto;
}
.site-header__cta .btn {
  min-block-size: 44px;
  --btn-pad-inline: 1.1rem;
  font-size: var(--fs-sm);
}
.site-header__burger {
  margin-inline-start: var(--space-3);
  width: 48px;
  height: 48px;
  padding: 0 12px;
}
@media (min-width: 1024px) {
  .site-header__cta { display: none; }
}
.site-drawer__link {
  display: flex;
  align-items: center;
  min-block-size: 48px;
}
.site-drawer__foot .btn { min-block-size: 48px; }

/* components/footer.css */
/* =========================================================
   Component — Site footer

   The brand column is wider than the link columns and holds the
   only prose in the footer; the rest is a directory. Sitting on the
   deepest surface, it closes the page the way the announcement bar
   opened it.
   ========================================================= */
.site-footer {
  margin-top: var(--space-24);
  padding-block: var(--space-16) var(--space-8);
  background: var(--color-canvas-deep);
  border-top: 1px solid var(--color-line);
}

.site-footer__top {
  display: grid;
  gap: var(--space-10) var(--space-8);
  grid-template-columns: 1fr;
}

.site-footer__brand {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  max-width: 34ch;
}

.site-footer__line {
  margin: 0;
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  color: var(--color-text-secondary);
}

.site-footer__agency {
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-line-gold);
  font-size: var(--fs-xs);
  color: var(--color-gold-text);
}

.site-footer__col-title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-body-ar);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--color-text);
}
:root[data-lang="en"] .site-footer__col-title {
  font-family: var(--font-latin);
  text-transform: uppercase;
  letter-spacing: var(--tracking-eyebrow);
}

.site-footer__links {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.site-footer__links a {
  font-size: var(--fs-sm);
  color: var(--color-text-secondary);
}
.site-footer__links a:hover { color: var(--color-text); }

.site-footer__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.site-footer__copyright {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}

@media (min-width: 640px) {
  .site-footer__top { grid-template-columns: repeat(2, 1fr); }
  .site-footer__brand { grid-column: 1 / -1; }
}

@media (min-width: 900px) {
  .site-footer__top {
    grid-template-columns: 1.6fr repeat(4, 1fr);
    gap: var(--space-8);
  }
  .site-footer__brand { grid-column: auto; }
}

/* A footer link at 21px is a link a thumb misses. The rows keep
   their visual rhythm — the gap shrinks by what the padding
   adds — so nothing below the fold grows to buy this. */
@media (pointer: coarse) {
  .site-footer__links { gap: 0; }
  .site-footer__links a {
    display: flex;
    align-items: center;
    min-block-size: 44px;
  }
}

/* components/marquee.css */
/* =========================================================
   Component — Marquee

   Two rails travelling against each other. The rail holds one set
   of chips in the markup and is duplicated by shell.js, because a
   seamless loop needs the content twice and writing it twice by
   hand means editing it twice forever.

   It is the only ambient motion in the system, so it is slow: fast
   marquees read as a banner ad. It stops on hover and on
   prefers-reduced-motion.
   ========================================================= */
.marquee {
  --marquee-duration: 64s;

  position: relative;
  display: flex;
  overflow: hidden;
  padding-block: var(--space-2);
  /* Fade both ends into the canvas so the chips arrive and leave
     instead of being cut by a hard edge. The #000 below is a
     stencil, not a colour: a mask reads only the alpha channel, so
     the value is "opaque here" and never paints. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
}

.marquee__rail {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--space-3);
  padding-inline-end: var(--space-3);
  will-change: transform;
  animation: marquee-run var(--marquee-duration) linear infinite;
}

/*
   The travel has to flip with direction, and not for a cosmetic
   reason.

   A flex row lays its content out from the inline start, so in LTR
   the rail begins at x=0 and runs right: the window at [0, w] sits
   over the head of the rail, and walking it to -50% drags the rest
   of the content through that window. In RTL the same rail begins
   at x = w - railWidth — the window is over its *tail* — so the
   identical -50% walks the whole thing off-canvas to the left and
   the band renders empty.

   So RTL travels the other way, to +50%. Both halves of the rail
   are identical, so either end point lands on a frame matching the
   start and the loop stays seamless. Same shape as athar-crawl /
   athar-crawl-rtl in busy.css.
*/
@keyframes marquee-run {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

@keyframes marquee-run-rtl {
  from { transform: translateX(0); }
  to   { transform: translateX(50%); }
}

:root[dir="rtl"] .marquee__rail { animation-name: marquee-run-rtl; }

.marquee--reverse .marquee__rail { animation-direction: reverse; }

.marquee:hover .marquee__rail,
.marquee:focus-within .marquee__rail { animation-play-state: paused; }

.marquee__chip {
  flex: none;
  padding: var(--space-2) var(--space-5);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-pill);
  background: var(--color-surface);
  color: var(--color-text-secondary);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

/* Every third chip gets the brass hairline, so the rail has a
   rhythm instead of being one long grey ribbon. */
.marquee__chip:nth-child(3n) {
  border-color: var(--color-line-gold);
  color: var(--color-gold-text);
}

@media (prefers-reduced-motion: reduce) {
  .marquee__rail { animation: none; }
  .marquee {
    overflow-x: auto;
    scrollbar-width: none;
  }
  .marquee::-webkit-scrollbar { display: none; }
}


/* components/accordion.css */
/* =========================================================
   Component — Accordion

   Built on native <details>/<summary>: it opens without
   JavaScript, it is announced as a disclosure by a screen reader,
   and the browser's own find-in-page can open it. shell.js only
   adds "one open at a time" on top of that.
   ========================================================= */
.accordion {
  display: flex;
  flex-direction: column;
}

.accordion__item {
  border-bottom: 1px solid var(--color-line);
}
.accordion__item:first-child { border-top: 1px solid var(--color-line); }

.accordion__head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding-block: var(--space-5);
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--color-text);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-fast) var(--ease-out);
}
/* Kill both the WebKit triangle and the Firefox one */
.accordion__head::-webkit-details-marker { display: none; }
.accordion__head::marker { content: ""; }

.accordion__head:hover { color: var(--color-gold-text); }
.accordion__head:focus-visible { outline-offset: -2px; }

/* A plus drawn from two rules, one of which retracts on open, so
   the sign becomes a minus without swapping a glyph or a file. */
.accordion__sign {
  position: relative;
  flex: none;
  width: 16px;
  height: 16px;
  margin-inline-start: auto;
  color: var(--color-gold-text);
}
.accordion__sign::before,
.accordion__sign::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 0;
  width: 100%;
  height: 1.6px;
  border-radius: var(--radius-pill);
  background: currentColor;
  translate: 0 -50%;
  transition: rotate var(--dur-base) var(--ease-out);
}
.accordion__sign::after { rotate: 90deg; }
.accordion__item[open] .accordion__sign::after { rotate: 0deg; }

.accordion__body {
  max-width: 62ch;
  margin: 0;
  padding-bottom: var(--space-5);
  color: var(--color-text-secondary);
  font-size: var(--fs-base);
  line-height: var(--lh-relaxed);
}

@media (prefers-reduced-motion: reduce) {
  .accordion__sign::before,
  .accordion__sign::after { transition: none; }
}

/* components/tabs.css */
/* =========================================================
   Component — Tabs

   Written in an early pass and never linked, so it still read
   five variables that no longer exist (--color-border,
   --font-display, --transition-base, --shadow-xs,
   --gradient-brand). م3 is the first page to use it, so the
   whole file was moved onto the current token names rather than
   shipped broken.

   Two shapes: a pill group on a sunken ground (default), and an
   underlined row for content pages. The --scroll modifier lets a
   long group slide sideways on a phone instead of wrapping into
   a block that pushes the panel down the screen.
   ========================================================= */
.tabs {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-pill);
  background: var(--color-surface-sunken);
}

.tabs__tab {
  appearance: none;
  border: 0;
  padding: var(--space-2) var(--space-5);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  white-space: nowrap;
  transition:
    background var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out);
}

.tabs__tab:hover { color: var(--color-text); }
.tabs__tab:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}

.tabs__tab[aria-selected="true"] {
  background: var(--color-surface-raised);
  color: var(--color-text);
  box-shadow: var(--shadow-sm);
}

/* A group that may outgrow a phone: it scrolls in place, and the
   scrollbar is left to the platform rather than hidden — a strip
   that moves with no affordance reads as broken. */
.tabs--scroll {
  display: flex;
  max-width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
}

@media (prefers-reduced-motion: reduce) {
  .tabs__tab { transition: none; }
}

/* components/console.css */
/* =========================================================
   Component — Agent console

   Promoted out of pages/home.css in م3. The home page carried a
   `.chat` mock of the agent; the AI-agent page needs the same
   object five times over (hero plus four worked examples), which
   is the point at which the constitution (§4.2) says to lift it
   into a component rather than keep two implementations that
   drift apart.

   Structure: head (avatar · name and role · balance) → thread of
   bubbles → task card (steps · cost · approve). The `--wide`
   modifier is the examples variant, which sits beside a result
   panel instead of standing alone.

   The step marks are drawn from borders and pseudo-elements, so
   nothing here needs an icon file, and the states — done, running,
   pending — are pure class swaps that ai-agent.js can replay.
   ========================================================= */
.console {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  box-shadow: var(--shadow-lg);
}

/* ---- Head ---- */
.console__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-4);
  border-bottom: 1px solid var(--color-line);
}
.console__avatar {
  width: 30px;
  height: 30px;
  flex: none;
  border-radius: var(--radius-pill);
  background: var(--gradient-trace);
}
.console__who {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}
.console__name {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--color-text);
}
.console__role {
  font-size: var(--fs-xs);
  color: var(--color-text-muted);
}
.console__credit {
  margin-inline-start: auto;
  flex: none;
}

/* ---- Thread ---- */
.console__thread {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.console__bubble {
  max-width: 88%;
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}
/* The visitor's own line sits on the deep brand tone, not on the
   action colour. It is the largest tinted mass in the console, and
   wearing the same colour as the buttons made it shout louder than
   the headline beside it. */
.console__bubble--user {
  align-self: flex-end;
  background: var(--color-primary-deep);
  color: var(--color-text-on-deep);
  border-end-end-radius: var(--radius-xs);
}
.console__bubble--agent {
  align-self: flex-start;
  background: var(--color-surface-raised);
  color: var(--color-text-secondary);
  border-end-start-radius: var(--radius-xs);
}

/* ---- Task card ---- */
.console__task {
  margin-top: var(--space-2);
  padding: var(--space-4);
  border: 1px solid var(--color-line-gold);
  border-radius: var(--radius-md);
  background: var(--color-surface-sunken);
}
.console__task-head {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--color-gold-text);
}

.console__steps {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}
.console__step {
  position: relative;
  padding-inline-start: var(--space-6);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--color-text-secondary);
  transition: color var(--dur-base) var(--ease-out);
}

/* Pending: a hollow ring. Running: a ring that turns.
   Done: a filled disc with a tick cut into it. All drawn. */
.console__step-mark {
  position: absolute;
  inset-inline-start: 0;
  top: 0.45em;
  width: 13px;
  height: 13px;
  border-radius: var(--radius-pill);
  border: 2px solid var(--color-line-strong);
  transition:
    background var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    box-shadow var(--dur-base) var(--ease-out);
}
.console__step-mark::after {
  content: "";
  position: absolute;
  inset-inline-start: 2px;
  top: 3px;
  width: 5px;
  height: 2.5px;
  border-inline-start: 1.5px solid var(--color-canvas-deep);
  border-bottom: 1.5px solid var(--color-canvas-deep);
  rotate: -45deg;
  opacity: 0;
  transition: opacity var(--dur-fast) var(--ease-out);
}

.console__step.is-done { color: var(--color-text-secondary); }
.console__step.is-done .console__step-mark {
  border-color: transparent;
  background: var(--color-success);
  box-shadow: 0 0 0 3px var(--color-success-soft);
}
.console__step.is-done .console__step-mark::after { opacity: 1; }

.console__step.is-running { color: var(--color-text); }
.console__step.is-running .console__step-mark {
  border-color: var(--color-growth-soft);
  border-top-color: var(--color-growth);
  animation: console-spin 900ms linear infinite;
}

.console__step.is-pending { color: var(--color-text-muted); }

@keyframes console-spin { to { rotate: 360deg; } }

/* ---- Foot ---- */
.console__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-line);
  font-size: var(--fs-xs);
}
.console__cost {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space-2);
}
.console__cost-figure {
  font-weight: var(--fw-bold);
  color: var(--color-gold-text);
}
.console__cost-label { color: var(--color-text-muted); }
.console__approve {
  margin-inline-start: auto;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}

/* ---- Wide variant: the worked-example console ---- */
.console--wide {
  padding: var(--space-6);
  box-shadow: var(--shadow-md);
}
.console--wide .console__bubble { max-width: 94%; }

@media (prefers-reduced-motion: reduce) {
  .console__step.is-running .console__step-mark { animation: none; }
  .console__step,
  .console__step-mark,
  .console__step-mark::after { transition: none; }
}
