@layer legacy, tokens, base, components, utilities;
/* ── v5 ────────────────────────────────────────────────────────────────────────
   The cascade order for the whole redesign, declared once, here, before any rule
   exists. A layer's position is fixed by THIS statement, not by where its rules
   are written, so later rounds can add to `tokens`/`base`/`components`/
   `utilities` in any order and still land above `legacy`.

   `legacy` is the lowest layer and holds the entire existing style.css (wrapped
   in `@layer legacy { … }` there, unchanged otherwise). Everything the redesign
   adds therefore wins on layer order alone — no priority overrides, no specificity
   games.

   Round 1 / Phase 1 adds NO tokens, base, component or utility rules. This file
   is the layer declaration plus the temporary v5 marker below, and nothing else.
   Custom properties are deliberately absent: they do not participate in layers
   and need the higher-specificity `:root[data-ui="v5"]` selectors from Addendum
   A, which is Phase 2.

   Loaded after style.css and gated by <html data-ui="v5">, which only appears
   when the `ui` cookie (or ?ui=v5) is set. Without it this file changes nothing.
   ──────────────────────────────────────────────────────────────────────────── */

/* ── The v5 marker ──────────────────────────────────────────────────────────
   Deliberately in `legacy`, not in `components`: the layer contract is that
   `tokens`/`base`/`components`/`utilities` are scoped under `:root[data-ui="v5"]`
   and are written from Phase 2 onward on top of a token set that does not exist
   yet. A marker rule sitting in `components` with hardcoded values would be the
   first violation of the do-not list. In `legacy` it is a peer of the old
   stylesheet — same layer, later source order — so it costs the redesign
   nothing and disappears with the rest of the switch in the final round.

   Selector-scoped to html[data-ui="v5"], so it is inert without the cookie.
   z-index sits above every page-level control (#toast 100, back-to-top 50) and
   below the modal backdrop (200) and the menu panel (300), so it can never
   cover a dialog. pointer-events:none keeps it from swallowing a tap on the
   element underneath.

   Logical insets, not left/right: this app is RTL, so inset-inline-start is the
   RIGHT edge here — the corner opposite #warehouse-back-to-top, which is pinned
   physically left. Colours come from the existing tokens, so the marker follows
   the light/dark theme for free and adds no hex literal.

   Deleted in the final round. It exists so the switch cannot be forgotten. */
@layer legacy {
  html[data-ui="v5"] .v5-badge {
    position: fixed;
    inset-block-end: max(8px, env(safe-area-inset-bottom));
    inset-inline-start: max(8px, env(safe-area-inset-left));
    z-index: 150;
    pointer-events: none;
    opacity: .35;
    padding: 2px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface);
    color: var(--text);
    font-family: 'Vazirmatn', Tahoma, Arial, sans-serif;
    font-size: 11px;
    font-weight: 700;
    line-height: 1.5;
    letter-spacing: .04em;
  }
}

/* ── Round 1 / Phase 2 — the token layer ───────────────────────────────────────
   Three things live here and nothing else: the v5 theme palettes, the
   theme-independent scales, and the alias block that re-points every one of the
   32 legacy custom properties at a v5 token. No base, component or utility rule
   — those are Phase 3 and Round 2.

   SELECTORS (Addendum A1). Custom properties do NOT participate in @layer; they
   cascade by specificity and source order alone. Wrapping them in `tokens`
   changes nothing about who wins, so every selector below carries `:root` to
   reach 0,2,0 and out-specify — or at minimum tie-and-outrank-by-source-order —
   the legacy blocks in style.css:

     :root                        style.css:18   0,1,0
     :root[data-theme="dark"]     style.css:72   0,2,0
     :root:not([data-theme])      style.css:108  0,2,0   (in prefers-color-scheme)

   A bare [data-ui="v5"] is only 0,1,0 and would have lost to the dark block —
   light would have worked, dark would silently not have. Hence:

     scales + aliases   :root[data-ui="v5"]                            0,2,0
     light              :root[data-ui="v5"][data-theme="light"]        0,3,0
     dark               :root[data-ui="v5"][data-theme="dark"]         0,3,0
     OS dark, no cookie :root[data-ui="v5"]:not([data-theme])          0,3,0

   The 0,2,0 default block ties style.css's dark and OS-dark blocks on
   specificity and wins on source order, because v5.css is linked after
   style.css. That tie is intentional and is what makes the un-themed default
   land on v5 light rather than legacy dark.

   THEME STRUCTURE (Addendum A2). v5 is not dark-first. It mirrors the existing
   structure exactly — default light, explicit light duplicated at higher
   specificity for clarity, explicit dark, and OS dark only when no theme cookie
   has been set — so the cookie still wins and the OS still decides in its
   absence. Behaviour is unchanged; only the palette differs.

   Everything is gated on data-ui="v5", so without the `ui` cookie not one
   declaration here applies and the app resolves exactly the legacy values.
   ──────────────────────────────────────────────────────────────────────────── */
@layer tokens {

/* ── Theme: light (default) ─────────────────────────────────────────────────
   No data-theme attribute → light, matching the app's current default. The
   --v5-surface-solid / -positive-hover / -negative-hover trio comes from
   Addendum A3: the legacy :root has opaque card backgrounds and hover states
   with no v5 equivalent, and --surface/--raised must alias to something OPAQUE
   or nested cards would stack transparency and go muddy. */
:root[data-ui="v5"] {
  --v5-bg:#F2EDE3;
  --v5-bloom-a:rgba(14,107,94,.16);
  --v5-bloom-b:rgba(168,121,31,.17);
  --v5-bloom-c:rgba(14,107,94,.08);

  --v5-surface:rgba(255,255,255,.72);
  --v5-surface-2:rgba(255,255,255,.92);
  --v5-surface-solid:#FFFFFF;
  --v5-line:rgba(80,66,40,.11);
  --v5-line-2:rgba(80,66,40,.2);
  --v5-sunken:rgba(80,66,40,.07);

  --v5-text:#1B1A17;
  --v5-text-muted:#756E62;
  --v5-text-faint:#A29988;

  --v5-accent:#0E6B5E;
  --v5-accent-2:#0A5449;
  --v5-accent-hi:#17998A;
  --v5-accent-tint:rgba(14,107,94,.11);
  --v5-accent-line:rgba(14,107,94,.26);
  --v5-on-accent:#FFFFFF;
  --v5-grad-accent:linear-gradient(158deg,#15866F 0%,#0E6B5E 55%,#0A4E45 100%);
  --v5-glow:0 6px 18px rgba(14,107,94,.22);
  --v5-edge-glow:0 0 0 rgba(0,0,0,0);

  --v5-gold:#A8791F;
  --v5-gold-2:#D4A63C;
  --v5-gold-tint:rgba(168,121,31,.13);
  --v5-gold-line:rgba(168,121,31,.28);

  --v5-positive:#2C7A4B;
  --v5-positive-tint:rgba(44,122,75,.12);
  --v5-positive-line:rgba(44,122,75,.26);
  --v5-positive-hover:#245F3B;
  --v5-negative:#BE3A31;
  --v5-negative-tint:rgba(190,58,49,.11);
  --v5-negative-line:rgba(190,58,49,.25);
  --v5-negative-hover:#9E2E27;

  --v5-ov-bg:rgba(255,255,255,.82);
  --v5-ov-line:rgba(255,255,255,.95);
  --v5-ov-gloss:inset 0 1px 0 rgba(255,255,255,1);
  --v5-ov-blur:blur(26px) saturate(150%);
  --v5-scrim:rgba(40,30,16,.42);

  --v5-sh:0 6px 22px rgba(60,48,26,.10);
  --v5-sh-float:0 14px 40px rgba(60,48,26,.18);
}

/* ── Theme: light (explicit) ────────────────────────────────────────────────
   Byte-identical to the block above, restated at 0,3,0. Deliberate duplication
   per Addendum A2: an explicit light cookie must out-specify the explicit dark
   block, and inheriting the default by omission would leave that dependent on
   source order alone. */
:root[data-ui="v5"][data-theme="light"] {
  --v5-bg:#F2EDE3;
  --v5-bloom-a:rgba(14,107,94,.16);
  --v5-bloom-b:rgba(168,121,31,.17);
  --v5-bloom-c:rgba(14,107,94,.08);

  --v5-surface:rgba(255,255,255,.72);
  --v5-surface-2:rgba(255,255,255,.92);
  --v5-surface-solid:#FFFFFF;
  --v5-line:rgba(80,66,40,.11);
  --v5-line-2:rgba(80,66,40,.2);
  --v5-sunken:rgba(80,66,40,.07);

  --v5-text:#1B1A17;
  --v5-text-muted:#756E62;
  --v5-text-faint:#A29988;

  --v5-accent:#0E6B5E;
  --v5-accent-2:#0A5449;
  --v5-accent-hi:#17998A;
  --v5-accent-tint:rgba(14,107,94,.11);
  --v5-accent-line:rgba(14,107,94,.26);
  --v5-on-accent:#FFFFFF;
  --v5-grad-accent:linear-gradient(158deg,#15866F 0%,#0E6B5E 55%,#0A4E45 100%);
  --v5-glow:0 6px 18px rgba(14,107,94,.22);
  --v5-edge-glow:0 0 0 rgba(0,0,0,0);

  --v5-gold:#A8791F;
  --v5-gold-2:#D4A63C;
  --v5-gold-tint:rgba(168,121,31,.13);
  --v5-gold-line:rgba(168,121,31,.28);

  --v5-positive:#2C7A4B;
  --v5-positive-tint:rgba(44,122,75,.12);
  --v5-positive-line:rgba(44,122,75,.26);
  --v5-positive-hover:#245F3B;
  --v5-negative:#BE3A31;
  --v5-negative-tint:rgba(190,58,49,.11);
  --v5-negative-line:rgba(190,58,49,.25);
  --v5-negative-hover:#9E2E27;

  --v5-ov-bg:rgba(255,255,255,.82);
  --v5-ov-line:rgba(255,255,255,.95);
  --v5-ov-gloss:inset 0 1px 0 rgba(255,255,255,1);
  --v5-ov-blur:blur(26px) saturate(150%);
  --v5-scrim:rgba(40,30,16,.42);

  --v5-sh:0 6px 22px rgba(60,48,26,.10);
  --v5-sh-float:0 14px 40px rgba(60,48,26,.18);
}

/* ── Theme: dark (explicit) ─────────────────────────────────────────────────
   0,3,0, so it beats both the default block above and style.css's dark block.
   Warm near-black rather than the legacy blue-grey; surfaces stay layered
   (bg < surface-solid) so a card still reads as raised. */
:root[data-ui="v5"][data-theme="dark"] {
  --v5-bg:#14120D;
  --v5-bloom-a:rgba(25,148,127,.20);
  --v5-bloom-b:rgba(201,162,74,.13);
  --v5-bloom-c:rgba(25,148,127,.09);

  --v5-surface:rgba(255,255,255,.048);
  --v5-surface-2:rgba(255,255,255,.078);
  --v5-surface-solid:#1E1C17;
  --v5-line:rgba(255,255,255,.085);
  --v5-line-2:rgba(255,255,255,.15);
  --v5-sunken:rgba(0,0,0,.3);

  --v5-text:#F0EBE0;
  --v5-text-muted:#9B9384;
  --v5-text-faint:#6C6558;

  --v5-accent:#1D9E88;
  --v5-accent-2:#0E6B5E;
  --v5-accent-hi:#2FBBA1;
  --v5-accent-tint:rgba(29,158,136,.15);
  --v5-accent-line:rgba(29,158,136,.34);
  --v5-on-accent:#04201B;
  --v5-grad-accent:linear-gradient(158deg,#1D9E88 0%,#12786A 55%,#0B5147 100%);
  --v5-glow:0 0 22px rgba(29,158,136,.34);
  --v5-edge-glow:0 0 9px currentColor;

  --v5-gold:#C9A24A;
  --v5-gold-2:#E0BE6C;
  --v5-gold-tint:rgba(201,162,74,.15);
  --v5-gold-line:rgba(201,162,74,.32);

  --v5-positive:#3EA869;
  --v5-positive-tint:rgba(62,168,105,.15);
  --v5-positive-line:rgba(62,168,105,.3);
  --v5-positive-hover:#4FBC7B;
  --v5-negative:#DF6357;
  --v5-negative-tint:rgba(223,99,87,.15);
  --v5-negative-line:rgba(223,99,87,.3);
  --v5-negative-hover:#EA7568;

  --v5-ov-bg:rgba(31,28,22,.72);
  --v5-ov-line:rgba(255,255,255,.16);
  --v5-ov-gloss:inset 0 1px 0 rgba(255,255,255,.15);
  --v5-ov-blur:blur(26px) saturate(150%);
  --v5-scrim:rgba(6,5,3,.62);

  --v5-sh:0 6px 24px rgba(0,0,0,.45);
  --v5-sh-float:0 14px 44px rgba(0,0,0,.6);
}

/* ── Theme: dark (OS preference, no cookie) ─────────────────────────────────
   Same values as the explicit dark block, scoped to :not([data-theme]) so an
   explicit light choice always wins over a dark OS — the same contract, and the
   same deliberate duplication, as style.css:108. A no-JS first paint cannot
   read a shared constant, so the two copies are kept in sync by hand. */
@media (prefers-color-scheme: dark) {
  :root[data-ui="v5"]:not([data-theme]) {
    --v5-bg:#14120D;
    --v5-bloom-a:rgba(25,148,127,.20);
    --v5-bloom-b:rgba(201,162,74,.13);
    --v5-bloom-c:rgba(25,148,127,.09);

    --v5-surface:rgba(255,255,255,.048);
    --v5-surface-2:rgba(255,255,255,.078);
    --v5-surface-solid:#1E1C17;
    --v5-line:rgba(255,255,255,.085);
    --v5-line-2:rgba(255,255,255,.15);
    --v5-sunken:rgba(0,0,0,.3);

    --v5-text:#F0EBE0;
    --v5-text-muted:#9B9384;
    --v5-text-faint:#6C6558;

    --v5-accent:#1D9E88;
    --v5-accent-2:#0E6B5E;
    --v5-accent-hi:#2FBBA1;
    --v5-accent-tint:rgba(29,158,136,.15);
    --v5-accent-line:rgba(29,158,136,.34);
    --v5-on-accent:#04201B;
    --v5-grad-accent:linear-gradient(158deg,#1D9E88 0%,#12786A 55%,#0B5147 100%);
    --v5-glow:0 0 22px rgba(29,158,136,.34);
    --v5-edge-glow:0 0 9px currentColor;

    --v5-gold:#C9A24A;
    --v5-gold-2:#E0BE6C;
    --v5-gold-tint:rgba(201,162,74,.15);
    --v5-gold-line:rgba(201,162,74,.32);

    --v5-positive:#3EA869;
    --v5-positive-tint:rgba(62,168,105,.15);
    --v5-positive-line:rgba(62,168,105,.3);
    --v5-positive-hover:#4FBC7B;
    --v5-negative:#DF6357;
    --v5-negative-tint:rgba(223,99,87,.15);
    --v5-negative-line:rgba(223,99,87,.3);
    --v5-negative-hover:#EA7568;

    --v5-ov-bg:rgba(31,28,22,.72);
    --v5-ov-line:rgba(255,255,255,.16);
    --v5-ov-gloss:inset 0 1px 0 rgba(255,255,255,.15);
    --v5-ov-blur:blur(26px) saturate(150%);
    --v5-scrim:rgba(6,5,3,.62);

    --v5-sh:0 6px 24px rgba(0,0,0,.45);
    --v5-sh-float:0 14px 44px rgba(0,0,0,.6);
  }
}

/* ── Scales (theme-independent) ─────────────────────────────────────────────
   Eight type sizes replacing the current ~30, six spacing steps replacing ~90,
   five radii replacing 13. These are the ONLY permitted values: no later round
   introduces a ninth font size or a seventh spacing step. Deliberately outside
   the theme blocks — nothing here changes between light and dark. */
:root[data-ui="v5"] {
  --v5-t-xs:.6875rem;  /* 11 */
  --v5-t-sm:.75rem;    /* 12 */
  --v5-t-md:.8125rem;  /* 13 */
  --v5-t-base:.875rem; /* 14 */
  --v5-t-lg:1rem;      /* 16 */
  --v5-t-xl:1.25rem;   /* 20 */
  --v5-t-2xl:1.625rem; /* 26 */
  --v5-t-3xl:2.25rem;  /* 36 */

  --v5-sp-1:4px; --v5-sp-2:8px; --v5-sp-3:12px;
  --v5-sp-4:16px; --v5-sp-5:20px; --v5-sp-6:24px;

  --v5-r-sm:10px; --v5-r-md:14px; --v5-r-lg:20px;
  --v5-r-xl:26px; --v5-r-pill:999px;

  --v5-row-compact:10px 16px;
  --v5-row-comfy:14px 16px;

  --v5-dur:160ms;
  --v5-ease:cubic-bezier(.2,.7,.3,1);
}

/* ── Aliases (Addendum A4) ──────────────────────────────────────────────────
   Every one of the 32 properties in style.css's :root, re-pointed at the
   nearest v5 token. Nothing in style.css is edited and nothing is renamed or
   deleted, so every existing rule keeps resolving — including the one runtime
   expression that builds a property name as a string at src/app.js:2869, which
   consumes --green and --red and needs no change.

   Notes on the three non-obvious entries:
   • --surface / --raised → --v5-surface-solid, not --v5-surface. They are used
     across the legacy stylesheet as opaque card backgrounds; the translucent
     token would let nested surfaces stack and go muddy.
   • --radius moves 10px → 14px under v5 only. Intended.
   • --logo-plate stays `none` — it is not a colour, and the logo PNG cannot be
     recoloured per theme. */
:root[data-ui="v5"] {
  /* green family → positive */
  --green:            var(--v5-positive);
  --green-hover:      var(--v5-positive-hover);
  --green-dot:        var(--v5-positive);
  --green-tint:       var(--v5-positive-tint);

  /* red family → negative */
  --red:              var(--v5-negative);
  --red-hover:        var(--v5-negative-hover);
  --red-dot:          var(--v5-negative);
  --red-tint:         var(--v5-negative-tint);
  --red-tint-border:  var(--v5-negative-line);
  --red-tint-soft:    var(--v5-negative-tint);

  /* warning / amber → gold */
  --warn:             var(--v5-gold);
  --warn-text:        var(--v5-gold);

  /* chart profit bars → gold */
  --chart-profit-1:   var(--v5-gold);
  --chart-profit-2:   var(--v5-gold-2);

  /* neutrals / surfaces */
  --bg:               var(--v5-bg);
  --surface:          var(--v5-surface-solid);
  --raised:           var(--v5-surface-solid);
  --hover:            var(--v5-surface-2);
  --warm-panel:       var(--v5-surface-2);
  --border:           var(--v5-line);
  --text:             var(--v5-text);
  --text-muted:       var(--v5-text-muted);
  --on-accent:        var(--v5-on-accent);

  /* backdrop + elevation */
  --backdrop:         var(--v5-scrim);
  --shadow:           var(--v5-sh);
  --shadow-md:        var(--v5-sh);
  --shadow-toast:     var(--v5-sh);
  --shadow-menu:      var(--v5-sh-float);
  --shadow-modal:     var(--v5-sh-float);

  /* brand mark — unchanged */
  --logo-plate:       none;

  /* geometry / type */
  --radius:           var(--v5-r-md);
  --font:             'Vazirmatn', Tahoma, Arial, sans-serif;
}

}

/* ── Base (Phase 3) ─────────────────────────────────────────────────────────
   Bare-element defaults only: no classes, no IDs, no !important, no new custom
   properties. Every selector is gated on :root[data-ui="v5"], so without the
   `ui` cookie not one declaration applies. Logical properties throughout (no
   physical left/right), which is what keeps the RTL document correct without a
   mirrored stylesheet. Heading tracking is pinned to 0 rather than negative:
   Persian is a joining script and letter-spacing breaks the connections.
   ──────────────────────────────────────────────────────────────────────────── */
@layer base {

/* universal box-sizing */
:root[data-ui="v5"],
:root[data-ui="v5"] *,
:root[data-ui="v5"] *::before,
:root[data-ui="v5"] *::after { box-sizing:border-box; }

/* document body */
:root[data-ui="v5"] body {
  font-family:var(--font);
  font-size:var(--v5-t-base);
  line-height:1.6;
  color:var(--v5-text);
  background:var(--v5-bg);
  -webkit-font-smoothing:antialiased;
}

/* ambient bloom — fixed, behind everything, never intercepts input */
:root[data-ui="v5"] body::before {
  content:"";
  position:fixed;
  inset-block:0;
  inset-inline:0;
  z-index:-1;
  pointer-events:none;
  background:
    radial-gradient(60rem 40rem at 78% -8%, var(--v5-bloom-a), transparent 62%),
    radial-gradient(46rem 34rem at 8% 4%, var(--v5-bloom-b), transparent 60%),
    radial-gradient(70rem 48rem at 50% 108%, var(--v5-bloom-c), transparent 66%);
}

/* Round 16 — touch feedback. Mobile WebKit paints a translucent blue rectangle
   over whatever was tapped for ~200ms, on every <a>, <button>, <label>, icon
   and control. It is the browser's own affordance, it ignores border-radius so
   it squares off every pill and round icon button, and the app already has its
   own :active paint — so it reads as a flash of someone else's design.
   `transparent` removes only that overlay; it is not a focus, hover or active
   style and no other rule sets this property, so nothing below it changes.
   Global like the box-sizing reset above rather than `.v5-scope`-gated: the
   flash happens on every page under the cookie, including the ones not yet
   migrated, and there is no legacy variant of this property to overwrite.
   :focus-visible (below) is untouched — keyboard focus rings still paint. */
:root[data-ui="v5"] * { -webkit-tap-highlight-color:transparent; }

/* Migrated-subtree defaults. `@layer base` outranks `@layer legacy`
   regardless of specificity, so element rules must never be global: doing so
   silently overwrites legacy class and parent-selector variants. `.v5-scope`
   is the non-visual opt-in marker added only when a page is migrated. */

/* heading scale */
:root[data-ui="v5"] .v5-scope h1,
:root[data-ui="v5"] .v5-scope h2,
:root[data-ui="v5"] .v5-scope h3,
:root[data-ui="v5"] .v5-scope h4,
:root[data-ui="v5"] .v5-scope h5,
:root[data-ui="v5"] .v5-scope h6 {
  margin-block:0 var(--v5-sp-3);
  line-height:1.35;
  letter-spacing:0;
  color:var(--v5-text);
}
:root[data-ui="v5"] .v5-scope h1 { font-size:var(--v5-t-3xl); font-weight:800; line-height:1.25; }
:root[data-ui="v5"] .v5-scope h2 { font-size:var(--v5-t-2xl); font-weight:800; }
:root[data-ui="v5"] .v5-scope h3 { font-size:var(--v5-t-xl);  font-weight:800; }
:root[data-ui="v5"] .v5-scope h4 { font-size:var(--v5-t-lg);  font-weight:800; }
:root[data-ui="v5"] .v5-scope h5 { font-size:var(--v5-t-md);  font-weight:800; }
:root[data-ui="v5"] .v5-scope h6 { font-size:var(--v5-t-sm);  font-weight:800; }

/* flow content */
:root[data-ui="v5"] .v5-scope p { margin-block:0 var(--v5-sp-3); }
:root[data-ui="v5"] .v5-scope ul,
:root[data-ui="v5"] .v5-scope ol {
  margin-block:0 var(--v5-sp-3);
  padding-inline-start:var(--v5-sp-5);
}
:root[data-ui="v5"] .v5-scope li { margin-block-end:var(--v5-sp-1); }

/* links */
:root[data-ui="v5"] .v5-scope a {
  color:var(--v5-accent);
  text-decoration:none;
  text-underline-offset:.2em;
  transition:color var(--v5-dur) var(--v5-ease);
}
:root[data-ui="v5"] .v5-scope a:hover { color:var(--v5-accent-2); text-decoration:underline; }

/* form controls */
:root[data-ui="v5"] .v5-scope button,
:root[data-ui="v5"] .v5-scope input,
:root[data-ui="v5"] .v5-scope select,
:root[data-ui="v5"] .v5-scope textarea {
  font:inherit;
  font-size:var(--v5-t-base);
  color:var(--v5-text);
  background:var(--v5-sunken);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-md);
  padding-block:var(--v5-sp-2);
  padding-inline:var(--v5-sp-3);
  transition:border-color var(--v5-dur) var(--v5-ease), box-shadow var(--v5-dur) var(--v5-ease);
}
:root[data-ui="v5"] .v5-scope button { cursor:pointer; font-weight:600; }
:root[data-ui="v5"] .v5-scope textarea { resize:vertical; min-block-size:6em; }
:root[data-ui="v5"] .v5-scope button:disabled,
:root[data-ui="v5"] .v5-scope input:disabled,
:root[data-ui="v5"] .v5-scope select:disabled,
:root[data-ui="v5"] .v5-scope textarea:disabled { opacity:.55; cursor:not-allowed; }

/* Round 6 — a field that is filled in and cannot be typed into: the manual
   طلب/بدهی form's LOCKED name on the person page, whose value the hidden
   inputs beside it carry. Legacy tells it apart by a class, which this file
   may not name (§12 forbids a legacy collision) and which the `input` rule
   above out-ranks by layer order anyway — so without this the field would look
   exactly like the editable ones and promise typing that `readonly` refuses.
   The state is already on the element, and §12 says to target the real
   control, so the paint hangs off the attribute and every form migrated later
   inherits it. Solid fill, not `.g`: this sits inside the modal's `.ov` and a
   translucent fill over a translucent sheet is the muddy stack §5 forbids.
   Paint only — no `display`, no geometry, nothing behaviour reads. */
:root[data-ui="v5"] .v5-scope input[readonly] {
  background:var(--v5-surface-solid);
  color:var(--v5-text-muted);
  cursor:default;
}

/* focus — keyboard only */
:root[data-ui="v5"] .v5-scope :focus-visible {
  outline:2px solid var(--v5-accent-line);
  outline-offset:2px;
  border-radius:var(--v5-r-md);
}

/* secondary text */
:root[data-ui="v5"] .v5-scope label {
  display:inline-block;
  font-weight:600;
  font-size:var(--v5-t-xs);
  color:var(--v5-text-muted);
  margin-block-end:var(--v5-sp-1);
}
:root[data-ui="v5"] .v5-scope small { font-size:var(--v5-t-xs); color:var(--v5-text-muted); }

/* tables */
:root[data-ui="v5"] .v5-scope table { inline-size:100%; border-collapse:collapse; font-size:var(--v5-t-sm); }
:root[data-ui="v5"] .v5-scope th,
:root[data-ui="v5"] .v5-scope td {
  padding:var(--v5-row-compact);
  text-align:start;
  border-block-end:1px solid var(--v5-line);
  vertical-align:middle;
}
:root[data-ui="v5"] .v5-scope th {
  font-weight:700;
  color:var(--v5-text-muted);
  background:var(--v5-sunken);
  border-block-end:1px solid var(--v5-line-2);
}

/* rules */
:root[data-ui="v5"] .v5-scope hr {
  border:0;
  block-size:1px;
  background:var(--v5-line);
  margin-block:var(--v5-sp-4);
}

/* placeholders */
:root[data-ui="v5"] .v5-scope ::placeholder { color:var(--v5-text-faint); opacity:1; }

/* motion opt-out */
@media (prefers-reduced-motion:reduce) {
  :root[data-ui="v5"] *,
  :root[data-ui="v5"] *::before,
  :root[data-ui="v5"] *::after {
    animation-duration:.01ms;
    animation-iteration-count:1;
    transition-duration:.01ms;
    scroll-behavior:auto;
  }
}
}

@layer components {
:root[data-ui="v5"] .ic { width:1.25em; height:1.25em; flex:none; display:block }
:root[data-ui="v5"] .ic-sm { width:1em; height:1em }
:root[data-ui="v5"] .ic-lg { width:1.5em; height:1.5em }
:root[data-ui="v5"] .g { background:var(--v5-surface); border:1px solid var(--v5-line); border-radius:var(--v5-r-lg); box-shadow:var(--v5-sh) }
:root[data-ui="v5"] .ov { background:var(--v5-ov-bg); border:1px solid var(--v5-ov-line); box-shadow:var(--v5-ov-gloss),var(--v5-sh-float); -webkit-backdrop-filter:var(--v5-ov-blur); backdrop-filter:var(--v5-ov-blur) }
:root[data-ui="v5"] .ib { inline-size:38px; block-size:38px; display:grid; place-items:center; border-radius:var(--v5-r-md); border:1px solid var(--v5-ov-line); background:var(--v5-ov-bg); box-shadow:var(--v5-ov-gloss); -webkit-backdrop-filter:var(--v5-ov-blur); backdrop-filter:var(--v5-ov-blur) }
:root[data-ui="v5"] .hdr { display:flex; align-items:center; gap:var(--v5-sp-3); padding:var(--v5-sp-5) var(--v5-sp-4) var(--v5-sp-3) }
:root[data-ui="v5"] .hdr-t { flex:1; min-inline-size:0 }
:root[data-ui="v5"] .hdr-t b { display:block; font-size:var(--v5-t-lg); font-weight:800; letter-spacing:-.02em }
:root[data-ui="v5"] .hdr-t span { display:block; font-size:var(--v5-t-xs); color:var(--v5-text-muted); margin-block-start:2px; font-weight:500 }
:root[data-ui="v5"] .hdr-t .v5-settings-title { display:flex; align-items:center; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .hdr-t .v5-settings-title-text { display:inline; font-size:inherit; font-weight:inherit; color:inherit; margin-block-start:0 }
/* Settings page header title only (not the hamburger-menu row, which never
   uses .v5-settings-title). Two steps up the shared type scale from .hdr-t
   b's --v5-t-lg (lg → xl → 2xl) and heavier than its inherited 800, on the
   text span only. The gear tracks it by re-pointing .ic's own em-based
   width/height (§"icons") at the same token via font-size, rather than
   redefining width/height here, so it stays in lockstep with the text and
   its stroke thickens along with it without any other .ic gaining size. */
:root[data-ui="v5"] .hdr-t .v5-settings-title-text { font-size:var(--v5-t-2xl); font-weight:900 }
:root[data-ui="v5"] .hdr-t .v5-settings-title .ic { font-size:var(--v5-t-2xl) }
/* /personal-only header title: expenses-home.js's renderExpensesHomeV5 swaps
   the header's visible «امور شخصی» text for the /personal tab's own wallet
   icon (layout.js's NAV_TABS) when personal=true, via `.v5-personal-title` on
   the `title` payload itself — pageHeader() and .hdr-t stay untouched, same
   technique as `.v5-settings-title` above. Icon-only, so the icon is sized by
   re-pointing .ic's own em-based width/height at the shared type scale via
   font-size, exactly like the gear above, rather than redefining width/height
   here or touching bare `.ic`. */
:root[data-ui="v5"] .hdr-t .v5-personal-title { display:flex; align-items:center }
:root[data-ui="v5"] .hdr-t .v5-personal-title .ic { font-size:var(--v5-t-2xl) }
:root[data-ui="v5"] .row { display:flex; gap:var(--v5-sp-3); padding:var(--v5-row-compact); position:relative }
:root[data-ui="v5"] .row + .row { border-block-start:1px solid var(--v5-line) }
:root[data-ui="v5"] .row.comfy { padding:var(--v5-row-comfy) }
:root[data-ui="v5"] .row::before { content:""; position:absolute; inset-block:0; inset-inline-start:0; inline-size:3px; border-radius:0 3px 3px 0; background:transparent }
:root[data-ui="v5"] .row.ok::before { background:var(--v5-positive); color:var(--v5-positive); box-shadow:var(--v5-edge-glow) }
:root[data-ui="v5"] .row.no::before { background:var(--v5-negative); color:var(--v5-negative); box-shadow:var(--v5-edge-glow) }
:root[data-ui="v5"] .rm { flex:1; min-inline-size:0 }
:root[data-ui="v5"] .rt { font-weight:800 }
:root[data-ui="v5"] .rs { color:var(--v5-text-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
:root[data-ui="v5"] .amt { font-weight:800; font-variant-numeric:tabular-nums }
:root[data-ui="v5"] .amt.p { color:var(--v5-positive) }
:root[data-ui="v5"] .amt.n { color:var(--v5-negative) }
:root[data-ui="v5"] .av { inline-size:40px; block-size:40px; flex:none; display:grid; place-items:center; background:var(--v5-accent-tint); color:var(--v5-accent); border-radius:var(--v5-r-md) }
:root[data-ui="v5"] .av.ps { background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .av.ng { background:var(--v5-negative-tint); color:var(--v5-negative) }
:root[data-ui="v5"] .av.gd { background:var(--v5-gold-tint); color:var(--v5-gold) }
:root[data-ui="v5"] .chipx { display:inline-flex; padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-2); font-size:var(--v5-t-xs); border-radius:var(--v5-r-pill); background:var(--v5-accent-tint); color:var(--v5-accent) }
:root[data-ui="v5"] .chipx.p { background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .v5-btn { background:var(--v5-grad-accent); color:var(--v5-on-accent); border:1px solid var(--v5-accent); border-radius:var(--v5-r-md); box-shadow:var(--v5-glow); font-weight:800; padding:var(--v5-row-compact) }
:root[data-ui="v5"] .v5-btn.gh { background:var(--v5-surface); color:var(--v5-accent); border-color:var(--v5-accent-line); box-shadow:none }
:root[data-ui="v5"] .v5-btn.ps { background:var(--v5-positive-tint); color:var(--v5-positive); border-color:var(--v5-positive-line); box-shadow:none }
:root[data-ui="v5"] .v5-btn.ng { background:var(--v5-negative-tint); color:var(--v5-negative); border-color:var(--v5-negative-line); box-shadow:none }
:root[data-ui="v5"] .v5-btn.q { background:transparent; color:var(--v5-text-muted); border-color:var(--v5-line-2); box-shadow:none; inline-size:40px }
:root[data-ui="v5"] .brow { display:flex; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-btn:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }
:root[data-ui="v5"] .v5-btn:disabled { opacity:.55; cursor:not-allowed }
:root[data-ui="v5"] input,:root[data-ui="v5"] select,:root[data-ui="v5"] textarea { background:var(--v5-sunken); border:1px solid var(--v5-line); border-radius:var(--v5-r-md); color:var(--v5-text) }
:root[data-ui="v5"] input:focus,:root[data-ui="v5"] select:focus,:root[data-ui="v5"] textarea:focus { border-color:var(--v5-accent); outline:3px solid var(--v5-accent-tint) }
:root[data-ui="v5"] input[aria-invalid="true"],:root[data-ui="v5"] select[aria-invalid="true"],:root[data-ui="v5"] textarea[aria-invalid="true"] { border-color:var(--v5-negative-line) }
:root[data-ui="v5"] .fld { display:grid; gap:var(--v5-sp-1) }
:root[data-ui="v5"] .fld label { font-size:var(--v5-t-xs); color:var(--v5-text-muted); font-weight:700 }
:root[data-ui="v5"] .two { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--v5-sp-3) }
:root[data-ui="v5"] .srch { display:flex; align-items:center; gap:var(--v5-sp-2); padding-inline:var(--v5-sp-3) }
:root[data-ui="v5"] .srch .ic { color:var(--v5-accent) }
:root[data-ui="v5"] .srch input { flex:1; min-inline-size:0; border:0; outline:0; background:transparent }
:root[data-ui="v5"] .bento { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--v5-sp-2) }
:root[data-ui="v5"] .b-hero { grid-column:span 3; position:relative; overflow:hidden }
:root[data-ui="v5"] .b-hero::after { content:""; position:absolute; inset:0; background:radial-gradient(circle at 80% 20%,var(--v5-gold-tint),transparent 55%); pointer-events:none }
:root[data-ui="v5"] .b-tile { display:flex; flex-direction:column; align-items:center; text-align:center; position:relative }
:root[data-ui="v5"] .b-tile .ic { inline-size:46px; block-size:46px; padding:var(--v5-sp-2); border-radius:var(--v5-r-md); background:var(--v5-accent-tint); color:var(--v5-accent); box-shadow:var(--v5-glow) }
:root[data-ui="v5"] .b-tile.gd .ic { background:var(--v5-gold-tint); color:var(--v5-gold) }
:root[data-ui="v5"] .b-tile.ps .ic { background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .b-tile.ng .ic { background:var(--v5-negative-tint); color:var(--v5-negative) }
:root[data-ui="v5"] .b-tile.nt .ic { background:var(--v5-sunken); color:var(--v5-text-muted) }
:root[data-ui="v5"] .b-tile i .cnt { position:absolute; inset-inline-end:-5px }
:root[data-ui="v5"] .b-wide { grid-column:span 3; display:flex; align-items:center; justify-content:space-between }
:root[data-ui="v5"] .lbl { font-size:var(--v5-t-xs); color:var(--v5-text-muted) }
:root[data-ui="v5"] .lbl .ic { color:var(--v5-accent) }
:root[data-ui="v5"] .num { font-size:var(--v5-t-3xl); font-weight:800; letter-spacing:-.04em }
:root[data-ui="v5"] .num u { font-size:var(--v5-t-xs); text-decoration:none }
:root[data-ui="v5"] .delta { border-radius:var(--v5-r-pill); padding:var(--v5-sp-1) var(--v5-sp-2); background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .delta.n { background:var(--v5-negative-tint); color:var(--v5-negative) }
:root[data-ui="v5"] .delta.a { background:var(--v5-accent-tint); color:var(--v5-accent) }
:root[data-ui="v5"] .spark { position:absolute; inset-block-end:0 }
:root[data-ui="v5"] { --v5-nav-h:64px }
:root[data-ui="v5"] .nav { position:fixed; inset-inline:var(--v5-sp-4); inset-block-end:calc(var(--v5-sp-4) + env(safe-area-inset-bottom)); border-radius:var(--v5-r-pill); z-index:10 }
:root[data-ui="v5"] .nb { color:var(--v5-text-muted) }
:root[data-ui="v5"] .nb[aria-current="page"] { background:var(--v5-accent-tint); color:var(--v5-accent); box-shadow:inset 0 0 0 1px var(--v5-accent-line) }
:root[data-ui="v5"] .fab { position:fixed; inset-inline-start:var(--v5-sp-4); inset-block-end:calc(var(--v5-sp-4) + env(safe-area-inset-bottom) + var(--v5-nav-h)); z-index:11 }
:root[data-ui="v5"] .has-nav { padding-block-end:calc(var(--v5-nav-h) + env(safe-area-inset-bottom)) }
:root[data-ui="v5"] .scrim { position:fixed; inset:0; background:var(--v5-scrim); -webkit-backdrop-filter:blur(4px); backdrop-filter:blur(4px) }
:root[data-ui="v5"] .sheet { border-start-start-radius:var(--v5-r-xl); border-start-end-radius:var(--v5-r-xl) }
:root[data-ui="v5"] .grab { inline-size:var(--v5-sp-6); block-size:var(--v5-sp-1); margin:var(--v5-sp-2) auto; border-radius:var(--v5-r-pill); background:var(--v5-line-2) }
:root[data-ui="v5"] .mi { color:var(--v5-text) }
:root[data-ui="v5"] .mi.dg { color:var(--v5-negative) }
:root[data-ui="v5"] .msep { block-size:1px; background:var(--v5-line) }
:root[data-ui="v5"] .menu { border-radius:var(--v5-r-lg); padding:var(--v5-sp-2) }
/* Round 3 — the REAL app's bottom bar and hamburger panel.
   Prefixed per the class rule in §12 of DESIGN-SYSTEM.md; the unprefixed .nav /
   .nb / .has-nav / .menu / .mi / .msep / .ov above are the /_design preview's
   and are deliberately left untouched. These carry the same declarations plus
   the layout the preview supplied with inline styles, since a real page has no
   inline styles to lean on. --v5-nav-h is the bar's own height (§12: a literal
   component dimension), so .v5-has-nav can reserve exactly that plus the bar's
   inset and one step of clearance — content ends above the bar, never behind it. */
:root[data-ui="v5"] .v5-nav { position:fixed; inset-inline:var(--v5-sp-4); inset-block-end:calc(var(--v5-sp-4) + env(safe-area-inset-bottom)); z-index:10; display:flex; align-items:stretch; gap:var(--v5-sp-1); block-size:var(--v5-nav-h); padding:var(--v5-sp-2); border-radius:var(--v5-r-pill) }
:root[data-ui="v5"] .v5-nb { flex:1; min-inline-size:0; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:var(--v5-sp-1); border-radius:var(--v5-r-pill); color:var(--v5-text-muted); font-size:var(--v5-t-xs); font-weight:700; text-align:center; text-decoration:none }
:root[data-ui="v5"] .v5-nb .ic { inline-size:22px; block-size:22px }
:root[data-ui="v5"] .v5-nb span { max-inline-size:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
:root[data-ui="v5"] .v5-nb[aria-current="page"] { background:var(--v5-accent-tint); color:var(--v5-accent); box-shadow:inset 0 0 0 1px var(--v5-accent-line) }
:root[data-ui="v5"] .v5-has-nav { padding-block-end:calc(var(--v5-nav-h) + var(--v5-sp-6) + env(safe-area-inset-bottom)) }
/* Two pre-existing fixed controls stand exactly where the bar now is, and both
   out-stack it (#toast z-index 100, back-to-top 50), so they would sit ON the
   bar rather than behind it. Lifted above it — in v5 only, by their existing
   ids, with no markup change and no edit to style.css. #toast is on every page
   including /login, which has no bar, so it is lifted only when a .v5-nav is
   actually present; the warehouse button only exists on a page that always has
   one. The modal backdrop deliberately keeps covering the bar (z-index 200). */
:root[data-ui="v5"]:has(.v5-nav) #toast { inset-block-end:calc(var(--v5-sp-5) + var(--v5-nav-h) + env(safe-area-inset-bottom)) }
:root[data-ui="v5"] #warehouse-back-to-top { inset-block-end:calc(var(--v5-sp-4) + var(--v5-sp-2) + var(--v5-nav-h) + env(safe-area-inset-bottom)) }
/* The same button's glyph sat off-centre in the 44px circle: legacy gives it no
   display, so the icon was an inline-level box riding the line box's baseline
   and the button's default padding. Centred here without touching the size,
   position or circle above. The `:not([hidden])` is load-bearing, not cosmetic —
   style.css hides this button with `#warehouse-back-to-top[hidden]{display:none}`
   at (1,1,0), and an unqualified `:root[data-ui="v5"] #warehouse-back-to-top`
   is (1,2,0), so a bare `display:grid` here would outrank it and strand the
   button on screen at every scroll position. The icon itself needs nothing: the
   `.ic` rule in §"icons" already makes it `display:block`, so it lands as the
   single grid item that `place-items:center` centres on both axes. */
:root[data-ui="v5"] #warehouse-back-to-top:not([hidden]) { display:grid; place-items:center; padding:0; line-height:1 }
:root[data-ui="v5"] .v5-ov { background:var(--v5-ov-bg); border:1px solid var(--v5-ov-line); box-shadow:var(--v5-ov-gloss),var(--v5-sh-float); -webkit-backdrop-filter:var(--v5-ov-blur); backdrop-filter:var(--v5-ov-blur) }
:root[data-ui="v5"] .v5-menu { border-radius:var(--v5-r-lg); padding:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-mi { color:var(--v5-text) }
:root[data-ui="v5"] .v5-mi.dg { color:var(--v5-negative) }
/* «تنظیمات» row only: .ic is display:block (see §"icons"), which inside a
   plain block .v5-mi stacks the gear above the label onto a second line and
   grows the row/panel. A flex wrapper scoped to this one row's icon+label
   puts them back on one line without touching .v5-mi's block layout, which
   the account/theme/logout rows still rely on. */
:root[data-ui="v5"] .menu-item-settings-ico { display:inline-flex; align-items:center; gap:.4rem }
:root[data-ui="v5"] .v5-msep { block-size:1px; background:var(--v5-line) }
:root[data-ui="v5"] .v5-nb,:root[data-ui="v5"] .v5-mi { transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),box-shadow var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .st { display:grid; grid-template-columns:repeat(2,1fr); border:1px solid var(--v5-line) }
:root[data-ui="v5"] .st b.p { color:var(--v5-positive) }
:root[data-ui="v5"] .st b.n { color:var(--v5-negative) }
:root[data-ui="v5"] .st b.a { color:var(--v5-accent) }
:root[data-ui="v5"] .bars { display:flex; align-items:end; gap:var(--v5-sp-2); block-size:132px }
:root[data-ui="v5"] .bar { flex:1; display:flex; align-items:end; block-size:100% }
:root[data-ui="v5"] .bar i { display:block; inline-size:100%; block-size:50%; background:var(--v5-accent-tint) }
:root[data-ui="v5"] .bar:nth-child(2n) i { block-size:70% }
:root[data-ui="v5"] .chart { padding:var(--v5-sp-4) }
:root[data-ui="v5"] .bar i.on { background:var(--v5-grad-accent); box-shadow:var(--v5-glow) }
:root[data-ui="v5"] .bar i.gd { background:linear-gradient(180deg,var(--v5-gold-2),var(--v5-gold)) }
:root[data-ui="v5"] .gauge-ring { inline-size:104px; block-size:104px; flex:none; transform:rotate(-90deg) }
:root[data-ui="v5"] .gauge-track { fill:none; stroke:var(--v5-line); stroke-width:11 }
:root[data-ui="v5"] .gauge-neg { fill:none; stroke:var(--v5-negative); stroke-width:11; stroke-linecap:round }
:root[data-ui="v5"] .gauge-pos { fill:none; stroke:var(--v5-positive); stroke-width:11; stroke-linecap:round }
:root[data-ui="v5"] .spark { position:absolute; inset-inline-start:0; inset-block-end:0; inline-size:100%; block-size:58px; z-index:0 }
:root[data-ui="v5"] .b-hero > :not(.spark) { position:relative; z-index:1 }
:root[data-ui="v5"] .spark-area { fill:var(--v5-accent); fill-opacity:.18; stroke:none }
:root[data-ui="v5"] .spark-line { fill:none; stroke:var(--v5-accent); stroke-width:2; stroke-linejoin:round; stroke-linecap:round }
:root[data-ui="v5"] .gauge { transform:rotate(-90deg) }
:root[data-ui="v5"] .gleg { display:grid; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .chips { display:flex; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .chips [aria-pressed="true"] { background:var(--v5-grad-accent) }
:root[data-ui="v5"] .step { display:flex; align-items:center; justify-content:space-between }
:root[data-ui="v5"] .eyebrow { display:flex; justify-content:space-between; color:var(--v5-text-muted) }
:root[data-ui="v5"] .eyebrow a,:root[data-ui="v5"] .empty a { color:var(--v5-accent) }
:root[data-ui="v5"] .empty { display:grid; place-items:center; color:var(--v5-text-muted) }
:root[data-ui="v5"] .v5-btn,:root[data-ui="v5"] .nb,:root[data-ui="v5"] .mi,:root[data-ui="v5"] .chips button { transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),box-shadow var(--v5-dur) var(--v5-ease) }
/* Round 4 / Part 1 — the REAL chooser's tile grid («/»).
   Prefixed per §12; the unprefixed .bento / .b-tile / .b-hero / .b-wide above
   are the /_design preview's and are deliberately left untouched. Only the two
   classes the chooser actually emits are defined: renderChooser() gets no
   numbers and Part 1 adds no query, so there is no hero, count or delta
   variant to carry one. The tile carries .g for its surface — .g never nests
   here, the page shell is not one. 46px is the documented icon-tile dimension
   (§12 component dimensions), not a spacing-scale value.
   The colour and text-decoration declarations are not redundant: `.v5-b-tile`
   is an <a>, and `@layer base`'s `.v5-scope a` / `a:hover` would otherwise
   paint all nine tiles accent and underline their labels on hover. */
:root[data-ui="v5"] .v5-bento { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--v5-sp-2) }
/* Grid-template override only, same specificity as `.v5-bento` so source order
   decides: a lone tile becomes one full-width rectangle (expenses landing). */
:root[data-ui="v5"] .v5-bento-1 { grid-template-columns:minmax(0,1fr) }
:root[data-ui="v5"] .v5-b-tile { display:flex; flex-direction:column; align-items:center; justify-content:flex-start; gap:var(--v5-sp-2); padding:var(--v5-sp-4) var(--v5-sp-2); text-align:center; color:var(--v5-text); text-decoration:none; transition:background var(--v5-dur) var(--v5-ease),box-shadow var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-b-tile .ic { inline-size:46px; block-size:46px; padding:var(--v5-sp-2); border-radius:var(--v5-r-md); background:var(--v5-accent-tint); color:var(--v5-accent); box-shadow:var(--v5-glow) }
:root[data-ui="v5"] .v5-b-tile b { font-size:var(--v5-t-md); font-weight:800; line-height:1.35 }
:root[data-ui="v5"] .v5-b-tile:hover { color:var(--v5-text); text-decoration:none; background:var(--v5-surface-2) }
/* The chooser keeps its branded header, and `.v5-scope` hands every h1 inside
   it the base heading rule — 36px with a 12px block-end margin. In a
   space-between flex header that margin shifts the mark off centre against the
   ☰ button, and 1.75em of 36px would render the logo at 63px. Restored to the
   header's own scale; nothing else about the header changes. */
:root[data-ui="v5"] .v5-chooser-head h1 { font-size:var(--v5-t-xl); margin-block-end:0 }
/* Round 4 / Part 2 — the REAL business home («/business») and the /search
   fragment that lands inside it. Prefixed per §12; the unprefixed .srch / .row
   / .rm / .rt / .rs / .amt / .av / .chipx / .empty above are the /_design
   preview's and are deliberately left untouched.
   Zone 1 — the search control. The field is a `.g` box (the section itself is
   NOT one, so no glass nests inside glass) and the input is transparent
   INSIDE it: the box owns the surface, the border and the focus ring, which is
   why the input's own border/outline is taken back off — the component input
   rules above are global under [data-ui="v5"], not scoped to `.v5-scope`, so
   they reach this field and have to be overridden here rather than left to
   layer order. #search-results carries a legacy top margin that would add to
   the wrapper's gap; zeroed for this wrapper only. */
:root[data-ui="v5"] .v5-srch { display:grid; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-srch #search-results { margin-block-start:0 }
:root[data-ui="v5"] .v5-srch-form { display:grid; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-srch-box { display:flex; align-items:center; gap:var(--v5-sp-2); padding-inline:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-srch-box .ic { color:var(--v5-accent) }
:root[data-ui="v5"] .v5-srch-box:focus-within { outline:2px solid var(--v5-accent-line); outline-offset:2px }
:root[data-ui="v5"] .v5-srch-input { flex:1; min-inline-size:0; padding-block:var(--v5-sp-3); padding-inline:0; background:transparent; border:0; border-radius:0; box-shadow:none; font-size:var(--v5-t-base); color:var(--v5-text) }
:root[data-ui="v5"] .v5-srch-input:focus { border:0; outline:0; box-shadow:none }
/* Real radios, visually hidden — exactly as legacy: the browser keeps
   single-select and keyboard arrows, and :checked drives the pill. The radio is
   invisible, so its focus ring is drawn on the label's span. */
:root[data-ui="v5"] .v5-seg { display:flex; gap:var(--v5-sp-1); padding:var(--v5-sp-1); border-radius:var(--v5-r-pill); background:var(--v5-sunken); border:1px solid var(--v5-line) }
:root[data-ui="v5"] .v5-seg-item { flex:1; margin:0 }
:root[data-ui="v5"] .v5-seg-item input { position:absolute; opacity:0; inline-size:0; block-size:0; padding:0; border:0; pointer-events:none }
:root[data-ui="v5"] .v5-seg-item span { display:block; padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-2); border-radius:var(--v5-r-pill); text-align:center; white-space:nowrap; font-size:var(--v5-t-sm); font-weight:700; color:var(--v5-text-muted); cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-seg-item input:checked + span { background:var(--v5-surface-solid); color:var(--v5-text); box-shadow:var(--v5-sh) }
:root[data-ui="v5"] .v5-seg-item input:focus-visible + span { outline:2px solid var(--v5-accent-line); outline-offset:2px }
/* Zone 2 — the two actions. `.v5-buy` is the filled positive; standalone uses
   (هزینه‌ها's landing page, where a button has no outline pair beside it) keep
   that full solid fill. Both buttons keep their legacy `btn btn-add` /
   `btn btn-sell` classes, which is why the paint has to be restated here:
   `.btn-add`'s green fill and `.btn-sell`'s red fill are still in
   `@layer legacy` underneath, out-ranked but present. `.btn`'s :active scale
   is deliberately left to legacy. */
:root[data-ui="v5"] .v5-brow { display:flex; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-buy,:root[data-ui="v5"] .v5-sell { flex:1; display:flex; align-items:center; justify-content:center; gap:var(--v5-sp-2); padding:var(--v5-row-comfy); border-radius:var(--v5-r-lg); font-size:var(--v5-t-base); font-weight:800; cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),box-shadow var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-buy { background:var(--v5-positive); color:var(--v5-on-accent); border:1px solid var(--v5-positive); box-shadow:var(--v5-sh) }
:root[data-ui="v5"] .v5-buy:hover { background:var(--v5-positive-hover); border-color:var(--v5-positive-hover) }
/* …except inside the HOME PAGE's action row (home.js's /business zone 2)
   and the CHOOSER's «کسب و کار من» action row (chooser.js's v5HomeBusiness,
   the app's «/» screen), where the two buttons sit SIDE BY SIDE and one
   filled block next to a bare outline reads as a hierarchy nobody asked for.
   `.v5-buy-outline` gives ثبت خرید a light, PERMANENT green tint fill — not
   transparent, not hover-only — with green text/border; `.v5-sell` gets the
   matching permanent light red tint. So the pair reads buy-is-green /
   sell-is-red at rest, still rounded and boxed, and neither half shouts.
   This tint is keyed off TWO hooks, never bare `.v5-brow`: `.v5-brow-home`,
   stamped ONLY by home.js's renderHomeV5 on its own zone-2 row, and
   `.v5-brow-chooser`, stamped ONLY by chooser.js's v5HomeBusiness on its own
   action row. expenses-home.js's three `.v5-brow` pairs and debts.js's
   `.v5-brow` row carry NEITHER hook, so they still fall through to the base
   filled `.v5-buy` rule above / the legacy `.btn-sell` red — never to this
   tint. Do not widen either selector back to bare `.v5-brow`; that
   previously leaked the tint onto chooser.js's and expenses-home.js's own
   action rows, before `.v5-brow-chooser` gave the chooser its own opt-in.
   Hover restates the same tint — there is no darker "pressed" fill to hover
   towards since the tint IS the resting state now. */
:root[data-ui="v5"] .v5-brow-home .v5-buy.v5-buy-outline { background:var(--v5-positive-tint); color:var(--v5-positive); border:1px solid var(--v5-positive-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-home .v5-buy.v5-buy-outline:hover { background:var(--v5-positive-tint); border-color:var(--v5-positive-line) }
:root[data-ui="v5"] .v5-brow-home .v5-sell { background:var(--v5-negative-tint); color:var(--v5-negative); border:1px solid var(--v5-negative-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-home .v5-sell:hover { background:var(--v5-negative-tint); border-color:var(--v5-negative-line) }
/* CHOOSER-ONLY twin of the four rules above — identical paint, keyed off
   `.v5-brow-chooser` instead of `.v5-brow-home`. Kept as separate rules
   rather than merged into one comma selector so either screen's tint can be
   changed or removed later without touching the other's. */
:root[data-ui="v5"] .v5-brow-chooser .v5-buy.v5-buy-outline { background:var(--v5-positive-tint); color:var(--v5-positive); border:1px solid var(--v5-positive-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-chooser .v5-buy.v5-buy-outline:hover { background:var(--v5-positive-tint); border-color:var(--v5-positive-line) }
:root[data-ui="v5"] .v5-brow-chooser .v5-sell { background:var(--v5-negative-tint); color:var(--v5-negative); border:1px solid var(--v5-negative-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-chooser .v5-sell:hover { background:var(--v5-negative-tint); border-color:var(--v5-negative-line) }
/* PERSONAL-ONLY twin of the four rules above, keyed off `.v5-brow-personal` —
   stamped ONLY by expenses-home.js's renderExpensesHomeV5 on its personal=true
   طلب/بدهی action row, so its bare `.v5-brow` never gains this tint through
   the generic selector. ثبت طلب carries `.v5-buy-outline` here too, matching
   ثبت خرید's light green tint contract; ثبت بدهی keeps its light red tint. */
:root[data-ui="v5"] .v5-brow-personal .v5-buy.v5-buy-outline { background:var(--v5-positive-tint); color:var(--v5-positive); border:1px solid var(--v5-positive-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-personal .v5-buy.v5-buy-outline:hover { background:var(--v5-positive-tint); border-color:var(--v5-positive-line) }
:root[data-ui="v5"] .v5-brow-personal .v5-sell { background:var(--v5-negative-tint); color:var(--v5-negative); border:1px solid var(--v5-negative-line); box-shadow:none }
:root[data-ui="v5"] .v5-brow-personal .v5-sell:hover { background:var(--v5-negative-tint); border-color:var(--v5-negative-line) }
/* Zone 3 — the same bento tile the chooser uses, two-up. `.v5-bento-2` is a
   grid-template override only and must stay after `.v5-bento`: same
   specificity, source order decides. `.v5-b-sub` is home's own — the chooser's
   nine tiles are a label and nothing else. */
:root[data-ui="v5"] .v5-bento-2 { grid-template-columns:repeat(2,minmax(0,1fr)) }
:root[data-ui="v5"] .v5-b-sub { font-size:var(--v5-t-xs); color:var(--v5-text-muted); line-height:1.45 }
/* PERSONAL-ONLY (`?personal=true`) two-up layout for expenses-home.js's first
   two option pairs (هزینه‌ها + قسط و وام). `.v5-p2up`/`.v5-p2up-col` are
   stamped ONLY by renderExpensesHomeV5's personal branch, so business mode's
   flat `.v5-brow`/`.v5-bento` sequence above is untouched by this. Each
   `.v5-p2up-col` just stacks the pair's existing action row above its
   existing bento tile (same components, no new ones); the grid only puts the
   two columns side by side. The debts pair stays a sibling of `.v5-p2up`, so
   it keeps its own full-width row below, unaffected. */
:root[data-ui="v5"] .v5-p2up { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-p2up-col { display:flex; flex-direction:column; gap:var(--v5-sp-4) }
/* The /search fragment. One glass shell, hairline-separated comfy rows (§6:
   tap-target list), status on the inline-start edge instead of a dot. The
   photo is dropped INTO the 40px tile rather than beside it, so a list mixing
   photographed and un-photographed items keeps one straight title edge — the
   same thing legacy's fixed 48px track buys, without the empty box. The tile
   clips it, so the img needs no radius of its own. Corners are logical: in this
   RTL document the bar sits at the RIGHT edge and rounds towards the row. */
:root[data-ui="v5"] .v5-list { display:block; overflow:hidden }
:root[data-ui="v5"] .v5-row { display:flex; align-items:center; gap:var(--v5-sp-3); padding:var(--v5-row-comfy); position:relative; cursor:pointer; user-select:none; transition:background var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-row + .v5-row { border-block-start:1px solid var(--v5-line) }
:root[data-ui="v5"] .v5-row:hover { background:var(--v5-surface-2) }
:root[data-ui="v5"] .v5-row::before { content:""; position:absolute; inset-block:0; inset-inline-start:0; inline-size:3px; border-start-end-radius:3px; border-end-end-radius:3px; background:transparent }
:root[data-ui="v5"] .v5-row.ok::before { background:var(--v5-positive); color:var(--v5-positive); box-shadow:var(--v5-edge-glow) }
:root[data-ui="v5"] .v5-row.no::before { background:var(--v5-negative); color:var(--v5-negative); box-shadow:var(--v5-edge-glow) }
:root[data-ui="v5"] .v5-av { inline-size:40px; block-size:40px; flex:none; display:grid; place-items:center; overflow:hidden; border-radius:var(--v5-r-md); background:var(--v5-accent-tint); color:var(--v5-accent) }
:root[data-ui="v5"] .v5-av.ps { background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .v5-av.ng { background:var(--v5-negative-tint); color:var(--v5-negative) }
:root[data-ui="v5"] .v5-av img { display:block; inline-size:100%; block-size:100%; object-fit:cover }
:root[data-ui="v5"] .v5-rm { flex:1; min-inline-size:0; display:grid; gap:2px }
:root[data-ui="v5"] .v5-rt { font-size:var(--v5-t-base); font-weight:800; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
:root[data-ui="v5"] .v5-rs { font-size:var(--v5-t-xs); color:var(--v5-text-muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
:root[data-ui="v5"] .v5-amt { font-size:var(--v5-t-sm); font-weight:800; white-space:nowrap; font-variant-numeric:tabular-nums; color:var(--v5-text) }
:root[data-ui="v5"] .v5-chip { display:inline-block; padding-inline:var(--v5-sp-2); border-radius:var(--v5-r-pill); background:var(--v5-sunken); color:var(--v5-text-muted); font-size:var(--v5-t-xs); font-weight:700 }
:root[data-ui="v5"] .v5-chip.p { background:var(--v5-positive-tint); color:var(--v5-positive) }
:root[data-ui="v5"] .v5-empty { margin-block:0; padding:var(--v5-sp-5); text-align:center; color:var(--v5-text-muted); font-size:var(--v5-t-sm) }
/* Round 4 / Part 3 — the REAL warehouse («/warehouse», its «/warehouse/table»
   fragment, and «/warehouse/stale»). Prefixed per §12; the unprefixed .chips /
   .st / .eyebrow / .empty above are the /_design preview's and stay untouched.
   Page and filter shell. The form is a plain grid: the وضعیت chips, the two
   remaining selects side by side, then the search box. `.v5-srch-box` /
   `.v5-srch-input` are REUSED from Part 2 rather than duplicated — the box owns
   the surface and the focus ring, and the input is transparent inside it. */
:root[data-ui="v5"] .v5-wh { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-wh-filters { display:grid; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-wh-sel { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-wh-sel select { inline-size:100%; font-size:var(--v5-t-sm) }
/* The وضعیت filter. Real radios, visually hidden inside their labels — the same
   construction `.v5-seg` uses, so the browser keeps single-select, arrow keys
   and focus, and a `change` still bubbles to the form's own htmx trigger.
   `input:checked + span` is the ONLY active hook: it tracks the live radio, so
   it stays correct after the browser moves the selection. A server-rendered
   `aria-pressed` cannot — it would still read true on «همه» once another chip
   was picked, lighting two — so the label carries no such attribute. */
:root[data-ui="v5"] .v5-chips { display:flex; flex-wrap:wrap; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-chips-item { margin:0 }
:root[data-ui="v5"] .v5-chips-item input { position:absolute; opacity:0; inline-size:0; block-size:0; padding:0; border:0; pointer-events:none }
:root[data-ui="v5"] .v5-chips-item span { display:block; padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-3); border-radius:var(--v5-r-pill); border:1px solid var(--v5-line); background:var(--v5-sunken); color:var(--v5-text-muted); font-size:var(--v5-t-sm); font-weight:700; white-space:nowrap; cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-chips-item input:checked + span { background:var(--v5-grad-accent); border-color:transparent; color:var(--v5-on-accent); box-shadow:var(--v5-glow) }
:root[data-ui="v5"] .v5-chips-item input:focus-visible + span { outline:2px solid var(--v5-accent-line); outline-offset:2px }
/* «گزارش انبار». `.v5-sum` is deliberately NOT a `.g`: the stat grid, the
   capital list and the stale link each carry their own glass, and §5 forbids
   `.g` inside `.g`. The disclosure marker is left to the browser — it is the
   affordance that says the block collapses. */
:root[data-ui="v5"] .v5-sum { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-sum-t { font-size:var(--v5-t-lg); font-weight:800; cursor:pointer }
:root[data-ui="v5"] .v5-sub { display:grid; gap:var(--v5-sp-2) }
/* «سرمایه خوابیده» only. It starts one small token below the summary content
   above it (on top of `.v5-sum`'s own gap) and binds its eyebrow tight to the
   category list, so the label reads as that card's heading rather than as a
   free-floating line. Scoped to this block — every other `.v5-sub` keeps the
   shared rhythm above. */
:root[data-ui="v5"] .v5-capital-block { margin-block-start:var(--v5-sp-2); gap:var(--v5-sp-1) }
/* «کالاهای راکد» only. It is the last child of `.v5-sum`, so the parent's gap
   gives it its space above but nothing separates it from the product list that
   follows the disclosure — the two cards read as one stack. This end margin
   restores the same rhythm below. No start margin: the gap above is already
   there and a second one would double it. Scoped to this block — every other
   `.v5-sub` keeps the shared rhythm. */
:root[data-ui="v5"] .v5-stale-block { margin-block-end:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-eyebrow { font-size:var(--v5-t-xs); font-weight:700; color:var(--v5-text-muted) }
:root[data-ui="v5"] .v5-note { margin-block:0; font-size:var(--v5-t-xs); line-height:1.6; color:var(--v5-text-muted) }
/* The stat grid. Two cells on the warehouse (value + count), one on the stale
   page — `1fr` columns collapse to a single full-width cell on their own, so
   the one-stat case needs no variant. */
:root[data-ui="v5"] .v5-st { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)) }
:root[data-ui="v5"] .v5-st > div { display:grid; gap:var(--v5-sp-1); align-content:start; padding:var(--v5-row-comfy); min-inline-size:0 }
:root[data-ui="v5"] .v5-st > div + div { border-inline-start:1px solid var(--v5-line) }
:root[data-ui="v5"] .v5-st b { font-size:var(--v5-t-xl); font-weight:800; line-height:1.25; overflow-wrap:anywhere; font-variant-numeric:tabular-nums }
:root[data-ui="v5"] .v5-st b.p { color:var(--v5-positive) }
:root[data-ui="v5"] .v5-st b.n { color:var(--v5-negative) }
:root[data-ui="v5"] .v5-st b.a { color:var(--v5-accent) }
/* The status word in a warehouse row's meta line. It is the only part of that
   subtitle that carries state, so it gets its own span and the row's own
   positive/negative tokens — the same pair `.v5-row.ok/.no` paints the edge
   with — while the category text after the `·` keeps `.v5-rs`'s muted grey.
   Scoped to this class alone: the generic `.v5-rs` colour is untouched, and
   the extra class beats it on specificity regardless of source order. */
:root[data-ui="v5"] .v5-rs .v5-wh-status { font-weight:800 }
:root[data-ui="v5"] .v5-rs .v5-wh-status.ps { color:var(--v5-positive) }
:root[data-ui="v5"] .v5-rs .v5-wh-status.ng { color:var(--v5-negative) }
/* The money column of a row: a headline amount, an optional quieter dollar
   line, and (on a sold row) the signed profit. */
:root[data-ui="v5"] .v5-amtc { flex:none; display:grid; gap:2px; justify-items:end; text-align:end }
:root[data-ui="v5"] .v5-amt.p { color:var(--v5-positive) }
:root[data-ui="v5"] .v5-amt.n { color:var(--v5-negative) }
:root[data-ui="v5"] .v5-amt-sub { font-size:var(--v5-t-xs); font-weight:700; white-space:nowrap; color:var(--v5-text-muted); font-variant-numeric:tabular-nums }
/* `.cmp` — the compact row (§6's `--v5-row-compact`, against `.v5-row`'s comfy
   default). The warehouse is the app's densest list and its legacy sold row is
   ~265px tall, because a table cell has to stack four labelled lines inside one
   narrow column; the same figures in a compact row cost a third of that.
   Title and subtitles WRAP here rather than ellipsing: §6 requires long Persian
   strings to wrap, and a truncated product name is the one thing this list
   cannot afford. No `height`/`min-height` — the row stays content-driven. */
:root[data-ui="v5"] .v5-row.cmp { align-items:flex-start; gap:var(--v5-sp-2); padding:var(--v5-row-compact) }
:root[data-ui="v5"] .v5-row.cmp .v5-rt { font-size:var(--v5-t-md); line-height:1.45; white-space:normal; overflow:visible; text-overflow:clip; overflow-wrap:anywhere }
:root[data-ui="v5"] .v5-row.cmp .v5-rs { line-height:1.5; white-space:normal; overflow:visible; text-overflow:clip; overflow-wrap:anywhere }
/* `.lnk` — a row that is a real `<a>` (the «کالاهای راکد» card). Inside
   `.v5-scope` the base `a` rules would paint and underline it. */
:root[data-ui="v5"] .v5-row.lnk { color:var(--v5-text); text-decoration:none }
:root[data-ui="v5"] .v5-row.lnk:hover { color:var(--v5-text); text-decoration:none }
:root[data-ui="v5"] .v5-row.lnk > .ic { align-self:center; color:var(--v5-text-faint) }
/* Neutral tile — stale rows and the capital/stale cards are neither موجود nor
   فروخته, and legacy paints them `dot-gray`. Mirrors `.b-tile.nt`. */
:root[data-ui="v5"] .v5-av.nt { background:var(--v5-sunken); color:var(--v5-text-muted) }
/* Round 4 / Parts 4–5 — the REAL report surfaces («/reports-page» with its
   «/reports/section» and «/reports/period» fragments, and «/expenses/reports»
   with its own two) plus the هزینه‌ها landing page. Prefixed per §12; the
   unprefixed .step / .chart / .bars / .bar / .b-hero / .eyebrow / .empty above
   are the /_design preview's and are deliberately left untouched. The landing
   page needs no rule of its own — it reuses Part 1's .v5-bento / .v5-b-tile /
   .v5-b-sub and Part 2's .v5-brow / .v5-buy verbatim, and the two breakdown
   lists reuse Part 2's .v5-list / .v5-row and Part 3's .v5-sub / .v5-eyebrow /
   .v5-st / .v5-amtc / .v5-note.
   The report stack. One class for three nesting levels: the page section, the
   auto-refresh target inside it, and the period wrap inside that. The two inner
   levels are addressed BY ID because both ids are htmx swap targets that have
   to survive their own swap — the same technique `.v5-srch #search-results`
   uses. Sales and expense reports share every one of these rules. */
:root[data-ui="v5"] .v5-rep { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-rep > h3 { margin-block-end:0 }
:root[data-ui="v5"] .v5-rep #reports-content,
:root[data-ui="v5"] .v5-rep #expense-reports-content { display:grid; gap:var(--v5-sp-3) }
/* The period tabs. Real <button>s carrying hx-get, so `aria-pressed` is both
   the documented active hook and a correct ARIA state here — unlike the
   warehouse chips, which are radios (§10 records that caveat). The paint
   matches `.v5-chips-item span` exactly so a filter reads the same everywhere;
   the declarations are restated rather than shared because `@layer base` gives
   a bare <button> inside `.v5-scope` its own fill, border, radius and padding,
   and those have to be taken back off here. */
:root[data-ui="v5"] .v5-tabs { display:flex; flex-wrap:wrap; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-tab { padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-3); border-radius:var(--v5-r-pill); border:1px solid var(--v5-line); background:var(--v5-sunken); color:var(--v5-text-muted); font-size:var(--v5-t-sm); font-weight:700; white-space:nowrap; cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-tab[aria-pressed="true"] { background:var(--v5-grad-accent); border-color:transparent; color:var(--v5-on-accent); box-shadow:var(--v5-glow) }
/* The period stepper: previous arrow, heading, next arrow. On the expense
   reports page it carries the two arrows that page already has; on the sales
   reports page it carries the heading ALONE, because that page renders no
   arrows today (reports.js's periodNavHtml is dead code) and a repaint must not
   add navigation. The heading takes `flex:1` and centres its own text, so it
   sits centred between the two buttons when they exist and centred in the row
   when they do not — one rule, both pages. */
:root[data-ui="v5"] .v5-step { display:flex; align-items:center; justify-content:space-between; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-step > b { flex:1; min-inline-size:0; text-align:center; font-size:var(--v5-t-lg); font-weight:800; line-height:1.35; letter-spacing:0; overflow-wrap:anywhere }
:root[data-ui="v5"] .v5-step-btn { inline-size:40px; block-size:40px; flex:none; display:grid; place-items:center; padding:0; border-radius:var(--v5-r-md); border:1px solid var(--v5-line); background:var(--v5-sunken); color:var(--v5-text-muted); cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-step-btn:hover { background:var(--v5-surface-2); border-color:var(--v5-accent-line); color:var(--v5-accent) }
/* The hero — one existing headline figure, its label, and (on the expense page)
   the sentence that explains it. The gold wash is the preview's `.b-hero::after`
   verbatim. `letter-spacing` is pinned to 0 deliberately: the preview's `.num`
   tracks −.04em, but this figure ends in «تومان» and Persian is a joining
   script (§7). No `.v5-bento` parent is required — a real page heros one number,
   it does not tile nine. */
:root[data-ui="v5"] .v5-b-hero { position:relative; overflow:hidden; display:grid; gap:var(--v5-sp-1); padding:var(--v5-sp-5) var(--v5-sp-4) }
:root[data-ui="v5"] .v5-b-hero::after { content:""; position:absolute; inset:0; background:radial-gradient(circle at 80% 20%,var(--v5-gold-tint),transparent 55%); pointer-events:none }
:root[data-ui="v5"] .v5-b-hero > * { position:relative; z-index:1 }
:root[data-ui="v5"] .v5-b-hero small { font-weight:700 }
:root[data-ui="v5"] .v5-b-hero b { font-size:var(--v5-t-3xl); font-weight:800; line-height:1.25; letter-spacing:0; overflow-wrap:anywhere; font-variant-numeric:tabular-nums }
:root[data-ui="v5"] .v5-b-hero b.p { color:var(--v5-positive) }
:root[data-ui="v5"] .v5-b-hero b.n { color:var(--v5-negative) }
:root[data-ui="v5"] .v5-b-hero > span { font-size:var(--v5-t-xs); line-height:1.6; color:var(--v5-text-muted) }
/* A `.v5-st` cell that is itself a drill-down (خریدها / فروش‌ها / مجموع
   هزینه‌ها). `.lnk` is the same modifier `.v5-row` takes, in its second home: a
   surface that opens something. No hover fill — `.v5-st` carries no
   `overflow:hidden` (nothing overflows a stat cell, so it never needed one) and
   a painted corner cell would square off the card's radius. The «مشاهده» chip
   is the affordance, exactly as legacy's `.report-card-more` was; it is pinned
   to the inline-start edge so it hugs the label instead of stretching the full
   width of its grid cell. */
:root[data-ui="v5"] .v5-st > .lnk { cursor:pointer }
:root[data-ui="v5"] .v5-st .v5-chip { justify-self:start }
/* Tone on a block label — «۵ معامله برتر» positive, «۵ معامله ضعیف» negative,
   the same two colours legacy's `.deals-best` / `.deals-worst` carry. Reuses
   the documented `.p` / `.n` modifiers rather than inventing a pair. */
:root[data-ui="v5"] .v5-eyebrow.p { color:var(--v5-positive) }
:root[data-ui="v5"] .v5-eyebrow.n { color:var(--v5-negative) }
/* The 6-month chart. Two bars per month — فروش positive, سود gold, the same
   pairing legacy paints with `--green` and `--chart-profit-*`, which the alias
   table already points at these two tokens. Bar heights arrive as inline
   `height:N%` percentages the view computed; that is data, not styling, and it
   is what the preview's `.bar i` does too. 132px is the documented chart
   dimension and the 10px swatch a component dimension (§12), neither a
   spacing-scale value; no row here has a fixed height (§6). */
:root[data-ui="v5"] .v5-chart { display:grid; gap:var(--v5-sp-3); padding:var(--v5-sp-4) }
:root[data-ui="v5"] .v5-leg { display:flex; flex-wrap:wrap; gap:var(--v5-sp-4); font-size:var(--v5-t-xs); color:var(--v5-text-muted) }
:root[data-ui="v5"] .v5-leg span { display:inline-flex; align-items:center; gap:var(--v5-sp-1) }
:root[data-ui="v5"] .v5-leg i { inline-size:10px; block-size:10px; flex:none; border-radius:var(--v5-r-pill); background:var(--v5-positive) }
:root[data-ui="v5"] .v5-leg i.gd { background:var(--v5-gold) }
:root[data-ui="v5"] .v5-bars { display:flex; align-items:flex-end; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-bar { flex:1; min-inline-size:0; display:grid; gap:var(--v5-sp-1) }
:root[data-ui="v5"] .v5-bar > span { display:flex; align-items:flex-end; justify-content:center; gap:var(--v5-sp-1); inline-size:100%; block-size:132px }
:root[data-ui="v5"] .v5-bar i { display:block; flex:1; min-inline-size:0; border-start-start-radius:var(--v5-r-sm); border-start-end-radius:var(--v5-r-sm); background:var(--v5-positive) }
:root[data-ui="v5"] .v5-bar i.gd { background:var(--v5-gold) }
:root[data-ui="v5"] .v5-bar small { text-align:center; overflow-wrap:anywhere }
/* Round 5 — the REAL طلب و بدهی section: «/debts» with its «/debts/table»
   fragment, «/debts/archived», «/debts/:id» with its «/debts/:id/body»
   fragment, and «/debts/:cpId/accounts/:kind/:recordId». Prefixed per §12; the
   unprefixed .gauge-ring / .gauge-track / .gauge-neg / .gauge-pos / .gleg /
   .row / .amt / .av / .empty above are the /_design preview's and stay
   untouched. Almost everything here is reuse: the person list, the معوق rows,
   the account rows and the archive card are Part 2's .v5-list / .v5-row (with
   Part 3's .cmp and .lnk), .v5-av, .v5-amtc, .v5-chip and .v5-empty; the
   filter reuses Part 2's .v5-seg and .v5-srch-box / .v5-srch-input verbatim;
   the person page's headline is Part 4's .v5-b-hero and its طلب/بدهی split
   Part 3's .v5-st; the معوق blocks are Part 3's .v5-sub / .v5-eyebrow /
   .v5-note. Only the ring, the tool buttons and four small structural classes
   are new.
   The page stack. One class for the section on all four pages, and the two
   htmx swap targets inside it are addressed BY ID rather than given classes of
   their own — the same technique .v5-rep #reports-content uses, and for the
   same reason: both ids have to survive their own swap. The h2 inside carries
   .v5-eyebrow for its paint, so only @layer base's heading margin is left to
   take back off. */
:root[data-ui="v5"] .v5-dbt { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-dbt #debts-body,
:root[data-ui="v5"] .v5-dbt #person-body { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-dbt h2 { margin-block:0 }
/* A block label and that block's own total on one line — the معوق sections'
   header. .v5-eyebrow supplies the label's paint; this only lays the pair out. */
:root[data-ui="v5"] .v5-shead { display:flex; align-items:baseline; justify-content:space-between; gap:var(--v5-sp-2) }
/* «کل طلب» / «کل بدهی» as a ratio ring. The two arc lengths arrive as
   stroke-dasharray/stroke-dashoffset computed by the view from the two totals
   it already has — geometry, exactly like the chart's inline bar percentages,
   never paint: every stroke here is a token. 104px is the documented gauge
   dimension (§12), and the -90deg rotation starts the first arc at the top.
   The legend figure is allowed to wrap: a Toman total is long, Persian must
   not be truncated (§6), and .v5-amt's own nowrap would push it off a 375px
   screen. */
:root[data-ui="v5"] .v5-gauge-card { display:flex; align-items:center; gap:var(--v5-sp-4); padding:var(--v5-sp-4) }
:root[data-ui="v5"] .v5-gauge { inline-size:104px; block-size:104px; flex:none; transform:rotate(-90deg) }
:root[data-ui="v5"] .v5-gauge-track { fill:none; stroke:var(--v5-line); stroke-width:11 }
:root[data-ui="v5"] .v5-gauge-pos { fill:none; stroke:var(--v5-positive); stroke-width:11 }
:root[data-ui="v5"] .v5-gauge-neg { fill:none; stroke:var(--v5-negative); stroke-width:11 }
:root[data-ui="v5"] .v5-gleg { flex:1; min-inline-size:0; display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-gleg > div { display:grid; gap:2px }
:root[data-ui="v5"] .v5-gleg small { display:flex; align-items:center; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-gleg i { inline-size:10px; block-size:10px; flex:none; border-radius:var(--v5-r-pill); background:var(--v5-positive) }
:root[data-ui="v5"] .v5-gleg i.ng { background:var(--v5-negative) }
:root[data-ui="v5"] .v5-gleg b { font-size:var(--v5-t-lg); white-space:normal; overflow-wrap:anywhere }
/* The icon actions on a row or under the person's name — ویرایش, آرشیو,
   بازگرداندن, حذف. A square 40px control (§12 component dimension), because
   @layer base gives a bare <button> inside .v5-scope a field's fill, border,
   radius and padding that have to be taken back off, the same way .v5-tab
   does. .ng is the documented negative modifier and marks the destructive
   pair; the buttons keep their legacy icon-btn / icon-btn-danger classes for
   their identity. */
:root[data-ui="v5"] .v5-tools { flex:none; display:flex; align-items:center; gap:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-tool { inline-size:40px; block-size:40px; flex:none; min-inline-size:0; display:grid; place-items:center; padding:0; border-radius:var(--v5-r-md); border:1px solid var(--v5-line); background:var(--v5-sunken); color:var(--v5-text-muted); cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-tool:hover { background:var(--v5-surface-2); border-color:var(--v5-accent-line); color:var(--v5-accent) }
:root[data-ui="v5"] .v5-tool.ng { border-color:var(--v5-negative-line); color:var(--v5-negative) }
:root[data-ui="v5"] .v5-tool.ng:hover { background:var(--v5-negative-tint); border-color:var(--v5-negative); color:var(--v5-negative) }
/* `.sta` — a row that opens nothing: the علی‌الحساب credits and the
   account-detail label/value pairs. .v5-row's pointer and hover are an
   affordance and would promise a tap that does not exist. Only those two go:
   selection stays off, because re-enabling it here re-opened the long-press
   text-selection callout on the home page's recent-transaction rows, and the
   app shell's no-selection policy owns that behaviour for every non-input
   surface. */
:root[data-ui="v5"] .v5-row.sta { cursor:auto; -webkit-user-select:none; user-select:none }
:root[data-ui="v5"] .v5-row.sta:hover { background:transparent }
/* The archived row keeps the legacy structure — a link for the person and a
   restore button beside it — because a button may not be nested inside an
   anchor. The anchor IS the row's text column, so the base `a` rules that
   would paint and underline it are taken back off, exactly as .v5-row.lnk
   does for a row that is itself a link. */
:root[data-ui="v5"] .v5-row a.v5-rm,:root[data-ui="v5"] .v5-row a.v5-rm:hover { color:var(--v5-text); text-decoration:none }
/* The person's initials in the media tile. A counterparty has no photo
   anywhere in this app, so the tile carries the name itself. */
:root[data-ui="v5"] .v5-av b { font-size:var(--v5-t-sm); font-weight:800; letter-spacing:0 }
/* The negative counterpart of .v5-chip.p, for the خرید / بدهی badges — the two
   colours legacy's .txn-badge-buy and .txn-badge-manual-debt already carry. */
:root[data-ui="v5"] .v5-chip.n { background:var(--v5-negative-tint); color:var(--v5-negative) }
/* The account-detail attachment. Legacy sizes it with an inline style, which no
   layer can outrank (§8) — the v5 branch drops that attribute and styles it
   here instead. */
:root[data-ui="v5"] .v5-photo { display:block; inline-size:100%; border-radius:var(--v5-r-lg); border:1px solid var(--v5-line) }
/* Round 6 / unit 1 — the REAL modal's sheet chrome (`modal.js`, every caller).
   Prefixed per §12; the unprefixed .sheet / .grab / .scrim above are the
   /_design preview's and are deliberately left untouched.

   These two classes are PAINT ONLY. The modal engine stays in @layer legacy,
   unedited and in force: `max-height`, the `100dvh` @supports block, the
   `body.dallal-kb-open` visualViewport cap, `overflow`, `.modal-drag-zone`'s
   `touch-action:none` (which hands vertical drags to dallalSwipeBind) and the
   480px breakpoint's own `display` switch for the handle are all still
   legacy's. Nothing here sets any of them.

   The glass comes from `.v5-ov`, already defined above and reused as-is — a
   bottom sheet is on §12's `.ov` allowlist. `.v5-sheet` adds only the corner
   radius, and it needs the width query because legacy gives the dialog TWO
   shapes and only matching both keeps the corners honest: a centred dialog
   above 480px (all four corners) and a bottom sheet at or below it (top two
   only, the rest flush with the screen edge). The query duplicates legacy's
   breakpoint value; it does not modify it. Logical corners, so the pair is the
   sheet's leading edge in this RTL document without being mirrored.

   `.v5-grab` sets no `display`. Legacy hides the handle above 480px and shows
   it below, and that switch is what decides whether the affordance exists at
   all — leaving `display` alone is what keeps this from becoming an engine
   change. It restates size, margin, radius and colour because legacy paints
   the handle too and, out-ranked or not, those declarations are still there. */
:root[data-ui="v5"] .v5-sheet { border-radius:var(--v5-r-lg) }
@media (max-width:480px) {
  :root[data-ui="v5"] .v5-sheet { border-radius:0; border-start-start-radius:var(--v5-r-xl); border-start-end-radius:var(--v5-r-xl) }
}
:root[data-ui="v5"] .v5-grab { inline-size:var(--v5-sp-6); block-size:var(--v5-sp-1); margin:var(--v5-sp-2) auto 0; border-radius:var(--v5-r-pill); background:var(--v5-line-2) }
/* Round 6 / unit 2 — the Jalali calendar fragment (`datepicker.js`, the
   `/datepicker` route, every form that owns a date field).

   These are PAINT ONLY, and they are additive: the v5 branch keeps legacy's
   `dp` / `dp-header` / `dp-week` / `dp-grid` / `dp-day` / `dp-close` classes and
   adds these beside them, so the fragment's GEOMETRY is still legacy's —
   `display:grid`, the seven `1fr` columns, the `aspect-ratio:1/1` cells, the
   flex header. That is the same division of labour `.v5-sheet` draws with the
   modal engine, and it is why nothing below sets `display` on the grid or the
   header.

   The reason this unit exists at all is `@layer base`. Five migrated forms
   carry `.v5-scope`, the picker is swapped INTO them, and base gives every
   `button` inside a scope a field's fill, border, radius and
   `--v5-sp-2`/`--v5-sp-3` padding — which lands on all thirty-one day cells and
   inflates a square grid. The fragment root now carries its own `.v5-scope` so
   the paint is identical in every host rather than depending on where it
   landed, and the rules here take that base control paint back off, the way
   `.v5-tool` does.

   `.v5-dp` is deliberately not a `.g`: this panel opens inside a modal's
   `.v5-ov` sheet as often as it opens on a page, and §5 forbids stacking
   translucency, so it takes the solid nested-card surface instead. No cell has
   a fixed block-size — the square comes from legacy's aspect ratio, so §6's
   content-driven rule still holds. */
:root[data-ui="v5"] .v5-dp { padding:var(--v5-sp-3); border:1px solid var(--v5-line); border-radius:var(--v5-r-lg); background:var(--v5-surface-solid); box-shadow:var(--v5-sh) }
:root[data-ui="v5"] .v5-dp-head { gap:var(--v5-sp-2); margin-block-end:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-dp-title { font-size:var(--v5-t-md); font-weight:800; color:var(--v5-text) }
:root[data-ui="v5"] .v5-dp-nav { inline-size:32px; block-size:32px; flex:none; display:grid; place-items:center; padding:0; border:1px solid var(--v5-line); border-radius:var(--v5-r-sm); background:var(--v5-sunken); color:var(--v5-accent); font-size:var(--v5-t-lg); line-height:1; cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-dp-nav:hover { background:var(--v5-accent-tint); border-color:var(--v5-accent-line); color:var(--v5-accent-2) }
/* The year is a real number input and stays one; only its box is narrowed, so
   the header's three parts fit beside each other at 375px. */
:root[data-ui="v5"] .v5-dp-year { inline-size:5.5rem; flex:none; text-align:center; font-size:var(--v5-t-sm); padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-dp-week { gap:var(--v5-sp-1) }
:root[data-ui="v5"] .v5-dp-week span { font-size:var(--v5-t-xs); color:var(--v5-text-muted) }
:root[data-ui="v5"] .v5-dp-grid { gap:var(--v5-sp-1) }
:root[data-ui="v5"] .v5-dp-day { padding:0; display:grid; place-items:center; border:1px solid transparent; border-radius:var(--v5-r-sm); background:var(--v5-sunken); color:var(--v5-text); font-size:var(--v5-t-sm); font-weight:600; cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-dp-day:hover { background:var(--v5-accent-tint); border-color:var(--v5-accent-line); color:var(--v5-accent) }
/* `.sel` is the day the field currently holds. It has to out-paint the hover
   state above, hence the accent fill rather than a tint. */
:root[data-ui="v5"] .v5-dp-day.sel { background:var(--v5-grad-accent); border-color:var(--v5-accent); color:var(--v5-on-accent); font-weight:800 }
:root[data-ui="v5"] .v5-dp-day.sel:hover { background:var(--v5-grad-accent); color:var(--v5-on-accent) }
:root[data-ui="v5"] .v5-dp-close { padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-2); border:1px solid var(--v5-line); border-radius:var(--v5-r-sm); background:transparent; color:var(--v5-text-muted); font-size:var(--v5-t-xs); cursor:pointer; transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease) }
:root[data-ui="v5"] .v5-dp-close:hover { background:var(--v5-surface-2); color:var(--v5-text) }
/* «حالت شب» switch — the thumb's containing block.

   The control's markup is legacy's, untouched: `.switch` > hidden checkbox +
   `.switch-track` > `.switch-thumb`, and style.css still paints all of it. The
   thumb rests against `.switch` there because `.menu-theme` is `display:flex`,
   which blockifies the `.switch` span and lets its 40×24 apply.

   Under v5 the row is inside a `.v5-scope`, and `@layer base`'s
   `.v5-scope label` rule out-ranks `@layer legacy` by layer order — so
   `.menu-theme` is `inline-block`, `.switch` is back to an inline span, and an
   inline box takes no width or height. The thumb then has a zero-sized
   reference box and lands outside the track.

   The fix keeps every legacy declaration and moves the reference box down one
   level: `.switch-track` is already `display:block` 40×24, so making it
   `relative` gives the thumb the same 40×24 rectangle it resolves against in
   legacy, whatever the label's display ends up being. Its size is restated for
   the same reason `.v5-grab` restates the handle's — this rule now depends on
   that box being 40×24, so it says so. The three offsets are legacy's values
   verbatim, re-declared because they are now measured from the track; the
   checked selector is legacy's too, which already descends through the track.
   No generic element rule, nothing outside `:root[data-ui="v5"]`, no change to
   the DOM or to style.css. */
:root[data-ui="v5"] .switch-track { position:relative; inline-size:40px; block-size:24px }
:root[data-ui="v5"] .switch-thumb { position:absolute; top:3px; inset-inline-start:3px; inline-size:18px; block-size:18px }
:root[data-ui="v5"] .switch input:checked ~ .switch-track .switch-thumb { inset-inline-start:19px }
:root[data-ui="v5"] .v5-rep .custom-range-fields .field { flex-basis:100%; min-inline-size:0 }
:root[data-ui="v5"] .v5-expense-recent { display:flex; flex-wrap:wrap; gap:var(--v5-sp-2); margin-top:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-expense-recent-chip { display:inline-flex; align-items:center; padding:4px 8px; border:1px solid var(--v5-line); border-radius:var(--v5-r-pill); background:var(--v5-sunken); color:var(--v5-text-muted); font-size:var(--v5-t-xs); font-weight:700; cursor:pointer }
:root[data-ui="v5"] .v5-expense-recent-chip:hover,:root[data-ui="v5"] .v5-expense-recent-chip:active { background:var(--v5-positive-tint); color:var(--v5-positive) }
}

/* ── The v5 home («/») ────────────────────────────────────────────────────────
   A handful of rules and a modifier, all prefixed and all under
   :root[data-ui="v5"], so with the kill switch off not one of them applies. Everything the screen can
   reuse, it reuses: the tiles are `.v5-bento` + `.v5-b-tile`, the section header
   is `.v5-shead` + `.v5-eyebrow`, the transaction list is `.v5-list` > `.v5-row
   cmp sta` with `.v5-av nt` / `.v5-amtc` / `.v5-amt` / `.v5-amt-sub` /
   `.v5-empty`, the card surfaces are `.g` and the freshness line is `.v5-note`.
   The two `.v5-home-*` classes below are this screen's own — a size step and a
   pill on shared elements — and both are nested under `.v5-home`, so neither
   can reach a `.v5-shead` on any other page. No generic/legacy
   selector is touched and no custom property is invented — the two literals
   below (56px, and the 46px the tile's icon already uses) are component
   dimensions in §12's sense, exactly like `--v5-nav-h`'s 64px.
   ──────────────────────────────────────────────────────────────────────────── */
@layer components {

/* Clearance. `.app` is already `display:flex; flex-direction:column; gap:1rem`,
   so the sections stack on their own — this only replaces `.app`'s 3rem bottom
   padding with a step of the same order (56px + a step). The «+» is IN FLOW as
   the column's last item, so nothing has to be reserved UNDER it any more; what
   is left is the breathing room at the end of the scroll, and the wrapper
   `<body class="v5-has-nav">` still holds the bar's own strip (nav-h + a step +
   the safe area) below that, so the button can never end up behind the bar. */
:root[data-ui="v5"] .v5-home { padding-block-end:calc(56px + var(--v5-sp-4)) }

/* Six tiles in two rows of three. `.v5-bento` supplies the grid; the only thing
   six tiles need that nine did not is equal row heights — «طلب و بدهی» wraps to
   two lines and «انبار» does not, and without this the second row's tiles are
   visibly shorter than the first's. `1fr` on auto-rows makes every row as tall
   as the tallest tile in the grid, so all six read as one block. */
:root[data-ui="v5"] .v5-bento-6 { grid-auto-rows:1fr }

/* The top card — today's date and «نرخ دلار امروز» on one `.g` surface. The
   card's own grid stacks two areas: a compact date strip, then the rate area
   whose interior is the label and, under it, ONE row carrying the figure and
   the freshness line side by side. The strip is separated by a hairline rather
   than extra space, so combining the two costs the tiles below it almost no
   vertical room.
   The card is deliberately SHORT: it sits above the six tiles, and every pixel
   it spends pushes them toward the fold. Its height comes down by a step at
   each level rather than by shrinking any one thing — the card's own padding
   16px → 12px, its grid gap 8px → 4px, and the strip's bottom padding 8px →
   4px, all still on the --v5-sp scale. NOTHING here touches `.v5-bento`,
   `.v5-b-tile` or the icons inside them: the tiles keep the size they had.
   The figure is tabular so a rate that ticks up doesn't shift its own digits. */
:root[data-ui="v5"] .v5-rate { display:grid; gap:var(--v5-sp-1); padding:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-rate-date { padding-block-end:var(--v5-sp-1); border-block-end:1px solid var(--v5-line) }
/* One step up for the strip's text only. `.v5-eyebrow` is an 11px label class,
   which is right where it sits above a figure, but here the strip is not a
   label — it is the date, the Gregorian date and the clock, three readings a
   user actually reads. --v5-t-sm is the next token up (12px), so this borrows
   the scale rather than inventing a size, and stays under `.v5-rate-date` so
   every other eyebrow on the screen is untouched. */
:root[data-ui="v5"] .v5-rate-date .v5-eyebrow { font-size:var(--v5-t-sm) }
/* «امروز:» — the strip's one label, in the negative red and a weight above the
   700 `.v5-eyebrow` already carries, so the word separates from the date it
   introduces. Colour only comes from the theme token, so it follows dark mode
   like every other red on the screen. */
:root[data-ui="v5"] .v5-rate-date .v5-today { color:var(--v5-negative); font-weight:800 }
/* The clock is preceded by the words «به وقت تهران», and with no space of its
   own the two run together as one string. --v5-sp-3 (12px) against the strip's
   12px text is about three Persian letters — one step up from sp-2, which read
   as tight enough that the digits still looked attached to the label. The clock
   moves that step FARTHER from «به وقت تهران», i.e. physically leftward, since
   inline-start is the side FACING the label: this column is RTL, so the label
   sits to the clock's right. Nothing else on the strip moves. */
:root[data-ui="v5"] #v5-home-time { display:inline-block; margin-inline-start:var(--v5-sp-3); inline-size:6ch; font-variant-numeric:tabular-nums; transform:translateX(-32px) }
:root[data-ui="v5"] .v5-rate-main { display:grid; gap:var(--v5-sp-1) }
/* Figure and freshness on ONE line instead of two stacked ones — the single
   biggest saving in the card. `space-between` pins each to its own end of the
   row: in this RTL column the figure lands at the inline-start (physically the
   RIGHT, where the reading begins) and the freshness sentence opposite it at
   the inline-end (physically the LEFT). `baseline` sits the small print on the
   figure's own baseline rather than centring it against a 26px number, and the
   wrap is the honest fallback — on a narrow phone the sentence drops under the
   figure instead of squeezing the digits. `.v5-note` is already margin-free, so
   a wrap costs the row nothing but its own line. */
:root[data-ui="v5"] .v5-rate-row {
  display:flex; flex-wrap:wrap; align-items:baseline;
  justify-content:space-between; gap:var(--v5-sp-1) var(--v5-sp-3);
}
:root[data-ui="v5"] .v5-rate-val { font-size:var(--v5-t-2xl); font-weight:800; letter-spacing:-.02em; font-variant-numeric:tabular-nums }
/* `.na` — the no-rate-stored state. It is a sentence, not a figure, so it drops
   to body size and the muted colour rather than shouting at 26px. It is NOT in
   the row above — nothing sits beside it — so it is unaffected by any of this. */
:root[data-ui="v5"] .v5-rate-val.na { font-size:var(--v5-t-md); font-weight:700; letter-spacing:0; color:var(--v5-text-muted) }

/* «تراکنش‌های اخیر» — one token up, and only here. `.v5-eyebrow` is the 11px
   label class, which is right for the word that introduces a figure, but this
   one introduces the screen's longest surface: six rows the user reads top to
   bottom, and a heading the same size as the dates inside them does not start
   that block. --v5-t-sm (12px) is the next token on the scale — the same step
   `.v5-rate-date .v5-eyebrow` takes above, borrowing the scale rather than
   inventing a size. Its own class, so every other eyebrow on the screen keeps
   the size it had. */
:root[data-ui="v5"] .v5-home .v5-home-stitle { font-size:var(--v5-t-sm) }

/* «همه» as a pill. It is the card's one control and it sits at the far end of
   a `.v5-shead` row opposite the title, where a bare two-letter accent link
   reads as a stray word rather than a thing to press. The border, the sunken
   fill and `--v5-r-pill` give it an edge to aim at without making it a second
   primary action — the «+» is the only one of those on this screen, and this
   stays label-sized and label-weighted next to it. `flex:none` keeps the pill
   off the title's wrapping (`.v5-shead` is a flex row), and the hover pair
   takes back the underline `@layer base`'s `.v5-scope a:hover` would add; this
   layer out-ranks that one, so the tint is what a press previews instead. */
:root[data-ui="v5"] .v5-home .v5-home-all {
  flex:none;
  display:inline-flex; align-items:center;
  padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-3);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-pill);
  background:var(--v5-sunken);
  color:var(--v5-accent);
  font-size:var(--v5-t-xs); font-weight:700;
  text-decoration:none;
  transition:background var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease);
}
:root[data-ui="v5"] .v5-home .v5-home-all:hover {
  background:var(--v5-accent-tint); color:var(--v5-accent); text-decoration:none;
}
:root[data-ui="v5"] .v5-home .v5-home-all:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }

/* A recent-transaction row is a `<button type="button">` — it opens that
   expense's existing edit modal — and this rule is the ONLY thing that keeps it
   from looking like one. Nothing here is new paint: every declaration takes
   back something a <button> is given that the `<div>` it replaced never had.

   `@layer base` gives every bare <button> inside `.v5-scope` a form field's
   dress (sunken fill, hairline border, `--v5-r-md` corners, `--v5-t-base` at
   weight 600) and the UA adds centred text, a shrink-to-fit box and its own
   native appearance on top. Six of those in a `.v5-list` would be six framed
   boxes where six hairline-separated rows used to be. So: the box goes full
   width with no min-width floor, the type and the colour and the alignment go
   back to inherited, and the border, the radius, the fill and the native
   appearance go away. This layer out-ranks base, so no !important is involved.

   What it must NOT do is restyle the row. The geometry is still `.v5-row` +
   `.cmp`'s (flex, gap, `--v5-row-compact` padding — base's button padding is
   out-ranked by those, by layer, and is not restated here), the hairline
   between rows is still `.v5-row + .v5-row`'s, and the cursor and the hover
   are still `.sta`'s. Hence the selector is `:root[data-ui="v5"]
   .home-expense-row` and NOT `… .v5-home .home-expense-row`: the divider rule
   is `:root[data-ui="v5"] .v5-row + .v5-row`, which is four class-level
   selectors, and adding `.v5-home` here would make this rule four as well —
   equal specificity, same layer, and this one is further down the file, so its
   `border:0` would win and take the divider off every row but the first. Three
   keeps `border-block-start` where it belongs and drops the other three sides,
   which is exactly the split this needs. The class name is the scope: it is
   this list's alone, under the v5 kill switch like everything else here.

   `outline` is deliberately untouched — the browser's own focus ring is what a
   keyboard user gets, and the row is activated with Enter and Space by the
   element itself, not by anything declared here. */
:root[data-ui="v5"] .home-expense-row {
  inline-size:100%;
  min-inline-size:0;
  font:inherit;
  color:inherit;
  text-align:inherit;
  border:0;
  border-radius:0;
  background:none;
  -webkit-appearance:none;
  appearance:none;
}

/* …and this pair puts back the one thing the row now needs and `.sta` takes
   away. `.sta` marks a row that opens nothing — it drops `.v5-row`'s pointer
   and its hover so the surface stops promising a tap that does not exist — and
   it is still the right class here, because it is also what holds
   `-webkit-user-select:none` over these rows and the long-press selection
   callout it fixes is this list's. But the promise is true again: every one of
   these rows opens its own expense. So the cursor and the hover come back, and
   the hover is `.v5-row:hover`'s own --v5-surface-2, not a new colour.
   THIS one is scoped `.v5-home .home-expense-row` on purpose, the opposite of
   the reset above. It has to out-rank `.v5-row.sta`, which is four class-level
   selectors, and four here beats it on source order alone — the reset's problem
   was `border:0` tying with `.v5-row + .v5-row` at that same four, and neither
   declaration below is a border, so nothing here can reach the divider. */
:root[data-ui="v5"] .v5-home .home-expense-row { cursor:pointer }
:root[data-ui="v5"] .v5-home .home-expense-row:hover { background:var(--v5-surface-2) }

/* The «+». A circle that sits IN FLOW, as the last item of the column, under
   the «تراکنش‌های اخیر» card it follows in the markup — so it scrolls with the
   page instead of hovering over it, and nothing it might cover has to be
   guessed at. It floated before, pinned above the nav bar with a z-index of 11;
   none of that is left, and with no fixed box there is nothing to stack.
   `.app` is a `flex-direction:column` container, so the button's cross axis IS
   the inline axis: `margin-inline-start:auto` eats the free space on the side
   the reading starts from — physically the RIGHT in this RTL column — and lands
   the circle at the far corner, physically LEFT, exactly where it used to hang.
   The column's own `gap` is 16px; --v5-sp-2 on top of it makes 24px, one clear
   step more air than the cards give each other, so the screen's one primary
   action reads as its own thing rather than as part of the list above it.
   The size, the paint and the press states are untouched. The declarations
   restate what `@layer base`'s bare-<button> rule would otherwise give it
   inside `.v5-scope` (sunken fill, hairline border, r-md corners): this layer
   out-ranks that one, and an action button of this weight owes none of it. */
:root[data-ui="v5"] .v5-fab {
  margin-block-start:var(--v5-sp-2);
  margin-inline-start:auto;
  inline-size:56px; block-size:56px; padding:0;
  display:grid; place-items:center;
  border:1px solid var(--v5-accent);
  border-radius:var(--v5-r-pill);
  background:var(--v5-grad-accent);
  color:var(--v5-on-accent);
  box-shadow:var(--v5-glow),var(--v5-sh-float);
  cursor:pointer;
  transition:box-shadow var(--v5-dur) var(--v5-ease),transform var(--v5-dur) var(--v5-ease);
}
:root[data-ui="v5"] .v5-fab .ic { inline-size:26px; block-size:26px }
:root[data-ui="v5"] .v5-fab:hover { background:var(--v5-accent-2) }
:root[data-ui="v5"] .v5-fab:active { transform:scale(.96) }
:root[data-ui="v5"] .v5-fab:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }
:root[data-ui="v5"] .v5-home-biz,:root[data-ui="v5"] .v5-home-personal { display:grid; gap:var(--v5-sp-3); padding:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-home-biz-link,:root[data-ui="v5"] .v5-home-personal-link { display:flex; align-items:center; gap:var(--v5-sp-2); color:var(--v5-text); font-size:var(--v5-t-lg); font-weight:800; text-decoration:none }
:root[data-ui="v5"] .v5-home-biz-link:hover,:root[data-ui="v5"] .v5-home-personal-link:hover { color:var(--v5-accent); text-decoration:none }
:root[data-ui="v5"] .v5-home-biz-link .ic,:root[data-ui="v5"] .v5-home-personal-link .ic { inline-size:24px; block-size:24px; color:var(--v5-accent) }
:root[data-ui="v5"] .v5-home-personal-add { justify-self:start; inline-size:56px; block-size:56px; padding:0; display:grid; place-items:center; border:1px solid var(--v5-accent); border-radius:var(--v5-r-pill); background:var(--v5-grad-accent); color:var(--v5-on-accent); box-shadow:var(--v5-glow),var(--v5-sh-float); cursor:pointer }
:root[data-ui="v5"] .v5-home-personal-add .ic { inline-size:26px; block-size:26px }
:root[data-ui="v5"] .v5-home-personal-add:active { transform:scale(.96) }
:root[data-ui="v5"] .v5-home-personal-add:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }

}

/* ════════════════════════════════════════════════════════════════════════════
   AUTH SHELL (AUTH round) — /signup, /welcome/1, /welcome/2, /login, /forgot
   ────────────────────────────────────────────────────────────────────────────
   A self-contained block for the four signed-out (or not-yet-onboarded)
   screens. Every rule is gated on `:root[data-ui="v5"] .v5-auth`, so nothing
   here can reach a page that does not render the auth shell, and every class
   is `v5-auth-`-prefixed so none of them can collide with a legacy class in
   style.css (§12).

   NOT ONE NEW CUSTOM PROPERTY. The mockup's palette maps onto tokens that
   already exist — --teal → --v5-accent, --teal-deep → --v5-accent-2,
   --teal-wash → --v5-accent-tint, --ink → --v5-text, --ink-2/--ink-3 →
   --v5-text-muted/--v5-text-faint, --line → --v5-line, --alert →
   --v5-negative, --surface-2 → --v5-surface-2, and the button's text →
   --v5-on-accent. Dark mode therefore needs no rule at all: the tokens already
   switch with [data-theme] and the OS preference.

   RTL. Logical properties only — no margin-left/right, no left/right. The eye
   button is placed with `inset-inline-start`, which puts it on the physically
   LEFT edge of an RTL field, and would move with the document if it ever
   rendered LTR. Numbers use tabular-nums so a phone number's digits do not
   jitter as it is typed.
   ════════════════════════════════════════════════════════════════════════════ */
@layer components {

/* The page. min-block-size:100dvh (not vh) so the card stays centred when a
   phone's on-screen keyboard shrinks the viewport — the same dvh reasoning
   layout.js's viewport meta records. The card is capped at 420px per the
   round's spec and is a flex column so a screen can push its last element to
   the bottom with margin-block-start:auto. */
:root[data-ui="v5"] .v5-auth {
  min-block-size:100dvh;
  display:flex; align-items:center; justify-content:center;
  padding:var(--v5-sp-5) var(--v5-sp-4);
}
:root[data-ui="v5"] .v5-auth-card {
  inline-size:100%; max-inline-size:420px;
  display:flex; flex-direction:column;
  background:var(--v5-surface);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-xl);
  box-shadow:var(--v5-sh);
  padding:var(--v5-sp-6) var(--v5-sp-5);
}

/* Brand lockup. The mark keeps the source PNG's 1:1 ratio. */
:root[data-ui="v5"] .v5-auth-brand {
  display:flex; align-items:center; gap:var(--v5-sp-2);
  margin-block-end:var(--v5-sp-5);
}
:root[data-ui="v5"] .v5-auth-mark {
  inline-size:34px; block-size:34px; flex:none;
  border-radius:var(--v5-r-sm);
}
:root[data-ui="v5"] .v5-auth-name { font-size:var(--v5-t-lg); font-weight:800; letter-spacing:-.01em }

/* Type. `.v5-auth-h` is an <h1>; the size is set here rather than left to
   @layer base's --v5-t-3xl heading, which at 36px wraps «چه چیزهایی می‌فروشی؟»
   onto three lines at 375px. */
:root[data-ui="v5"] .v5-auth-h {
  font-size:var(--v5-t-xl); font-weight:800; line-height:1.45;
  margin-block:0 var(--v5-sp-2);
}
:root[data-ui="v5"] .v5-auth-sub {
  font-size:var(--v5-t-md); color:var(--v5-text-muted); line-height:1.85;
  margin-block:0;
}

/* Fields */
:root[data-ui="v5"] .v5-auth-fields {
  margin-block-start:var(--v5-sp-6);
  display:flex; flex-direction:column; gap:var(--v5-sp-4);
}
:root[data-ui="v5"] .v5-auth-field label {
  display:block; font-size:var(--v5-t-sm); color:var(--v5-text-muted);
  font-weight:600; margin-block-end:var(--v5-sp-1);
}
:root[data-ui="v5"] .v5-auth-opt { color:var(--v5-text-faint); font-weight:500 }
:root[data-ui="v5"] .v5-auth-inp {
  inline-size:100%;
  background:var(--v5-surface-2);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-md);
  color:var(--v5-text);
  padding-block:var(--v5-sp-3); padding-inline:var(--v5-sp-3);
  font-size:var(--v5-t-lg);
}
/* The phone/email fields: an LTR run of characters inside an RTL document.
   `direction` and `text-align` are both needed — direction alone leaves the
   text hugging the inline-start edge, which in RTL is the wrong side. */
:root[data-ui="v5"] .v5-auth-inp.ltr {
  direction:ltr; text-align:left;
  letter-spacing:.03em; font-variant-numeric:tabular-nums;
}
:root[data-ui="v5"] .v5-auth-inp:focus {
  border-color:var(--v5-accent);
  background:var(--v5-surface-solid);
  outline:3px solid var(--v5-accent-tint);
  outline-offset:0;
}
:root[data-ui="v5"] .v5-auth-inp[aria-invalid="true"] { border-color:var(--v5-negative) }
:root[data-ui="v5"] .v5-auth-hint {
  font-size:var(--v5-t-xs); color:var(--v5-text-faint);
  margin-block:var(--v5-sp-1) 0;
}
:root[data-ui="v5"] .v5-auth-err {
  font-size:var(--v5-t-xs); color:var(--v5-negative);
  margin-block:var(--v5-sp-1) 0;
}
:root[data-ui="v5"] .v5-auth-err a { color:var(--v5-negative); font-weight:800; text-decoration:underline }

/* Password field + its show/hide control. The button is placed on the field's
   inline-start edge (physically LEFT in RTL) and is a 44px touch target — the
   input's own padding-inline-start makes room for it so the two never overlap. */
:root[data-ui="v5"] .v5-auth-wrap { position:relative }
:root[data-ui="v5"] .v5-auth-wrap .v5-auth-inp { padding-inline-start:46px }
:root[data-ui="v5"] .v5-auth-eye {
  position:absolute; inset-inline-start:var(--v5-sp-1); inset-block-start:50%;
  transform:translateY(-50%);
  inline-size:40px; block-size:40px; padding:0;
  display:grid; place-items:center;
  background:transparent; border:0; border-radius:var(--v5-r-sm);
  color:var(--v5-text-muted); cursor:pointer;
}
:root[data-ui="v5"] .v5-auth-eye:hover { color:var(--v5-accent) }
:root[data-ui="v5"] .v5-auth-eye:focus-visible { outline:2px solid var(--v5-accent-line); outline-offset:2px }

/* The honeypot. Positioned off-screen rather than display:none, deliberately —
   see the comment on the field in views/auth-shell.js. It must stay in the
   layout tree (a display:none field is skipped by the naive bots this catches)
   and must never be reachable by a real user, which tabindex="-1" and
   aria-hidden in the markup handle. */
:root[data-ui="v5"] .v5-auth-hp {
  position:absolute; inset-inline-start:-9999px;
  inline-size:1px; block-size:1px; overflow:hidden;
}

/* Buttons. `.v5-auth-btn` is also used on an <a> (the Telegram link and «برگرد
   به ورود»), so it declares its own text-align and text-decoration rather than
   inheriting a button's — and takes back the underline @layer base's
   `.v5-scope a:hover` would otherwise add. */
:root[data-ui="v5"] .v5-auth-btn {
  display:block; inline-size:100%;
  margin-block-start:var(--v5-sp-5);
  padding-block:var(--v5-sp-4); padding-inline:var(--v5-sp-4);
  border:1px solid var(--v5-accent);
  border-radius:var(--v5-r-md);
  background:var(--v5-grad-accent);
  color:var(--v5-on-accent);
  box-shadow:var(--v5-glow);
  font-size:var(--v5-t-lg); font-weight:700;
  text-align:center; text-decoration:none; cursor:pointer;
}
:root[data-ui="v5"] .v5-auth-btn:hover { background:var(--v5-accent-2); color:var(--v5-on-accent); text-decoration:none }
:root[data-ui="v5"] .v5-auth-btn:active { transform:scale(.99) }
:root[data-ui="v5"] .v5-auth-btn:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }
:root[data-ui="v5"] .v5-auth-btn.gh {
  margin-block-start:var(--v5-sp-3);
  background:transparent; color:var(--v5-text-muted);
  border-color:var(--v5-line-2); box-shadow:none;
  font-weight:600;
}
:root[data-ui="v5"] .v5-auth-btn.gh:hover { background:var(--v5-surface-2); color:var(--v5-text) }

/* The «حساب داری؟ وارد شو» / «رمزم را فراموش کرده‌ام» lines under a form. */
:root[data-ui="v5"] .v5-auth-alt {
  margin-block:var(--v5-sp-4) 0; text-align:center;
  font-size:var(--v5-t-md); color:var(--v5-text-muted);
}
:root[data-ui="v5"] .v5-auth-alt + .v5-auth-alt { margin-block-start:var(--v5-sp-2) }
:root[data-ui="v5"] .v5-auth-alt a { color:var(--v5-accent); font-weight:700 }

:root[data-ui="v5"] .v5-auth-legal-links {
  margin-block:var(--v5-sp-3) 0;
  text-align:center;
  font-size:var(--v5-t-sm);
  color:var(--v5-text-faint);
  display:flex;
  align-items:center;
  justify-content:center;
  gap:var(--v5-sp-2);
  flex-wrap:wrap;
}
:root[data-ui="v5"] .v5-auth-legal-links a {
  color:var(--v5-text-muted);
  text-decoration:underline;
  text-underline-offset:2px;
}

:root[data-ui="v5"] .v5-auth-accept {
  margin-block-start:var(--v5-sp-5);
  padding:var(--v5-sp-3);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-md);
  background:var(--v5-surface-2);
}
:root[data-ui="v5"] .v5-auth-checkrow {
  display:flex;
  align-items:flex-start;
  gap:var(--v5-sp-2);
  font-size:var(--v5-t-sm);
  line-height:1.9;
  color:var(--v5-text-muted);
  cursor:pointer;
}
:root[data-ui="v5"] .v5-auth-check {
  inline-size:18px;
  block-size:18px;
  margin-block-start:3px;
  accent-color:var(--v5-accent);
  flex:none;
}
:root[data-ui="v5"] .v5-auth-checkrow a {
  color:var(--v5-accent);
  font-weight:700;
}

/* The trust footnote, pushed to the bottom of the card. */
:root[data-ui="v5"] .v5-auth-trust {
  margin-block-start:var(--v5-sp-6);
  padding-block-start:var(--v5-sp-4);
  border-block-start:1px solid var(--v5-line);
  font-size:var(--v5-t-xs); color:var(--v5-text-faint); line-height:1.9;
}
:root[data-ui="v5"] .v5-auth-trust span { display:block }

/* Notices. `.calm` is the accent-tinted variant used on /forgot; the default is
   the negative one used for a cap message or an expired form. */
:root[data-ui="v5"] .v5-auth-notice {
  display:flex; gap:var(--v5-sp-2);
  padding:var(--v5-sp-3);
  border-radius:var(--v5-r-md);
  border:1px solid var(--v5-negative-line);
  background:var(--v5-negative-tint);
  color:var(--v5-negative);
  font-size:var(--v5-t-md); line-height:1.8;
  margin-block-start:var(--v5-sp-4);
}
:root[data-ui="v5"] .v5-auth-notice.calm {
  border-color:var(--v5-accent-line);
  background:var(--v5-accent-tint);
  color:var(--v5-accent-2);
}
:root[data-ui="v5"] .v5-auth-notice .ic { margin-block-start:3px }

/* /forgot's single action group, and the handle printed as copyable LTR text
   for a user whose desktop Telegram will not open the link. */
:root[data-ui="v5"] .v5-auth-actions { margin-block-start:var(--v5-sp-5) }
:root[data-ui="v5"] .v5-auth-handle {
  margin-block:var(--v5-sp-3) 0; text-align:center;
  font-size:var(--v5-t-md); color:var(--v5-text-faint);
  font-variant-numeric:tabular-nums;
}

/* Onboarding progress row. */
:root[data-ui="v5"] .v5-auth-prog {
  display:flex; align-items:center; gap:var(--v5-sp-3);
  margin-block-end:var(--v5-sp-5);
}
:root[data-ui="v5"] .v5-auth-prog-txt {
  font-size:var(--v5-t-xs); color:var(--v5-text-faint);
  font-variant-numeric:tabular-nums; flex:none;
}
:root[data-ui="v5"] .v5-auth-prog-bar {
  flex:1; block-size:4px; border-radius:var(--v5-r-pill);
  background:var(--v5-sunken); overflow:hidden;
}
:root[data-ui="v5"] .v5-auth-prog-bar i {
  display:block; block-size:100%;
  background:var(--v5-accent); border-radius:var(--v5-r-pill);
}
:root[data-ui="v5"] .v5-auth-skipform { flex:none; margin:0 }
:root[data-ui="v5"] .v5-auth-skip {
  padding:0; background:transparent; border:0;
  border-block-end:1px solid var(--v5-line-2);
  color:var(--v5-text-muted); font-size:var(--v5-t-md); font-weight:600;
  cursor:pointer; border-radius:0;
}
:root[data-ui="v5"] .v5-auth-skip:hover { color:var(--v5-text) }
:root[data-ui="v5"] .v5-auth-skip:focus-visible { outline:2px solid var(--v5-accent-line); outline-offset:3px }

/* Multi-select chips. The control is a real checkbox whose LABEL is the chip:
   the checkbox is taken out of the paint but NOT out of the accessibility tree
   or the tab order — `position:absolute;opacity:0` keeps it focusable, which
   `display:none` and `visibility:hidden` would not. `:focus-visible + label`
   is what draws the keyboard ring, since the thing being focused is invisible. */
:root[data-ui="v5"] .v5-auth-chips {
  display:flex; flex-wrap:wrap; gap:var(--v5-sp-2);
  margin-block-start:var(--v5-sp-5);
}
:root[data-ui="v5"] .v5-auth-chipin {
  position:absolute; inline-size:1px; block-size:1px;
  padding:0; margin:0; border:0; opacity:0; pointer-events:none;
}
:root[data-ui="v5"] .v5-auth-chip {
  display:inline-block; margin:0;
  padding-block:var(--v5-sp-2); padding-inline:var(--v5-sp-4);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-pill);
  background:var(--v5-surface-2);
  color:var(--v5-text);
  font-size:var(--v5-t-md); font-weight:600;
  cursor:pointer; user-select:none;
  transition:background var(--v5-dur) var(--v5-ease),border-color var(--v5-dur) var(--v5-ease),color var(--v5-dur) var(--v5-ease);
}
:root[data-ui="v5"] .v5-auth-chipin:checked + .v5-auth-chip {
  background:var(--v5-accent-tint);
  border-color:var(--v5-accent);
  color:var(--v5-accent-2);
}
:root[data-ui="v5"] .v5-auth-chipin:focus-visible + .v5-auth-chip {
  outline:3px solid var(--v5-accent-line); outline-offset:2px;
}

/* ── The home screen's one-time password warning (AUTH round) ──────────────
   A dashed panel rather than a filled one: it is advice, not an error, and the
   filled negative treatment above is reserved for things that went wrong. It
   sits inside the existing home column and takes its width from it. */
:root[data-ui="v5"] .v5-pw-warn {
  display:flex; align-items:flex-start; gap:var(--v5-sp-3);
  padding:var(--v5-sp-3) var(--v5-sp-4);
  border:1px dashed var(--v5-gold-line);
  background:var(--v5-gold-tint);
  border-radius:var(--v5-r-md);
  font-size:var(--v5-t-sm); color:var(--v5-text-muted); line-height:1.85;
}
:root[data-ui="v5"] .v5-pw-warn .ic { color:var(--v5-gold); margin-block-start:3px; flex:none }
:root[data-ui="v5"] .v5-pw-warn b { color:var(--v5-text) }
:root[data-ui="v5"] .v5-pw-warn form { margin:0; margin-inline-start:auto }
:root[data-ui="v5"] .v5-pw-warn-x {
  padding-block:var(--v5-sp-1); padding-inline:var(--v5-sp-3);
  background:transparent; border:1px solid var(--v5-gold-line);
  border-radius:var(--v5-r-pill);
  color:var(--v5-gold); font-size:var(--v5-t-xs); font-weight:700; cursor:pointer;
}
:root[data-ui="v5"] .v5-pw-warn-x:hover { background:var(--v5-surface-2) }

/* ── The empty home screen's single action ─────────────────────────────────
   Rendered ONLY when the account has recorded nothing at all. One action, and
   it opens the purchase modal through the app's own route. */
:root[data-ui="v5"] .v5-home-empty {
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  text-align:center; gap:var(--v5-sp-3);
  padding:var(--v5-sp-6) var(--v5-sp-4);
}
:root[data-ui="v5"] .v5-home-empty .v5-av-lg {
  inline-size:62px; block-size:62px;
  display:grid; place-items:center;
  border-radius:var(--v5-r-lg);
  background:var(--v5-accent-tint); color:var(--v5-accent);
}
:root[data-ui="v5"] .v5-home-empty .v5-av-lg .ic { inline-size:28px; block-size:28px }
:root[data-ui="v5"] .v5-home-empty h3 { font-size:var(--v5-t-lg); font-weight:800; margin:0 }
:root[data-ui="v5"] .v5-home-empty p {
  font-size:var(--v5-t-md); color:var(--v5-text-muted);
  max-inline-size:28ch; margin:0;
}
:root[data-ui="v5"] .v5-home-first {
  margin-block-start:var(--v5-sp-2);
  padding-block:var(--v5-sp-3); padding-inline:var(--v5-sp-6);
  border:1px solid var(--v5-accent);
  border-radius:var(--v5-r-md);
  background:var(--v5-grad-accent);
  color:var(--v5-on-accent);
  font-size:var(--v5-t-base); font-weight:700; cursor:pointer;
  box-shadow:var(--v5-glow);
}
:root[data-ui="v5"] .v5-home-first:hover { background:var(--v5-accent-2) }
:root[data-ui="v5"] .v5-home-first:focus-visible { outline:3px solid var(--v5-accent-line); outline-offset:2px }

/* 375px — the narrowest phone the app targets. Nothing may overflow
   horizontally there; the card loses its side padding rather than its width. */
@media (max-inline-size:400px) {
  :root[data-ui="v5"] .v5-auth { padding:var(--v5-sp-4) var(--v5-sp-3) }
  :root[data-ui="v5"] .v5-auth-card { padding:var(--v5-sp-5) var(--v5-sp-4) }
}

/* ── حساب کاربری («/account») ────────────────────────────────────────────────
   v5-native from the start — the page has no legacy predecessor, so there is
   one render path and no dual branch. `.v5-acct` is the page's section stack,
   the same one-declaration shape as `.v5-dbt`/`.v5-rep`. `.v5-acct-card` is
   the one new card type: a padded `.g` surface holding a heading and either a
   form (legacy `.field`/`form-grid` markup, which `.v5-scope`'s base layer
   already styles — see §3.3) or a couple of plain rows. Everything a card
   actually needs beyond that — buttons, chips, read-only fields, link rows —
   is reuse: `.v5-btn` `.ps`/`.gh`/`.ng`, `.v5-auth-chips`/`.v5-auth-chip` from
   the AUTH round, `input[readonly]` for the locked phone number, and
   `.v5-row` `.cmp` `.sta` / `.v5-row` `.lnk` for label/value pairs and the
   link to `/app-lock`, exactly as the account-detail page already uses them. */
:root[data-ui="v5"] .v5-acct { display:grid; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .v5-acct-card { display:grid; gap:var(--v5-sp-3); padding:var(--v5-sp-4) }
:root[data-ui="v5"] .v5-acct-card h2 { margin-block:0 }

/* Public legal page — one shared route for privacy + terms. The `.app` / `.zone`
   structure keeps the legacy branch readable too; these selectors only tighten
   the v5 presentation. */
:root[data-ui="v5"] .legal-page { max-inline-size:760px; gap:var(--v5-sp-3) }
:root[data-ui="v5"] .legal-hero,
:root[data-ui="v5"] .legal-section {
  background:var(--v5-surface);
  border:1px solid var(--v5-line);
  border-radius:var(--v5-r-xl);
  box-shadow:var(--v5-sh);
}
:root[data-ui="v5"] .legal-title {
  margin:0;
  font-size:var(--v5-t-xl);
  line-height:1.6;
}
:root[data-ui="v5"] .legal-updated {
  margin:var(--v5-sp-2) 0 0;
  color:var(--v5-text-muted);
  font-size:var(--v5-t-sm);
}
:root[data-ui="v5"] .legal-section h2,
:root[data-ui="v5"] .legal-section h3 {
  margin-block:0;
}
:root[data-ui="v5"] .legal-section h2 { font-size:var(--v5-t-lg) }
:root[data-ui="v5"] .legal-section h3 {
  margin-block-start:var(--v5-sp-4);
  font-size:var(--v5-t-md);
}
:root[data-ui="v5"] .legal-section p,
:root[data-ui="v5"] .legal-section li {
  color:var(--v5-text);
  line-height:2;
  font-size:var(--v5-t-sm);
}
:root[data-ui="v5"] .legal-section ul,
:root[data-ui="v5"] .legal-section ol {
  margin:0;
  padding-inline-start:1.4rem;
}
:root[data-ui="v5"] .legal-section a {
  color:var(--v5-accent);
  font-weight:700;
}

}
