/* ============================================================================
   WPROVIDER — premium redesign layer for the client portal.
   Loaded AFTER portal.css so it has the final word in the cascade.

   Direction: a calm, instrument-grade financial console. Palette (2026-07-28):
   GREEN — but a different green per theme, on purpose. Light is a DEEP FOREST
   #214C31 on a warm paper canvas; dark is a CLEAN EMERALD #3FC77F on a lifted
   green-charcoal base. Carrying the deep forest into dark was the first pass and
   it failed twice over: a fill darker than its own card cannot pop or glow, and
   at that lightness the hue reads olive/military.

   So the accent flips role per theme, exactly as the retired indigo did:
   light = dark fill + white ink; dark = light fill + dark ink. Text on the
   accent is --portal-accent-ink; accent-coloured TEXT is
   --portal-accent-soft-ink (the accent in both themes now — the token stays
   because a future dark-accent theme would need it again).

   Success/money-positive is a leafier, warmer green than the brand accent so a
   "+" amount never reads as an accent chip; warning amber, destructive red.

   Re-skin only (color / type / depth / motion); layout grids are untouched.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. Design tokens
   --------------------------------------------------------------------------- */
:root {
    /* SURFACES — paper-calm, white cards, and one held neutral hue.
       The old values drifted through yellow 95° (bg) → olive 114° (surface-soft)
       → sage 140-148° (borders, muted), which is why the canvas read as old
       newsprint and the greys as dusty: nothing agreed on a hue. Every neutral
       now holds ONE hue, 280°, at 6-10% of the chroma sRGB allows there — enough
       that edges resolve toward something, far too little to name as a colour,
       and 124° away from the brand green so a surface can never read as green.
       Lightness is held within ~1% of the old values: the hierarchy is unchanged,
       only the cast is. */
    --portal-bg: #f3f4f8;
    --portal-surface: #ffffff;
    --portal-surface-soft: #ebecf3;
    /* hover/press are the ONLY surfaces allowed the brand hue — they are
       transient and small. Chroma doubled: pale sage becomes a breath of mint. */
    --portal-hover: #dfece3;
    --portal-press: #cee4d5;
    /* Press fill for ROW-shaped surfaces (nav links, list rows, wide selects).
       Half the step of --portal-press: the same tint on a 240x44 plate is a
       highlighter swipe, not a press. One notch past hover instead of two. */
    --portal-press-row: #d7e8dc;
    /* Press fill for a row that is ALREADY on the soft accent (the current nav
       item). Pressing it must read, and swapping it to the neutral tint would
       make the active pill blink grey — so it deepens its own accent instead. */
    --portal-accent-press: #cfe1d7;
    /* The pointer-anchored press bloom (see section 2). Sits ON TOP of the
       press tint, so it is deliberately faint — it is a highlight, not a fill. */
    --portal-press-bloom: rgba(20, 62, 42, .13);
    /* Flags. TWO ring tokens, because a flag lands on two different kinds of
       surface and one value cannot serve both — that single value is exactly
       why the owner kept reporting flags bleeding into the plate (2026-08-05).

       --portal-flag-ring is for a flag on a CARD surface (region cards, select
       options). That surface flips with the theme, so this token flips too:
       dark ring in light theme, light ring in dark theme.

       --portal-flag-ring-plate is for a flag on the brand ARTWORK PLATE (catalog
       tile badge, eSIM masthead). That plate is light in BOTH themes (see
       html.dark .cat-tile-img), so this one must NOT flip — in dark theme the
       flipping token resolved to a white ring on a light plate, i.e. no edge at
       all, which is why the tile badges had no rectangle in the owner's
       screenshots.

       The mat is the backing behind the tile GLOBE glyph that stands in for
       world-ish regions — monochrome line art, which does need a surface. */
    --portal-flag-ring: rgba(15, 23, 42, .34);
    --portal-flag-ring-plate: rgba(15, 23, 42, .40);
    --portal-flag-mat: #e5e8ee;
    /* The tile badge's ink chip. A hairline ALONE cannot carry the badge on the
       artwork plate: half the catalog's flags are white-dominant (JP, FI, PL,
       QA, AR, UY…), so their INTERIOR matches the plate and the badge reads as
       a floating red dot or a floating blue cross rather than as a flag. The
       chip gives every flag the same dark surround regardless of what it
       contains; the chip-ring keeps a dark-banded flag (DE, EE) off the chip's
       own ink. Fixed in both themes — the plate under them does not flip. */
    --portal-flag-chip: rgba(15, 23, 42, .78);
    --portal-flag-chip-ring: rgba(255, 255, 255, .26);
    --portal-text: #17181d;
    --portal-muted: #676872;
    --portal-border: #d4d5df;
    --portal-border-soft: #e4e5ed;
    /* Third border weight — the "instrument" rule. The surface ladder (section 6)
       carries hierarchy on LINE WEIGHT, not shadow depth: soft hairline for a
       sheet, --portal-border for a panel, --portal-border-strong under a section
       heading or a table header. Not 2px — that is a different (brutalist) voice. */
    --portal-border-strong: #7e7f8a;
    --portal-rule-w: 1px;
    --portal-rule-w-strong: 1.5px;
    /* ACCENT — a jewel-tone green, hue-locked at OKLCh ~156.5°, which is the band
       Rolex (157.5°), British Racing Green (157.0°) and Lacoste PMS 357 sit in.
       Not the 135-149° band (Spotify, Apple systemGreen) — that reads herbal, and
       not desaturated, which is what reads military: every green that reads
       expensive rides >=73% of the chroma sRGB can give at its lightness. The old
       #214c31 rode 70.4%; this rides 81.7% at the same weight on the page.
       --portal-accent is a FILL; text on it is --portal-accent-ink, which flips
       per theme. --portal-accent-dark is the PRESSED accent. Accent-coloured
       TEXT/icons use --portal-accent-soft-ink, never a bare --portal-accent. */
    --portal-accent: #1c5637;
    --portal-accent-dark: #143d27;
    --portal-accent-soft: #dce9e1;
    --portal-accent-soft-ink: #1c5637;
    /* .70, not .28 — at .28 the halo measured 1.4:1 against white, i.e. it was
       not a focus indicator at all. */
    --portal-accent-ring: rgba(28, 86, 55, .70);
    --portal-accent-ink: #ffffff;
    /* The accent as a LINE, not a fill — for the one accented top rule on the
       instrument surface and for row-highlight-by-border (no fill under data). */
    --portal-accent-line: rgba(28, 86, 55, .55);
    /* The one focus halo every field blooms on focus. Resolves accent-ring at
       use-site, so it auto-brightens in dark mode. */
    --portal-focus-ring: 0 0 0 3px var(--portal-accent-ring);
    --portal-focus-ring-inset: inset 0 0 0 calc(var(--portal-rule-w) * 3) var(--portal-accent-ring);
    /* The brand as a GRADIENT — the primary CTA fill. */
    --portal-accent-gradient: linear-gradient(135deg, #143d27 0%, #1c5637 100%);
    /* The canvas wash — the auth screen's page background. Neutral-cool: Law 5,
       the brand hue never touches a surface. The workspace canvas stays flat +
       beams (see .app-shell). */
    --portal-canvas-gradient: linear-gradient(180deg, #f3f4f8 0%, #e7e8f0 100%);

    /* ---- FUNCTIONAL PALETTE -------------------------------------------------
       Variety does not come from tinting the walls. It comes from having more
       than one colour that MEANS something. The brand accent is reserved for
       "this is Simorox and you can press it"; everything below is spent on a
       6px dot, a 20px pill, a 10px chip label or a 16px glyph, and answers a
       question the user actually has. Roughly 3% of pixels are coloured.
       Each colour ships as ink + an opaque -soft chip fill + a -line hairline. */
    /* money in / completed / delivered. The old #3f8a5b measured 4.20:1 on white
       — a live WCAG failure on every "+$" figure. */
    --portal-good: #1d7941;
    --portal-good-soft: #dcefe4;
    --portal-good-line: rgba(29, 121, 65, .40);
    /* caution — the ONE warm colour in the product. The old #b17a32 was literally
       ochre (OKLCh hue 70.2°; the pigment is 59.6°) and failed at 3.68:1. This is
       burnt orange riding 100% of the gamut ceiling: the Hermès-orange escape
       route from the mud window, not the mustard one. No gold, no amber, no
       beige, no olive anywhere else in the system. */
    --portal-warn: #ab4f00;
    --portal-warn-soft: #fbe9de;
    --portal-warn-line: rgba(171, 79, 0, .40);
    --portal-danger: #b94241;
    --portal-danger-soft: #fbeae8;
    --portal-danger-line: rgba(185, 66, 65, .40);
    /* in flight: processing, under review, syncing. Previously these rendered
       identically to "cancelled, nothing happened". */
    --portal-progress: #007199;
    --portal-progress-soft: #e0f4ff;
    --portal-progress-line: rgba(0, 113, 153, .40);
    /* reversal: refunded, chargeback, dispute. A refund is money coming BACK —
       bucketing it with FAILED was simply wrong. Orchid (hue 342), not violet:
       against the progress blue that is dE 12.5 under deuteranopia instead of 3.1. */
    --portal-reversed: #8d3e73;
    --portal-reversed-soft: #ffe9f6;
    --portal-reversed-line: rgba(141, 62, 115, .40);
    /* nothing is happening: new, cancelled, archived, draft. Aliases --portal-muted
       on purpose — a second grey four L* away is noise, not a palette. */
    --portal-idle: #676872;
    --portal-idle-soft: #e9eaf1;
    --portal-idle-line: rgba(103, 104, 114, .40);
    /* TAXONOMY — identity, not state: catalog product types and ledger categories.
       One shared chroma/lightness band is what makes a set look designed. */
    --portal-hue-sapphire: #32529b;
    --portal-hue-sapphire-soft: #e9f0ff;
    --portal-hue-verdigris: #1e766e;
    --portal-hue-verdigris-soft: #d9f5f1;
    --portal-hue-iris: #624391;
    --portal-hue-iris-soft: #f2edff;
    /* Money ink = body ink. Hue lives on the sign and the category, not the digits
       (the old #1d2921 was a dusty dark green at 34% of gamut). */
    --portal-financial-text: #17181d;
    /* Canvas texture = LIGHT SHAFTS: two wide, soft diagonal beams falling
       across the upper canvas under the ambient glow, veiled back into the
       flat --portal-bg by mid-viewport. Smooth gradients ONLY — every
       repeating pattern has been rejected, do NOT re-add: hairline blueprint
       grid («очень колхозная», 2026-07-15), dot grid, engraved guilloché
       rosettes («колхозно»), static radial-gradient blooms («как нейросеть
       нарисовала»), blurred drifting aurora orbs («не очень круто»), oil-film
       marble SVG, raster film grain, SVG grain tile. */
    /* Law 5: the brand hue never touches a surface, and the beams cover most of
       the upper canvas — so they are neutral-cool, like everything structural. */
    --portal-canvas-beam: rgba(23, 24, 29, .04);
    --portal-canvas-beam-soft: rgba(23, 24, 29, .025);
    --portal-canvas-veil-from: rgba(243, 244, 248, 0);
    /* layered, premium depth — cast in the palette's near-black ink (#141816),
       i.e. essentially NEUTRAL. Deliberately NOT the accent's green: the old
       emerald-era rgba(20,33,28,…) put a warm-grey tint under every white surface
       and read as "eggy". Longer, softer throw so a surface floats instead of
       sitting on a chunky pad. */
    /* Every floating shadow starts with a 1px ring (Primer's habit) so a surface
       has an EDGE, not just a smudge under it. Cast in the palette's neutral-cool
       ink — never the accent's green: the emerald-era greenish shadow put a warm
       tint under every white surface and read as "eggy". */
    /* No `0 0 0 1px` ring here either. At 5% on #f3f4f8 it barely read, but
       leaving it would put the two themes on different material models, which
       resurfaces the moment they are compared side by side. Light's rim already
       gets the lit top edge RIGHT (.9) — that is what dark was missing. */
    --portal-shadow-sm:
        0 1px 1px rgba(23, 24, 29, .05),
        0 2px 4px -2px rgba(23, 24, 29, .06);
    --portal-shadow:
        0 1px 2px rgba(23, 24, 29, .05),
        0 6px 12px -8px rgba(23, 24, 29, .10),
        0 24px 48px -32px rgba(23, 24, 29, .20);
    --portal-shadow-lg:
        0 1px 2px rgba(23, 24, 29, .06),
        0 12px 24px -12px rgba(23, 24, 29, .14),
        0 48px 88px -48px rgba(23, 24, 29, .28);
    /* The card RIM — a lit top edge plus an outer ring. This is what makes a card
       LIFT off the canvas instead of sitting flat on it; it is a plain box-shadow,
       so it costs nothing to scroll and carries no hue. In light it is barely a
       hairline; in dark it does most of the work (see html.dark). */
    --portal-rim:
        inset 0 1px 0 rgba(255, 255, 255, .9),
        inset 0 -1px 0 rgba(23, 24, 29, .045),
        0 1px 2px rgba(23, 24, 29, .05);
    /* The sidebar/topbar plate. In light this is today's --portal-surface, so
       the token is a no-op here; dark uses it to dissolve the grey L-shape. */
    --portal-chrome: #ffffff;
    --portal-radius: 16px;
    --portal-radius-sm: 11px;
    /* panel fill — near-opaque card tone (frosted, but cards read clearly). */
    --portal-glass: rgba(255, 255, 255, .9);
    /* lighter frosted glass for the dashboard plates — more translucent than the
       chrome glass, so the canvas texture drifts softly through them. */
    --portal-card-glass: rgba(255, 255, 255, .55);
    /* "liquid glass" specular — a soft, NEUTRAL light highlight that drifts across
       panels (the wave). No colour: motion without re-flooding the UI green. */
    /* Light has no sheen. It used to carry a 5%-alpha dark radial that was only
       invisible because the 220% geometry pushed it off the card — an accident,
       not a decision. Dark defines a real top-light (see html.dark). */
    --portal-sheen: none;
    /* motion — one standardized system, used everywhere */
    --mo-fast: var(--portal-mo-fast);
    --mo-base: var(--portal-mo-base);
    --mo-slow: var(--portal-mo-slow);
    --mo-ease: var(--portal-ease);
    --mo-out: var(--portal-ease-out);
    --mo-spring: var(--portal-ease-spring);
    /* Press is a quiet dip, never a bounce — a gentle scale on an ease-out
       curve. (The spring overshoots, which read as jittery on click.) */
    --mo-press: var(--portal-mo-press);
    /* Press timing is ASYMMETRIC — see the note at --portal-mo-press-in. */
    --mo-press-in: var(--portal-mo-press-in);
    --mo-press-out: var(--portal-mo-press-out);
    --mo-ease-press-in: var(--portal-ease-press-in);
    --mo-ease-press-out: var(--portal-ease-press-out);
    /* type — Switzer covers Latin + Cyrillic and drives headings AND body for a
       consistent bilingual system. Numerals use Inter (already loaded): a clean,
       standard grotesk with true tabular/lining figures — the conventional choice
       for money & data, and a natural match for Switzer. */
    --portal-font-ui: "Switzer", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --portal-font-display: "Switzer", Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --portal-font-figure: Inter, "Switzer", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --portal-font-number: Inter, "Switzer", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    --portal-heading-font: var(--portal-font-display);
    /* mono — system monospace stack for code, TXIDs, wallet addresses & keys. No
       web font is downloaded for mono (speed). --portal-font-code aliases this so
       the legacy code/samp rules in portal.css inherit the same system stack. */
    --portal-font-mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", "Courier New", monospace;
    --portal-font-code: var(--portal-font-mono);

    /* TYPE SCALE — the portal's dominant sizes on one ladder (base = 13px, the
       default body/cell size). New features pick a step here; legacy half-pixel
       one-offs (12.5/13.5/14.5) should migrate onto these as they're touched. */
    --portal-text-xs: 11px;
    --portal-text-sm: 12px;
    --portal-text-base: 13px;
    --portal-text-md: 14px;
    --portal-text-lg: 16px;
    --portal-text-xl: 18px;
    --portal-text-2xl: 22px;
    /* One step above the page title, for the single headline figure a screen is
       about (the Finances balance). Not for prose. */
    --portal-text-3xl: 28px;
    /* weights — shared Switzer/Inter ladder. */
    --portal-weight-normal: 400;
    --portal-weight-medium: 500;
    --portal-weight-semibold: 600;
    --portal-weight-bold: 700;
    --portal-weight-heavy: 800;

    /* SPACING SCALE — 4/8/12/16/18/24/32/48. space-5 (18px) is the section gap. */
    --portal-space-1: 4px;
    --portal-space-2: 8px;
    --portal-space-3: 12px;
    --portal-space-4: 16px;
    --portal-space-5: 18px;
    --portal-space-6: 24px;
    --portal-space-7: 32px;
    --portal-space-8: 48px;

    /* Wide dialog measure for task flows that split navigation from detail.
       Deposit is the first consumer; keeping the measure in the layout scale
       prevents another one-off modal width from becoming a parallel system. */
    --portal-dialog-wide: 960px;

    /* RADIUS SCALE — extends --portal-radius (16) / --portal-radius-sm (11) with
       the two other in-use corners (10/14) and the fully-rounded pill. */
    /* Ladder tightened so the three surface tiers read apart: 16 (instrument) /
       12 (panel) / 8 (sheet). 16-vs-14-vs-10 was too close to distinguish. */
    --portal-radius-xs: 8px;
    --portal-radius-md: 12px;
    --portal-radius-pill: 999px;

    /* MOTION (canonical namespace) — the --portal-* tokens hold the SOURCE
       values; the shorthand --mo-* names below (276 existing references) are
       back-compat aliases of these. Edit durations/eases HERE only. */
    --portal-mo-fast: 140ms;
    --portal-mo-base: 210ms;
    --portal-mo-slow: 340ms;
    --portal-ease: cubic-bezier(.4, 0, .2, 1);
    --portal-ease-out: cubic-bezier(0, 0, .2, 1);
    --portal-ease-spring: cubic-bezier(.34, 1.4, .64, 1);
    /* PRESS TIMING — deliberately asymmetric, and the single thing that separates
       a press that feels physical from one that reads as a sprite swap. Press-in
       is short and accelerating: it has to look like it is tracking the finger,
       not catching up to it. The release is nearly 3x longer and strongly
       decelerating — the surface LETTING GO. One duration for both directions
       (the old behaviour: 140ms each way) is what made every control in here feel
       mechanical. Applied via the standard idiom: the RELEASE transition lives on
       the base rule, the :active / .is-pressing rule overrides duration+easing for
       the way IN. */
    --portal-mo-press-in: 90ms;
    --portal-mo-press-out: 260ms;
    --portal-ease-press-in: cubic-bezier(.4, 0, 1, 1);
    /* The release OVERSHOOTS very slightly (peaks ~4% past rest, then settles).
       This is the difference between "the state changed back" and "the surface
       let go", and it is where the whole feel of the thing lives. Note this is a
       gentler curve than --portal-ease-spring, which peaks ~40% past and is
       reserved for entrances: a spring that visible on a press reads as jitter,
       which is why the press must never use it. On colour channels the overshoot
       is imperceptible, so one curve is safe for the whole contract. */
    --portal-ease-press-out: cubic-bezier(.22, 1.24, .46, 1);
    /* press TRANSFORM (note: --portal-press is already a surface COLOR token
       above, so the motion alias is namespaced --portal-mo-press).
       SMALL CONTROLS ONLY — buttons, pills, chips, icon buttons. Never on a row.
       A scale is a RATIO, so the pixel travel it produces grows with the element:
       on a 240x44 sidebar row .98 moves the vertical edges 2.4px but the
       horizontal edges only 0.44px, so the row does not depress, it NARROWS like
       a rubber stamp — and the 14px label gets bitmap-resampled to 13.72px and
       goes soft. Rows take the tint alone (--portal-press-row); see section 2. */
    --portal-mo-press: scale(.97);
    /* bridge legacy tokens */
    --page: var(--portal-bg);
    --paper: var(--portal-surface);
    --ink: var(--portal-text);
    --muted: var(--portal-muted);
    --hairline: var(--portal-border);
    --black: #0a0a0a;
    --green: var(--portal-good);
    --blue: var(--portal-accent);
    --wp-blue: var(--portal-accent);
    --wp-blue-soft: var(--portal-accent-soft);
    --wp-blue-ring: var(--portal-accent-ring);
    --cabinet-primary: var(--portal-accent);
    --cabinet-primary-dark: var(--portal-accent-dark);
    --cabinet-soft: var(--portal-accent-soft);
}

html.dark {
    color-scheme: dark;

    /* ── DEEP INK ────────────────────────────────────────────────────────────
       One model: a deep blue-ink room, two light sources above it (a green-teal
       key from the left, a cool fill from the right), and surfaces that are cool
       slate — tinted AWAY from the brand at hue 258-264.

       WHY THE FLOOR DROPPED. The old canvas #1b1b1f measured OKLCh lightness
       22.4%. That is not a canvas value — it is where Linear (13.9%), Raycast
       (13.4%), Primer (17.6%) and Radix (17.9%) put their CARDS. The whole
       product was compressed into a band running L 22 → 42 that never once
       touched black, and a mid-grey band with no black in it is the definition
       of «залито серым». The fix is to drop the floor and give the neutrals a
       real temperature — NOT to sprinkle more accent, which was the previous
       attempt and was invisible: hue perception in a dark surround collapses
       below roughly 40x40px, so colour has to arrive as an OBJECT.

       WHY THE TINT GOES AWAY FROM THE BRAND. Tinting surfaces WITH the green
       made the screen green («куда ни посмотришь всё зелёное»). Tinting them
       away from it makes the emerald read as a jewel by temperature contrast,
       and it is structurally impossible for the result to read as green. It is
       equally impossible for it to go khaki: olive is what you get when a
       green-cast neutral meets a green accent at similar chroma, and there is
       no green-cast neutral left in this file. */
    --portal-bg: #0b0f15;              /* oklch(16.7% .0142 258) */
    --portal-chrome: #0e131a;          /* sidebar + topbar + app-shell column */
    --portal-surface: #1e242f;         /* oklch(25.9% .0225 262) — opaque raised */
    --portal-surface-soft: #272e3b;    /* inputs, wells, table headers */
    --portal-hover: #313949;
    --portal-press: #3b4455;
    --portal-press-row: #363e4f;
    --portal-accent-press: rgba(53, 206, 133, .28);
    --portal-press-bloom: rgba(150, 226, 190, .12);
    /* DARK NEEDS A LIGHT RING — for the flags that sit on a CARD, which is dark
       here: a near-black hairline on a dark card is invisible. */
    --portal-flag-ring: rgba(255, 255, 255, .32);
    /* …and the PLATE ring must stay dark even in dark theme, because the plate
       it lands on is light in both themes. Flipping this one with the rest of
       the palette is the bug that left the tile badges with no edge at all in
       dark mode. It is NOT redeclared lighter here on purpose. */
    --portal-flag-ring-plate: rgba(15, 23, 42, .40);
    --portal-flag-mat: #e5e8ee;
    --portal-flag-chip: rgba(15, 23, 42, .78);
    --portal-flag-chip-ring: rgba(255, 255, 255, .26);
    --portal-border-soft: #222834;
    --portal-border: #2f3745;
    --portal-border-strong: #8b95a7;   /* 3.24-6.36:1 — holds WCAG 1.4.11 */

    /* Text hierarchy is a TEMPERATURE step as well as a lightness step: primary
       near-neutral, secondary distinctly cool — a 4.8x chroma ratio. Type covers
       more area than every dot, pill and chip in the product combined, which
       makes this the cheapest visible hue we have. */
    --portal-text: #f0f2f6;
    --portal-muted: #aab4c6;
    --portal-financial-text: #f0f2f6;

    /* MATERIAL. --portal-glass is deliberately UNCHANGED: 22 consumers include
       the deposit modal panel, the messenger layout and .portal-close, and
       repointing it to a light alpha produces a modal you read the page through
       — against recorded canon (непрозрачные поповеры). --portal-glass-thin is
       new and reaches exactly two surfaces, both with moving content behind. */
    --portal-card-glass: rgba(30, 37, 49, .88);
    /* v6 changed this token's ALPHA policy but never its HUE, so it kept the
       rgb of the retired grey palette (41,42,48 = the old --portal-surface).
       Every control that reads it — the collapse button, the balance chip, the
       messenger toolbar, the marketplace tabs, the modal close — therefore sat
       neutral-grey next to slate siblings, one visible step off-palette. Same
       family as card-glass now, one tier lighter, so a control reads as lifted
       whether it sits on the chrome, on a card or over the canvas. */
    --portal-glass: rgba(39, 46, 59, .92);
    --portal-glass-thin: rgba(14, 19, 26, .62);
    /* The old sheen was a radial at 50% 50% — a white blob in the MIDDLE of the
       panel, terminating at a stop you can see. That is airbrushed plastic. One
       direction, from the top, every card. */
    --portal-sheen:
        linear-gradient(180deg,
            rgba(255, 255, 255, .075) 0%,
            rgba(255, 255, 255, .028) 38%,
            rgba(255, 255, 255, .012) 100%);

    /* BRAND — hue unchanged, canon. */
    --portal-accent: #35ce85;
    --portal-accent-dark: #24a868;
    /* .12 composited to 1.259:1 — the active nav item has been effectively
       invisible this whole time. That is a live bug, not a redesign. */
    --portal-accent-soft: rgba(53, 206, 133, .20);
    --portal-accent-ink: #0b0f11;
    --portal-accent-soft-ink: #35ce85;
    --portal-accent-ring: rgba(53, 206, 133, .85);
    --portal-accent-line: rgba(53, 206, 133, .55);
    /* A physical control is lit from above; a diagonal gradient reads as a sticker. */
    --portal-accent-gradient: linear-gradient(180deg, #3fdc90 0%, #2bb974 58%, #24a868 100%);
    /* The hero plate is on the TEAL side of green (hue 164). At low lightness a
       157° green slides toward olive, and «охра» was the exact complaint. */
    --portal-hero-plate: linear-gradient(168deg, #0f4632 0%, #092c21 100%);
    --portal-hero-muted: #a9cbbb;
    --portal-canvas-gradient: linear-gradient(180deg, #0b0f15 0%, #141b26 100%);

    /* FUNCTIONAL PALETTE — unchanged. All ten measure 5.09-7.30:1 on the
       brightest point of the new card, so none of them needs touching. */
    --portal-good: #6ada94;
    --portal-good-soft: #1a3e27;
    --portal-good-line: rgba(106, 218, 148, .40);
    --portal-warn: #fa8e44;
    --portal-warn-soft: #4d2c16;
    --portal-warn-line: rgba(250, 142, 68, .40);
    --portal-danger: #fc7f71;
    --portal-danger-soft: #5d1d18;
    --portal-danger-line: rgba(252, 127, 113, .40);
    --portal-progress: #65cdff;
    --portal-progress-soft: #193a4a;
    --portal-progress-line: rgba(101, 205, 255, .40);
    --portal-reversed: #e688c4;
    --portal-reversed-soft: #561c44;
    --portal-reversed-line: rgba(230, 136, 196, .40);
    --portal-idle: #aab4c6;
    --portal-idle-soft: #262d3a;
    --portal-idle-line: rgba(170, 180, 198, .40);
    --portal-hue-sapphire: #91b5ff;
    --portal-hue-sapphire-soft: #112e75;
    --portal-hue-verdigris: #28dbcc;
    --portal-hue-verdigris-soft: #1a3c38;
    --portal-hue-iris: #c3a1ff;
    --portal-hue-iris-soft: #40206a;

    /* CANVAS LIGHT — two temperatures. Neutral white measured 1.096:1, i.e.
       invisible; two sources at different temperatures is the "lit room" tell,
       one is just a tint. */
    --portal-canvas-beam: rgba(190, 255, 230, .045);
    --portal-canvas-beam-soft: rgba(150, 190, 255, .032);

    /* ── DEPTH ───────────────────────────────────────────────────────────────
       NOT ONE `0 0 0 1px rgba(0,0,0,x)` ANYWHERE — that ring is what made the
       cards look die-cut. Composited over the canvas it landed at L* 3.4 between
       a canvas at 9.9 and a card at 13.9: a DARK line between two lighter
       fields, which is exactly how an illustrator draws card stock. Shadows are
       now cast in #040a12, the room's own darkest value, never pure black, and
       every y-offset is positive because the light is above. */
    --portal-shadow-sm:
        0 1px 2px rgba(4, 10, 18, .50),
        0 3px 8px -4px rgba(4, 10, 18, .55);
    --portal-shadow:
        0 1px 2px rgba(4, 10, 18, .45),
        0 8px 20px -10px rgba(4, 10, 18, .58),
        0 28px 56px -30px rgba(4, 10, 18, .72);
    --portal-shadow-lg:
        0 2px 4px rgba(4, 10, 18, .50),
        0 14px 32px -14px rgba(4, 10, 18, .62),
        0 44px 80px -40px rgba(4, 10, 18, .82);

    /* THE CARDBOARD FIX — a DIRECTIONAL BEVEL, not an outline. Four different
       values on four sides: a bright top facing the light, a left shoulder, a
       weaker right shoulder, a dark bottom edge inside the box. Reflectance on a
       real surface rises toward grazing angles, so a lit edge VARIES around the
       perimeter; a constant-luminance contour is the one edge physics forbids
       and the one every toon shader draws — which is what «мультяшный» named.
       The lit top goes .045 -> .14 (1.142:1 -> 1.56:1). The approved LIGHT theme
       ships .9 in that same slot: dark had a 20x under-scale on the single cue
       that makes a panel look lit. Deliberately not .30+: replacing a dark
       outline with a bright one is the same cardboard inverted. */
    --portal-rim:
        inset 0 1px 0 rgba(255, 255, 255, .14),
        inset 1px 0 0 rgba(255, 255, 255, .055),
        inset -1px 0 0 rgba(255, 255, 255, .028),
        inset 0 -1px 0 rgba(4, 10, 18, .30),
        0 1px 2px rgba(4, 10, 18, .45),
        0 10px 22px -12px rgba(4, 10, 18, .58),
        0 30px 56px -32px rgba(4, 10, 18, .75);

    /* legacy bridge for dark */
    --page: var(--portal-bg);
    --paper: var(--portal-surface);
    --ink: var(--portal-text);
    --muted: var(--portal-muted);
    --hairline: var(--portal-border);
    --black: #0a0a0a;
    --wp-page: var(--portal-bg);
    --wp-surface: var(--portal-surface);
    --wp-surface-soft: var(--portal-surface-soft);
    --wp-text: var(--portal-text);
    --wp-muted: var(--portal-muted);
    --wp-border: var(--portal-border);
    --wp-border-soft: var(--portal-border-soft);
}

/* The base sheet redefines a couple of tokens under `html:not(.dark)` (higher
   specificity than :root), so re-assert the full light palette at that level. */
html:not(.dark) {
    --portal-bg: #f3f4f8;
    --portal-surface: #ffffff;
    --portal-surface-soft: #ebecf3;
    --portal-hover: #dfece3;
    --portal-press: #cee4d5;
    --portal-press-row: #d7e8dc;
    --portal-accent-press: #cfe1d7;
    --portal-press-bloom: rgba(20, 62, 42, .13);
    --portal-flag-ring: rgba(15, 23, 42, .34);
    --portal-flag-ring-plate: rgba(15, 23, 42, .40);
    --portal-flag-mat: #e5e8ee;
    --portal-flag-chip: rgba(15, 23, 42, .78);
    --portal-flag-chip-ring: rgba(255, 255, 255, .26);
    --portal-text: #17181d;
    --portal-muted: #676872;
    --portal-border: #d4d5df;
    --portal-border-soft: #e4e5ed;
    --portal-border-strong: #7e7f8a;
    --portal-glass: rgba(255, 255, 255, .9);
    --portal-card-glass: rgba(255, 255, 255, .55);
    --portal-accent: #1c5637;
    --portal-accent-dark: #143d27;
    --portal-accent-soft: #dce9e1;
    --portal-accent-soft-ink: #1c5637;
    --portal-accent-ring: rgba(28, 86, 55, .70);
    --portal-accent-ink: #ffffff;
    --portal-accent-line: rgba(28, 86, 55, .55);
    --portal-good: #1d7941;
    --portal-good-soft: #dcefe4;
    --portal-good-line: rgba(29, 121, 65, .40);
    --portal-warn: #ab4f00;
    --portal-warn-soft: #fbe9de;
    --portal-warn-line: rgba(171, 79, 0, .40);
    --portal-danger: #b94241;
    --portal-danger-soft: #fbeae8;
    --portal-danger-line: rgba(185, 66, 65, .40);
    --portal-progress: #007199;
    --portal-progress-soft: #e0f4ff;
    --portal-progress-line: rgba(0, 113, 153, .40);
    --portal-reversed: #8d3e73;
    --portal-reversed-soft: #ffe9f6;
    --portal-reversed-line: rgba(141, 62, 115, .40);
    --portal-idle: #676872;
    --portal-idle-soft: #e9eaf1;
    --portal-idle-line: rgba(103, 104, 114, .40);
    --portal-hue-sapphire: #32529b;
    --portal-hue-sapphire-soft: #e9f0ff;
    --portal-hue-verdigris: #1e766e;
    --portal-hue-verdigris-soft: #d9f5f1;
    --portal-hue-iris: #624391;
    --portal-hue-iris-soft: #f2edff;
    --portal-financial-text: #17181d;
}

/* Match the scroll root to the page colour and kill the rubber-band overscroll,
   so pulling past the top/bottom doesn't reveal a stray band or smear the sticky
   topbar's backdrop-filter. */
html {
    background: var(--portal-bg);
    overscroll-behavior: none;
}
body,
.login-body {
    font-family: var(--portal-font-ui);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    background: var(--portal-bg);
    color: var(--portal-text);
    overscroll-behavior: none;
    /* Reserve the scrollbar track so a page whose content grows past the viewport
       (or a reload during language switching) never shifts the layout sideways as
       the scrollbar appears. No-op with overlay scrollbars. */
    scrollbar-gutter: stable;
}

/* Theme switch is INSTANT. JS adds .theme-anim-off to <html> for the flip so every
   transition is suppressed and the whole page repaints the new theme in ONE frame.
   The old approach — a 0.4s universal colour cross-fade, or a View-Transitions
   full-page snapshot — animated for ~0.4s, re-painting every frosted-glass and
   gradient layer the whole time, which stuttered on phones. Transitions return on
   the next frame, so hover/press still animate; instant also satisfies
   reduced-motion for free. */
html.theme-anim-off,
html.theme-anim-off *,
html.theme-anim-off *::before,
html.theme-anim-off *::after {
    transition: none !important;
}
/* Pause every perpetual animation during an active scroll. The 18–19s liquid-glow /
   gradient-drift loops each repaint a gradient every frame; freezing them for the
   ~gesture hands the paint budget back to the scroll (JS toggles html.is-scrolling
   for the gesture + 160ms). Imperceptible on 18s loops; a real scroll-fps win on
   mobile where many surfaces animate at once. */
html.is-scrolling *,
html.is-scrolling *::before,
html.is-scrolling *::after {
    animation-play-state: paused !important;
}

/* Backdrop — the flat --portal-bg canvas + the ambient top glow + the LIGHT
   SHAFTS: two wide, soft diagonal beams (smooth gradients, no pattern) that
   fall across the upper canvas and are veiled back into the flat --portal-bg
   (an opaque radial ramp) so the lower page stays perfectly calm. Layers
   top→bottom: glow, veil, beam, beam.
   PERF: was background-attachment:fixed (pinned to viewport), which forced a
   full-viewport repaint on EVERY scroll frame — the same jank staff.css removed
   (staff.css:2586). The wash now scrolls with the page (default attachment): the
   gradients are unchanged and sit in the upper canvas where they're seen, and the
   per-frame repaint is gone. */
.app-shell {
    background-color: var(--portal-bg);
    /* Just the ambient glow / veil / beams — no pattern texture on the canvas. */
    background-image:
        radial-gradient(125% 80% at 50% -12%, rgba(33, 76, 49, .05) 0%, rgba(33, 76, 49, 0) 55%),
        radial-gradient(125% 80% at 50% -12%, var(--portal-canvas-veil-from) 0%, var(--portal-canvas-veil-from) 30%, var(--portal-bg) 72%),
        linear-gradient(112deg, transparent 24%, var(--portal-canvas-beam) 40%, transparent 56%),
        linear-gradient(112deg, transparent 54%, var(--portal-canvas-beam-soft) 68%, transparent 82%);
    background-repeat: no-repeat;
    background-size: auto;
}
/* THE LIT ROOM. Same element, same layer count, same scroll attachment as
   before — what changes is that the light is now real and that the OPAQUE VEIL
   layer is gone. That veil ramped to a solid --portal-bg at ~51% of DOCUMENT
   height, so on any real page every bit of light existed only in the top half
   and the rest was flat hex. The ambient layers also get fixed PIXEL heights,
   so the falloff is identical on a short home page and a 4000px catalog instead
   of being stretched to the document. */
html.dark .app-shell {
    background-color: var(--portal-bg);
    background-image:
        /* two light shafts, now carrying the two source temperatures */
        linear-gradient(112deg, transparent 24%, var(--portal-canvas-beam) 40%, transparent 56%),
        linear-gradient(112deg, transparent 54%, var(--portal-canvas-beam-soft) 68%, transparent 82%),
        /* FILL — cool, from above-right, origin off-canvas, tail alive to 86% */
        radial-gradient(74% 54% at 94% -12%,
            rgba(116, 158, 255, .065) 0%,
            rgba(116, 158, 255, .028) 36%,
            rgba(116, 158, 255, .010) 62%,
            rgba(116, 158, 255, 0) 86%),
        /* KEY — green-teal, from above-left, origin off-canvas. Five stops and a
           tail still alive at 88%, so there is no visible termination: that
           edge is what made the previously-rejected radial read as a painted
           shape rather than as light. */
        radial-gradient(104% 62% at 16% -16%,
            rgba(46, 200, 150, .10) 0%,
            rgba(46, 200, 150, .052) 28%,
            rgba(46, 200, 150, .020) 55%,
            rgba(46, 200, 150, .007) 78%,
            rgba(46, 200, 150, 0) 100%);
    background-repeat: no-repeat;
    background-size: 100% 1400px, 100% 1400px, 100% 980px, 100% 1240px;
    background-position: 0 0, 0 0, 0 0, 0 0;
}
/* Fill the left 260px (the sidebar column) with the sidebar surface for the FULL
   viewport height. The sidebar is sticky + 100vh; its sticky range is bounded by
   .app-shell, which can sit a hair shorter than the document — so on scroll a bare
   strip of page background used to show below it. background-attachment:fixed pins
   this surface to the left of the viewport at every scroll position, so there's no
   gap. Desktop only (≥861px); mobile has a bottom tab bar, no left column. The
   opaque surface column is the TOP layer, so it hides the wash behind the
   sidebar — the colour only blooms across the content canvas. */
@media (min-width: 861px) {
    html .app-shell {
        background-image:
            linear-gradient(to right, var(--portal-chrome) 0 var(--cabinet-sidebar, 260px), var(--portal-border) var(--cabinet-sidebar, 260px) calc(var(--cabinet-sidebar, 260px) + 1px), transparent calc(var(--cabinet-sidebar, 260px) + 1px)),
            radial-gradient(125% 80% at 50% -12%, rgba(33, 76, 49, .05) 0%, rgba(33, 76, 49, 0) 55%),
            radial-gradient(125% 80% at 50% -12%, var(--portal-canvas-veil-from) 0%, var(--portal-canvas-veil-from) 30%, var(--portal-bg) 72%),
            linear-gradient(112deg, transparent 24%, var(--portal-canvas-beam) 40%, transparent 56%),
            linear-gradient(112deg, transparent 54%, var(--portal-canvas-beam-soft) 68%, transparent 82%);
        background-position: 0 0;
        background-size: auto;
        background-repeat: no-repeat;
        /* All layers scroll with the document (the column must, to span the sticky
           sidebar's full height); no background-attachment:fixed → no per-frame
           full-viewport repaint on scroll. */
    }
    html.dark .app-shell {
        background-image:
            /* LOAD-BEARING: the opaque sidebar column. It must be the identical
               fill to .sidebar and .topbar or a seam appears where the sticky
               sidebar's range ends — all three now read --portal-chrome. */
            linear-gradient(to right, var(--portal-chrome) 0 var(--cabinet-sidebar, 260px), var(--portal-border) var(--cabinet-sidebar, 260px) calc(var(--cabinet-sidebar, 260px) + 1px), transparent calc(var(--cabinet-sidebar, 260px) + 1px)),
        /* two light shafts, now carrying the two source temperatures */
            linear-gradient(112deg, transparent 24%, var(--portal-canvas-beam) 40%, transparent 56%),
            linear-gradient(112deg, transparent 54%, var(--portal-canvas-beam-soft) 68%, transparent 82%),
            /* FILL — cool, from above-right, origin off-canvas, tail alive to 86% */
            radial-gradient(74% 54% at 94% -12%,
                rgba(116, 158, 255, .065) 0%,
                rgba(116, 158, 255, .028) 36%,
                rgba(116, 158, 255, .010) 62%,
                rgba(116, 158, 255, 0) 86%),
            /* KEY — green-teal, from above-left, origin off-canvas. Five stops and a
               tail still alive at 88%, so there is no visible termination: that
               edge is what made the previously-rejected radial read as a painted
               shape rather than as light. */
            radial-gradient(104% 62% at 16% -16%,
                rgba(46, 200, 150, .10) 0%,
                rgba(46, 200, 150, .052) 28%,
                rgba(46, 200, 150, .020) 55%,
                rgba(46, 200, 150, .007) 78%,
                rgba(46, 200, 150, 0) 100%);
        background-repeat: no-repeat;
        background-size: 100% 100%, 100% 1400px, 100% 1400px, 100% 980px, 100% 1240px;
        background-position: 0 0, 0 0, 0 0, 0 0, 0 0;
    }
}
/* The liquid-glass SVG filter defs are 0×0 but render display:inline, adding a
   ~19px line-box below .app-shell (so the body ends up taller than the shell),
   which left a thin bare strip under the sticky sidebar at the bottom. Pull it
   out of flow so the shell == the document height. */

main,
html.dark main,
html .portal-workspace {
    background: transparent;
}
/* Login: clean background, no dot texture — the palette's canvas wash
   («фоновый градиент») behind the glass card, nothing else. */
.login-body {
    background-color: var(--portal-bg);
    background-image: var(--portal-canvas-gradient);
    background-repeat: no-repeat;
}
html.dark .login-body {
    background-image: var(--portal-canvas-gradient);
}
/* The login uses <main class="login-shell">; neutralise the global `main` dot
   texture + opaque fill so the clean login background (with its glow) shows.
   The dark `html.dark main` rule is more specific, so match it explicitly. */
.login-shell,
html.dark .login-shell {
    background: transparent;
    background-image: none;
}

/* ---------------------------------------------------------------------------
   2. Motion system — one transition contract + one press + one focus ring.
   --------------------------------------------------------------------------- */
.sidebar-link, .sidebar-collapse, .mp-tab, .quick-action-card, .home-feed-row,
.ns-data-table tbody tr.ns-row-link, .settings-list .settings-row, .session-row,
.messenger-thread, .balance-chip, .lang-row .lang-pill,
.ns-pagination button, .ghost-button, .ghost-danger, .portal-select-button,
.portal-date-button, .messenger-unread-toggle, .dropdown-option, .home-feed-head a,
.mp-table-link, .detail-back, button, .tool-link, input, select,
.login-form button, .security-step-next, .metric-card, .home-feed-card {
    /* The RELEASE half of the press lives here, on the resting rule: a control
       that is not being pressed is always on its way back, so it eases out over
       --mo-press-out. The :active rules below override duration+easing to snap
       IN. Hover keeps the neutral --mo-fast (it is not a press; see .sidebar-link
       and the hover rules, which re-assert the fast pair). */
    transition:
        background-color var(--mo-press-out) var(--mo-ease-press-out),
        color var(--mo-fast) var(--mo-ease),
        border-color var(--mo-fast) var(--mo-ease),
        box-shadow var(--mo-base) var(--mo-ease),
        transform var(--mo-press-out) var(--mo-ease-press-out),
        filter var(--mo-press-out) var(--mo-ease-press-out);
}

/* ONE PRESS SYSTEM: "the tint IS the press; scale is reserved for objects."
   Scale is permitted only on discrete, self-contained controls — buttons, pills,
   chips, icon buttons. Anything ROW-shaped (nav links, list rows, wide selects)
   takes the tint alone and never moves: a ratio transform cannot produce
   consistent motion across the 6x size range this portal spans, and on a
   full-width row it narrows the plate and softens the label instead of reading
   as "down". Same rule Linear's sidebar, Things' rows and a UITableViewCell all
   follow. See --portal-mo-press for the measurements. */

/* Press IN — fast and accelerating, on every channel a press can use. */
.sidebar-link:active, .sidebar-collapse:active, .mp-tab:active,
.balance-chip:active, .lang-row .lang-pill:active, .home-feed-row:active,
.ns-data-table tbody tr.ns-row-link:active, .session-row:active,
.messenger-thread:active, .dropdown-option:active,
.ns-pagination button:active:not(:disabled), .ghost-button:active, .ghost-danger:active,
.portal-select-button:active, .portal-date-button:active, .messenger-unread-toggle:active,
.tool-link:active, .login-form button:active, .security-step-next:active, button:active {
    transition-duration: var(--mo-press-in);
    transition-timing-function: var(--mo-ease-press-in);
}

/* Press dip — SMALL CONTROLS only (they read as "pressable" objects). */
.mp-tab:active, .balance-chip:active, .lang-row .lang-pill:active,
.ns-pagination button:active:not(:disabled), .ghost-button:active, .ghost-danger:active,
.messenger-unread-toggle:active, .login-form button:active,
.security-step-next:active, button:active {
    transform: var(--mo-press);
}
/* …and explicitly NOT on the row-shaped ones. `button:active` above is a
   catch-all, and three of these are <button>s, so they need the opt-out by name.
   (.sidebar-link is an <a>, so simply leaving it out of the list above is
   enough — but it is listed here too, as the statement of intent.) */
.sidebar-link:active, .sidebar-collapse:active, .tool-link:active,
.portal-select-button:active, .portal-date-button:active {
    transform: none;
}

/* Press fill — an opaque step, NEVER a translucent fill (translucent accent-soft
   is what made solid buttons/rows "vanish" on press, especially in dark). */
.ns-pagination button:active:not(:disabled), .ghost-button:active, .ghost-danger:active,
.balance-chip:active, .lang-row .lang-pill:not(.active):active,
.mp-tab:not(.active):active, .messenger-unread-toggle:not(.active):active {
    background: var(--portal-press);
}
/* Row-shaped surfaces take the HALF step. Full-strength --portal-press across a
   240px plate reads as a highlighter swipe; the row only has to say "you hit me". */
.home-feed-row:active, .ns-data-table tbody tr.ns-row-link:active,
.session-row:active, .messenger-thread:active, .dropdown-option:active,
.portal-select-button:active, .portal-date-button:active,
.sidebar-link:not(.active):active, .tool-link:active, .sidebar-collapse:active {
    background: var(--portal-press-row);
}

/* THE BLOOM — the press lights up from the point you actually hit.
   A flat tint tells you THAT you pressed; a bloom anchored to the pointer tells
   you WHERE, which is the thing that makes a surface feel physical rather than
   toggled. portal.js writes --press-x/--press-y on pointerdown; with no JS (or
   on a keyboard activation) the fallback is dead centre, which still reads fine.
   Painted on ::after under the content via a negative z-index inside an isolated
   stacking context, so it can never cover a label or eat a click. */
.sidebar-link,
.lang-row .lang-pill,
.home-feed-row,
.session-row,
.messenger-thread,
.dropdown-option {
    position: relative;
    isolation: isolate;
}
.sidebar-link::after,
.lang-row .lang-pill::after,
.home-feed-row::after,
.session-row::after,
.messenger-thread::after,
.dropdown-option::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    background: radial-gradient(circle at var(--press-x, 50%) var(--press-y, 50%),
        var(--portal-press-bloom) 0%,
        color-mix(in srgb, var(--portal-press-bloom) 45%, transparent) 34%,
        transparent 72%);
    transition: opacity var(--mo-press-out) var(--mo-ease-press-out);
}
.sidebar-link:active::after, .sidebar-link.is-pressing::after,
.lang-row .lang-pill:active::after, .lang-row .lang-pill.is-pressing::after,
.home-feed-row:active::after, .home-feed-row.is-pressing::after,
.session-row:active::after, .session-row.is-pressing::after,
.messenger-thread:active::after, .messenger-thread.is-pressing::after,
.dropdown-option:active::after, .dropdown-option.is-pressing::after {
    opacity: 1;
    transition-duration: var(--mo-press-in);
    transition-timing-function: var(--mo-ease-press-in);
}

/* Press for SOLID-accent elements — keep the fill, darken slightly (no vanish). */
/* The CURRENT nav item: it already sits on --portal-accent-soft, so re-stating
   that colour (the old rule) meant pressing it did nothing at all once the scale
   is gone. It deepens its own accent instead. */
html .sidebar-link.active:active {
    background: var(--portal-accent-press);
}
.mp-tab.active:active, .messenger-unread-toggle.active:active,
.login-form button:active, .security-step-next:active,
.portal-modal button[type="submit"]:active {
    filter: brightness(.92);
}

/* One focus ring for every keyboard-interactive element. */
:where(a, button, input, select, textarea,
    .sidebar-link, .sidebar-collapse, .settings-row, .mp-tab, .quick-action-card,
    .home-feed-row, .ns-row-link, .ns-data-table tbody tr.ns-row-link, .messenger-thread,
    .balance-chip, .ns-pagination button,
    .ghost-button, .ghost-danger, .portal-select-button, .portal-date-button,
    .dropdown-option, .messenger-unread-toggle, .home-feed-head a):focus-visible {
    outline: 2px solid var(--portal-accent);
    outline-offset: 2px;
    border-radius: 9px;
}

/* ---------------------------------------------------------------------------
   3. Typography
   --------------------------------------------------------------------------- */
h1,
.wip-page h1 {
    font-family: var(--portal-heading-font);
    letter-spacing: -.02em;
    line-height: 1.04;
    color: var(--portal-text);
}

main h2,
.section-head h2,
.quick-actions-panel > h2,
.home-financials > h2,
.profile-section > h2 {
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.012em;
    color: var(--portal-text);
}

/* Restrained section labels — clean display face, ink colour, no decoration. */
.quick-actions-panel > h2,
.home-financials > h2,
.profile-section > h2,
.section-head h2 {
    font-family: var(--portal-heading-font);
    font-size: clamp(17px, 1.2vw, 20px);
    font-weight: 700;
    letter-spacing: -.012em;
    color: var(--portal-text);
}

/* Tabular numerals everywhere money is shown. */
.metric-card strong,
.total-balance-card strong,
.balance-chip strong,
.amount,
.txn-amount,
.mp-metric strong,
.mp-pulse-cell > strong,
.home-feed-row .amount,
.ns-data-table .amount {
    font-family: var(--portal-font-figure);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}

/* ---------------------------------------------------------------------------
   4. Sidebar + nav
   --------------------------------------------------------------------------- */
/* Sidebar collapse is INSTANT (no animation) — the base's grid-track collapse
   handles it without any transition. */

/* Sidebar + topbar share ONE flat surface tone (var(--portal-surface)) so the
   seam between them is invisible — the old sidebar gradient + translucent topbar
   read as two different shades. The brand header below is the same height as the
   topbar, so their bottom borders meet as a single continuous line. */
.sidebar {
    background: var(--portal-surface);
    border-right: 1px solid var(--portal-border);
    padding-top: 0;
    gap: 18px;
    /* Short viewports (~720px) with the full nav (11 items) overflowed this
       fixed-height column; overflow:hidden clipped the lower groups and collided
       a group title with the link above it. Scroll the column instead — with a
       quiet hairline scrollbar — so every group and the margin-top:auto footer
       stay reachable. */
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--portal-border) transparent;
}
/* THE CHROME DISSOLVES. The desktop sidebar + topbar were one uninterrupted flat
   #292a30 L-shape covering ~18% of a 1440x900 viewport — the single largest grey
   object in the product, and a big part of «всё серое». It now sits 1.03:1 from
   the canvas: effectively dissolved. It stays OPAQUE on purpose — making the rail
   transparent means the sticky column stands still while the light behind it
   scrolls away, so the rail would visibly drain as you scroll. */
html.dark .sidebar {
    background: var(--portal-chrome);
}
.sidebar::-webkit-scrollbar { width: 7px; }
.sidebar::-webkit-scrollbar-track { background: transparent; }
.sidebar::-webkit-scrollbar-thumb {
    background: var(--portal-border);
    border-radius: var(--portal-radius-pill);
    border: 2px solid transparent;
    background-clip: padding-box;
}
.sidebar:hover::-webkit-scrollbar-thumb {
    background: var(--portal-muted);
    background-clip: padding-box;
}
/* Keep the flex children at their natural height: without this they shrink to
   fit the column and their content spills over the shrunk box, overlapping the
   next group's title. Pinning shrink to 0 makes the column genuinely overflow so
   the scroll above engages instead. */
.sidebar > * { flex-shrink: 0; }

/* Legal / company line — pinned to the bottom of the sidebar (margin-top:auto).
   The sidebar fills the full column height now, so it stays visible at the bottom. */
.sidebar-legal {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 16px 12px 4px;
    font-size: 11px;
    line-height: 1.45;
    color: var(--portal-muted);
}
.sidebar-legal strong {
    color: var(--portal-text);
    font-weight: 700;
    font-size: 11px;
    letter-spacing: .06em;
}
/* The collapse button carries the margin-top:auto that pins the footer cluster
   to the bottom; the legal line then sits directly beneath it (its own top
   padding gives the gap), instead of both fighting over the free space. */
.sidebar-collapse + .sidebar-legal {
    margin-top: 0;
}

/* wprovider brand lockup — sidebar + login. */
.sidebar-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    height: 60px;
    box-sizing: border-box;
    /* Full-bleed bottom border: the -14px side margins cancel the sidebar's 14px
       padding so the line runs edge-to-edge and continues the topbar border. */
    margin: 0 -14px;
    padding: 0 20px;
    border-bottom: 1px solid var(--portal-border);
    text-decoration: none;
}
/* Logo temporarily removed from the sidebar corner (owner request 2026-06-22).
   The login screen keeps its own brand (.login-brand). Restore by deleting both
   rules below. */
.sidebar-brand { display: none; }
.sidebar { padding-top: 18px; }
.sidebar-brand-mark,
.login-brand-mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background: var(--portal-accent);
    color: #fff;
    flex: 0 0 auto;
    box-shadow: var(--portal-shadow-sm);
}
html.dark .sidebar-brand-mark,
html.dark .login-brand-mark {
    color: var(--portal-accent-ink);
}
.sidebar-brand-mark svg,
.login-brand-mark svg {
    width: 18px;
    height: 18px;
}
.sidebar-brand-word,
.login-brand-word {
    font-family: var(--portal-heading-font);
    font-weight: 800;
    font-size: 23px;
    letter-spacing: -.02em;
    color: var(--portal-text);
}
.login-brand {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-bottom: 22px;
}
/* The brand wordmark, rendered as theme-aware text (no raster logo). Colour
   follows the theme via --portal-text, so it reads in both light and dark. */
.brand-wordmark {
    display: inline-block;
    font-family: var(--portal-font-display);
    font-weight: 700;
    font-size: 22px;
    letter-spacing: -.03em;
    line-height: 1;
    color: var(--portal-text);
}
.login-brand-word.brand-wordmark {
    font-size: 26px;
    margin: 0 auto;
}

.sidebar-group-title {
    font-size: var(--portal-text-xs);
    font-weight: 700;
    letter-spacing: .16em;
    color: var(--portal-muted);
    padding-left: 12px;
}

.sidebar-link {
    border-radius: 11px;
    color: var(--portal-text);
    font-weight: 600;
    font-size: 14px;
    letter-spacing: -.005em;
    /* Glide the hover/active highlight instead of snapping — the nav is the
       most-used surface, so easing it makes the whole UI feel alive at zero risk
       (a transition never changes behaviour). Honours reduced-motion via the
       global block below.

       This is the RELEASE curve. It used to be a `transition` SHORTHAND naming
       only background-color and color, which — being the shorthand — reset
       transition-property and silently dropped `transform` from the global
       contract in section 2. The result on the most-pressed surface in the
       product: the press scale landed in a single frame with no curve at all
       while the fill crawled behind it on a separate 140ms clock, so the two
       halves of one gesture never agreed. That desync is what read as cheap.
       Now: the fill is the whole press, and it eases out slowly on release. */
    transition: background-color var(--mo-press-out) var(--mo-ease-press-out),
                color var(--mo-fast) var(--mo-ease);
}
/* Hover is not a press — it comes in on the neutral fast pair. Declared after
   the base rule and before :active, so a mouse press (which always also hovers)
   still lands on the press-in timing below. */
.sidebar-link:hover {
    transition-duration: var(--mo-fast), var(--mo-fast);
    transition-timing-function: var(--mo-ease), var(--mo-ease);
}
/* Press in — the JS-driven .is-pressing (touch) shares the curve with :active
   (pointer) so a tap and a click are the same gesture. */
.sidebar-link:active,
.sidebar-link.is-pressing {
    transition-duration: var(--mo-press-in), var(--mo-fast);
    transition-timing-function: var(--mo-ease-press-in), var(--mo-ease);
}
/* THE NAV PRESS ANIMATION.
   The row itself must not move (see --portal-mo-press for why a scale on a
   240x44 plate narrows it and softens the label). But "must not move" is not the
   same as "must do nothing" — so the GLYPH carries the gesture. An icon is
   vector and 19px square: scaling it is crisp at any factor, there is no text to
   resample, and a deep .82 dip is legible where .97 on the row would have been
   invisible. Down fast on the accelerating curve, back on the overshoot. */
.sidebar-link .nav-icon {
    color: var(--portal-muted);
    transition: color var(--mo-fast) var(--mo-ease),
                transform var(--mo-press-out) var(--mo-ease-press-out);
}
.sidebar-link:active .nav-icon,
.sidebar-link.is-pressing .nav-icon {
    transform: scale(.82);
    transition-duration: var(--mo-fast), var(--mo-press-in);
    transition-timing-function: var(--mo-ease), var(--mo-ease-press-in);
}
.sidebar-link:hover:not(.active) {
    background: var(--portal-hover);
    color: var(--portal-text);
}
.sidebar-link:hover:not(.active) .nav-icon {
    color: var(--portal-accent-soft-ink);
}
/* Active nav — a soft tinted highlight (not a strong solid plate). */
.sidebar-link.active {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
    box-shadow: none;
}
.sidebar-link.active .nav-icon {
    color: var(--portal-accent-soft-ink);
}
html.dark .sidebar-link.active {
    background-color: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
html.dark .sidebar-link.active .nav-icon {
    color: var(--portal-accent-soft-ink);
}
html:not(.dark) .sidebar-link.active:hover,
html:not(.dark) .sidebar-link.active:focus-visible,
html.dark .sidebar-link.active:hover,
html.dark .sidebar-link.active:focus-visible {
    background-color: var(--portal-accent-soft);
}
html:not(.dark) .sidebar-link.active:hover .nav-icon,
html:not(.dark) .sidebar-link.active:focus-visible .nav-icon,
html.dark .sidebar-link.active:hover .nav-icon,
html.dark .sidebar-link.active:focus-visible .nav-icon {
    color: var(--portal-accent-soft-ink);
}

.sidebar-collapse {
    border-radius: 11px;
    font-weight: 600;
}
html:not(.dark) .sidebar-collapse,
html.dark .sidebar-collapse {
    border: 1px solid var(--portal-border);
    background: var(--portal-surface);
    color: var(--portal-muted);
    box-shadow: var(--portal-shadow-sm);
}
html:not(.dark) .sidebar-collapse:hover,
html.dark .sidebar-collapse:hover {
    color: var(--portal-accent-soft-ink);
    border-color: var(--portal-accent);
    background: var(--portal-surface);
}

/* ---------------------------------------------------------------------------
   5. Topbar + breadcrumbs + balance chip + controls
   --------------------------------------------------------------------------- */
.topbar {
    /* Breadcrumb removed → flex so the control cluster pins to the right
       regardless of whether the (mobile-only) nav toggle is present. */
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--portal-surface);
    border-bottom: 1px solid var(--portal-border);
    min-height: 60px;
}
.topbar .topbar-actions {
    margin-left: auto;
}
/* The topbar is one of exactly TWO surfaces that keep a live backdrop-filter,
   because it is one of only two with genuinely moving content behind it. */
html.dark .topbar {
    background: var(--portal-glass-thin);
    border-bottom: 1px solid var(--portal-border);
    -webkit-backdrop-filter: blur(22px) saturate(165%);
    backdrop-filter: blur(22px) saturate(165%);
    box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .05);
}
/* portal.css deliberately makes the MOBILE topbar position:relative so dropdowns
   are not trapped in a stacking context — it scrolls away with the page, so
   there is nothing moving behind it to blur. Opaque fill there, or the mobile
   header becomes a weak unblurred wash. */
@media (max-width: 860px) {
    html.dark .topbar {
        background: var(--portal-chrome);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }
}
/* Header controls sit INSIDE the blurred topbar. Nesting backdrop-filter doubles
   the cost and goes milky, so they become lift-chips ON the glass instead.
   (Today --portal-glass over the --portal-surface topbar composited to EXACTLY
   #292a30 — 1.000:1. The balance chip was mathematically invisible and existed
   only by its border.) */
html.dark .balance-chip,
html.dark .language-switch.lang-row,
html.dark .theme-switch.lang-row {
    background-color: rgba(255, 255, 255, .07);
    background-image: none;
    border: 1px solid rgba(255, 255, 255, .12);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .10);
}
html.dark .balance-chip strong { color: var(--portal-financial-text); }
.breadcrumbs {
    font-size: 13px;
    color: var(--portal-muted);
}
.breadcrumbs strong {
    color: var(--portal-text);
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.01em;
}
.breadcrumbs b {
    color: var(--portal-muted);
    font-weight: 400;
    opacity: .6;
}

/* Glass header controls — translucent fill + backdrop blur (safe: the header has
   no dropdowns now — language is an inline row, settings is a plain link). */
/* All three header controls (balance chip, language row, settings cog) share one
   spec: 42px tall, pill radius, the same translucent glass + hairline border, so
   the cluster reads as a single, consistent set. */
.balance-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 42px;
    padding: 0 18px;
    background: var(--portal-glass);
    -webkit-backdrop-filter: blur(14px) saturate(150%);
    backdrop-filter: blur(14px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, .05);
    border-radius: 999px;
    box-shadow: var(--portal-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .04);
}
/* The base sheet ships `html.dark .balance-chip` (a green accent-soft fill) whose
   (0,1,1) specificity outranks the plain `.balance-chip` above — that's why the
   chip read as a different, green control next to the glass language row + cog.
   Re-assert the glass treatment at matching specificity so all three match. */
html.dark .balance-chip {
    /* Neutral glass + the drifting light sheen (matching the other controls);
       no emerald wash. The accent lives on the value, not the chrome. */
    background-color: var(--portal-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    border-color: rgba(255, 255, 255, .05);
    color: var(--portal-text);
}
html:not(.dark) .balance-chip {
    border-color: rgba(18, 32, 27, .10);
}
.balance-chip:hover {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-shadow);
}
.balance-chip span {
    color: var(--portal-muted);
    font-weight: 600;
    font-size: 12px;
}
.balance-chip strong {
    color: var(--portal-financial-text);
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.01em;
}

/* Header controls read as quiet neutral glass; no green-tinted edge. */
html:not(.dark) .balance-chip {
    border-color: rgba(18, 22, 28, .10);
}
.avatar-initial {
    background: var(--portal-accent);
    color: #fff;
    font-family: var(--portal-heading-font);
    font-weight: 700;
}
html.dark .avatar-initial {
    color: var(--portal-accent-ink);
}

.dropdown-menu {
    border: 1px solid var(--portal-border);
    border-radius: 14px;
    box-shadow: var(--portal-shadow-lg);
    background: var(--portal-surface);
}
.dropdown-user strong {
    font-family: var(--portal-heading-font);
    font-weight: 700;
}

/* Language selector — a glass segmented control: EN · 中文 · RU in one row.
   The theme switch (.theme-switch) reuses the exact same glass row + pills. */
.language-switch.lang-row,
.theme-switch.lang-row {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    min-height: 42px;
    /* more inner room so the active/focused pill's ring doesn't blend into the
       container's rounded border. */
    padding: 4px 6px;
    border-radius: 999px;
    background: var(--portal-glass);
    -webkit-backdrop-filter: blur(14px) saturate(150%);
    backdrop-filter: blur(14px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, .05);
    box-shadow: var(--portal-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .04);
}
html:not(.dark) .language-switch.lang-row,
html:not(.dark) .theme-switch.lang-row {
    border-color: rgba(18, 32, 27, .10);
}
/* Pills are scoped under .lang-row so they outrank the base sheet's
   `.language-switch a` / `a.active` rules (specificity 0,1,1 / 0,2,1), which were
   forcing a 6px-radius dark chip. A near-square chip sitting against the
   container's fully-rounded (999px) ends poked OUT past the arc — that was the
   "вылазит за пределы". A fully-rounded chip nests concentrically and stays in. */
.lang-row .lang-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 34px;
    min-height: 34px;
    min-width: 0;
    padding: 0 13px;
    border-radius: 999px;
    color: var(--portal-muted);
    text-decoration: none;
    font-weight: 700;
    font-size: 12px;
    letter-spacing: .01em;
    /* transition + press inherited from the shared motion contract (section 2). */
}
/* Flag chip — the 21 July geometry, which the owner chose over the twemoji one
   (2026-08-04): 18x12, small and quiet, and NO frame.
   No frame for two reasons. The catalog primitive needs its hairline because a
   flag there sits on a large pale artwork plate and dissolves into it without
   one; this pill is a small bounded surface with a single object in it. And a
   ring around the BOX stands off any artwork whose own corners are rounded
   differently, which is what made the previous version read as a picture in a
   frame.
   18x12 is 3:2 and the inline artwork is authored at 3:2 to match — this chip
   does not take the 4:3 image set. */
.lang-pill-flag {
    display: inline-flex;
    width: 18px;
    height: 12px;
    /* 2px — the landing point between two failures, both seen on screen.
       3px (July's value) is a 25% radius on a 12px chip: at that size a radius
       does not soften a corner, it amputates it, and the Union Jack's diagonals
       run straight INTO the corners, so they came out cut mid-stroke. July got
       away with it because its artwork had nothing in the corners — a plain
       tricolour, a plain red field, a symmetric X.
       1px then read as square.
       2px keeps every diagonal reaching its corner and still visibly rounds the
       edge. Checked at 6x in both themes at both breakpoints. */
    border-radius: 2px;
    overflow: hidden;
    flex: 0 0 auto;
    /* The hairline is back, and it is safe now for the reason it was not before.
       It was removed because twemoji artwork carries its OWN rounded, transparent
       corners, so a ring on the BOX stood off the picture and framed it. This
       artwork is flat and fills its box exactly, so the ring lands ON the edge.
       It earns its place in light theme: RU's top band is white against a white
       pill, and without an edge the tricolour reads as blue-and-red with the top
       third simply missing — checked at 1x, which is the only size that counts.
       Deliberately WEAKER than the catalog's --portal-flag-ring rather than
       reusing it at full strength: the catalog flag sits on a large pale plate
       and needs a firm edge, this one sits in a small pill and at full strength
       reads as a grey frame around the flag. ~55% of the token, still theme-aware
       so dark keeps its light ring instead of an invisible dark one. */
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--portal-flag-ring) 55%, transparent);
}
/* NOT desaturated. July also ran saturate(.45) brightness(.93); the owner asked
   for that treatment WITHOUT the muting, and it is the right call — .45 turns
   the PRC flag brown and the tricolour to mud, and three greyed rectangles in a
   row stop working as an orientation cue at all. */
/* img now, svg kept so the selector survives either source. The global flag
   primitive (img[src*="/flags/"]) sets its own 24px width and square corners;
   inside this chip the chip owns the geometry, so override both. */
.lang-pill-flag svg,
.lang-pill-flag img[src*="/flags/"] {
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    aspect-ratio: auto;
    border-radius: 0;
    object-fit: cover;
    box-shadow: none;
}
.lang-row .lang-pill:hover {
    color: var(--portal-text);
    background: var(--portal-hover);
}

.lang-row .lang-pill.active {
    color: var(--portal-text);
    background: var(--portal-accent-soft);
    box-shadow: inset 0 0 0 1px var(--portal-accent-ring);
}
/* The active pill is already marked by its accent-soft fill and accent edge —
   the flag inside does not also need a tinted ring, which was the same frame
   problem in accent colour. The flag used to lean in on hover, sit proud when
   current and dip on press too: three moving parts on a control whose whole job
   is "click the flag you want". */
/* Contained focus ring: the global `a:focus-visible` uses outline-offset:2px,
   which drew the ring OUTSIDE the row. Keep it inset for language pills. */
.lang-row .lang-pill:focus-visible {
    outline: none;
    box-shadow: inset 0 0 0 2px var(--portal-accent);
}

/* Theme switch — two icon pills (sun / moon) in the same glass row as the
   language switch. <button> reset so they match the <a> language pills; the
   active pill = the current theme, highlighted purely via the html.dark class
   (no JS → no flash on load). Hover / press reuse the .lang-pill behaviour. */
html .theme-switch .theme-pill {
    padding: 0 12px;
    background: transparent;
    border: 0;
    box-shadow: none;
    color: var(--portal-muted);
    cursor: pointer;
}
.theme-pill .theme-pill-svg {
    display: block;
    width: 16px;
    height: 16px;
}
html:not(.dark) .theme-switch .theme-pill[data-theme-set="light"],
html.dark .theme-switch .theme-pill[data-theme-set="dark"] {
    color: var(--portal-text);
    background: var(--portal-accent-soft);
    box-shadow: inset 0 0 0 1px var(--portal-accent-ring);
}

/* Sign-out row inside Settings (logout moved here from the header menu). */
.settings-logout-card {
    padding: 16px 18px;
}
.settings-logout-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.settings-logout-copy {
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.settings-logout-copy strong {
    font-family: var(--portal-heading-font);
    font-weight: 700;
    color: var(--portal-text);
}
.settings-logout-copy span {
    color: var(--portal-muted);
    font-size: 13px;
}
.settings-logout-row .logout-form {
    margin: 0;
    /* The base sheet's unscoped `.logout-form { border-top }` (a separator meant
       for the account-dropdown logout) leaked here as a stray light line above the
       button. Kill it for the settings card. */
    border-top: 0;
    padding-top: 0;
}

/* ---------------------------------------------------------------------------
   6. Surface ladder — hierarchy by line weight and depth, not one box for all.

   WAS: a single rule gave the SAME radius + 1px border + shadow to all 21
   selectors below, so a metric, a filter panel, a feed and a table read as the
   identical object — "everything is a card, nothing has hierarchy". NOW three
   explicit tiers:
     · Sheet (tier 1)      — solid, soft hairline, NO shadow. Lists / tables / feeds.
     · Panel (tier 2)      — solid, --portal-border, small shadow. Grouped summaries.
     · Instrument (tier 3) — glass + drifting sheen + accent top rule + long
                             shadow. ONE per screen (balance hero / marketplace pulse).
   The glass + sheen + blur that used to coat all of them (section 18) is now
   confined to tier 3, which also drops backdrop-blur off ~18 surfaces (a paint
   win — relevant to the "slow over the tunnel" reports). Class names stay the
   external hooks the repo contract requires; membership is set here, in CSS. */

/* Tier 1 — Sheet: held by a thin line, sitting flat on the page. */
.home-feed-card,
.ns-filter-grid,
.ns-table-wrap,
.security-card,
.sessions-card,
.ns-profile-card,
.mp-activity-card,
.mp-event-card,
.mp-channel-summary-card,
.mp-automation-card,
.mp-sales-trend-card,
.mp-product-performance-card,
.mp-customer-performance-card,
.mp-inbox-workload-card,
.mp-order-workload-card {
    border-radius: var(--portal-radius-xs);
    border: var(--portal-rule-w) solid var(--portal-border-soft);
    box-shadow: none;
}
/* Tier 2 — Panel: a grouped object that lifts a little off the sheet. */
.metric-card,
.quick-action-card,
.mp-overview-status-card,
.mp-account-health-card,
.messenger-layout {
    border-radius: var(--portal-radius-md);
    border: var(--portal-rule-w) solid var(--portal-border);
    box-shadow: var(--portal-shadow-sm);
}
/* Tier 3 — Instrument: the one surface per screen that floats and is allowed a
   line of accent along its top. (The balance hero adds its glass in section 17.) */
.mp-pulse-card {
    border-radius: var(--portal-radius);
    border: var(--portal-rule-w) solid var(--portal-border);
    box-shadow: var(--portal-shadow-lg);
}

/* ---------------------------------------------------------------------------
   7. Home — metric cards, the instrument hero, quick actions, activity feed
   --------------------------------------------------------------------------- */
.metric-card {
    position: relative;
    overflow: hidden;
    background: var(--portal-surface);
    padding: 20px 22px;
}
/* THE RIM — a lit top edge plus an outer ring, applied to the four plate types
   that make up most of the screen. This is the depth tool that replaces tinting:
   on a dark canvas a card stops sitting flat on the grey and starts catching the
   light. Pure box-shadow, so it costs nothing to scroll and carries no hue. */
.metric-card,
.quick-action-card,
.home-feed-card,
html .cat-tile {
    box-shadow: var(--portal-rim);
}
/* Cards are plain surfaces now — no corner graphic. */
/* Drop the base's green-sheen gradients — crisp surfaces. */
html:not(.dark) .wallet-card,
html:not(.dark) .metric-card.wallet-card,
html:not(.dark) .quick-action-card {
    background: var(--portal-surface);
    border-color: var(--portal-border);
}
.metric-card > span {
    color: var(--portal-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
}
/* Mobile: stack the label one word per line (БАЛАНС / КАБИНЕТА, ПОСЛЕДНЕЕ /
   ПОПОЛНЕНИЕ) so every card carries the same two-line column and the value and
   caption sit at identical offsets. word-spacing pushes each space past the
   line width. CENTER the label in its slot: a two-line RU label fills it either
   way, but a single-line CJK label («账户余额») used to bottom-align (flex-end)
   and sit glued to the number with a hole above — now it rests mid-slot. */
@media (max-width: 760px) {
    .metric-card > span {
        display: flex;
        align-items: center;
        min-height: 30px;
        word-spacing: 100vw;
    }
}
.metric-card > strong {
    font-family: var(--portal-font-figure);
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--portal-text);
}
/* All three home summary figures share one voice: same face, size, weight and
   colour — the balance is not visually heavier than its neighbours. */
.ns-home-summary .metric-card > strong,
.ns-home-summary .metric-card.wallet-card > strong {
    white-space: nowrap;
    max-width: 100%;
    overflow: hidden;
    font-weight: 600;
    letter-spacing: -.01em;
    color: var(--portal-financial-text);
}
.metric-card > small {
    color: var(--portal-muted);
    font-weight: 500;
}
/* The ACCOUNT card holds a login handle. Match the other metric cards' value
   treatment (same display face + weight) so the card reads as a complete,
   consistent member of the grid — just a touch smaller and clipped so a long
   login never overflows. */
.account-overview-card > strong {
    font-family: var(--portal-font-display);
    font-weight: 700;
    font-size: clamp(18px, 1.5vw, 23px);
    letter-spacing: -.012em;
    line-height: 1.1;
    font-variant-numeric: normal;
    font-feature-settings: normal;
    display: block;
    max-width: 100%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Section headings — the placeholder's quiet voice: a small, letterspaced
   uppercase micro-label, NOT a big bold sentence-case admin heading. Structure is
   carried by the LINE and the space (the way the welcome page carries it), not by
   heavy type. One shared language; add a page's section-heading container here as
   it is brought into the system (home, profile, …). */
.quick-actions-panel > h2,
.home-feed-head > h3,
.profile-section > h2 {
    margin: 0;
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--portal-muted);
    line-height: 1.3;
}
/* Top-level sections sit over a hairline rule (the sub-cards keep their own edge). */
.quick-actions-panel > h2,
.profile-section > h2 {
    padding-bottom: var(--portal-space-3);
    border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
}
.profile-section > h2 {
    margin-bottom: var(--portal-space-4);
}

.quick-actions-panel {
    display: grid;
    gap: 14px;
}
.home-financials {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

/* Let the financial feeds fill the remaining viewport height so the home page
   uses its vertical space instead of leaving a big empty area at the bottom.
   Scoped to the home page (the only `main` that contains `.home-financials`). */
main:has(.home-financials) {
    display: flex;
    flex-direction: column;
    gap: 14px;
    /* trim the legacy oversized bottom padding so the feeds reach the page end
       (mobile keeps its bottom-nav clearance via a !important rule). */
    padding-bottom: 32px;
}
/* Size the feed section to its content instead of stretching it to fill the
   viewport — the stretch left a tall band of empty (dark) space under short
   feeds. The page simply ends below the last row now. */
main:has(.home-financials) .home-financials {
    flex: 0 0 auto;
}
main:has(.home-financials) .home-activity-grid {
    flex: 0 0 auto;
    min-height: 0;
}
.quick-action-card {
    background: var(--portal-surface);
    padding: 20px;
}
.quick-action-card > span {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 13px;
    /* A quiet accent-soft chip — NOT the base's solid green gradient tile. Reset
       the base's accent border + green glow shadow so it reads as a subtle mark. */
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
    border: 0;
    box-shadow: none;
}
.quick-action-icon {
    width: 22px;
    height: 22px;
}
.quick-action-card strong {
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--portal-text);
}
.quick-action-card small {
    color: var(--portal-muted);
}

/* Activity feed — equal-height cards (explicit, so it never depends on the
   browser's default grid stretch), symmetric padding, centred empty state. */
.home-activity-grid {
    align-items: stretch;
}
.home-activity-grid > .home-feed-card {
    height: 100%;
}
.home-feed-card {
    position: relative;
    background: var(--portal-surface);
    padding: 0;
    display: flex;
    flex-direction: column;
}
/* Flex column (not grid): rows always pack at the top at their natural height
   and never vertically stretch — avoids a browser-dependent grid auto-row
   stretch that blew a single row up to fill the card. */
.home-feed-list {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
}
/* Header is its own zone, set apart from the body by a full-width divider. */
.home-feed-head {
    padding: 16px 20px 15px;
    margin: 0;
    border-bottom: 1px solid var(--portal-border);
}
.home-feed-head h3 {
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.01em;
    color: var(--portal-text);
}
.home-feed-head a {
    color: var(--portal-accent-soft-ink);
    font-weight: 600;
    font-size: 13px;
}
.home-feed-head a:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}
.home-feed-row {
    border-radius: 12px;
}
/* The balance-movement rows are links given a -12px OUTWARD bleed (margin-inline)
   tuned for the original larger card padding. With the tighter padding that bleed
   overflowed the rounded card and poked past its border on hover/press. Keep the
   highlight INSET and aligned with the header instead. */
.home-financials .home-feed-link {
    margin-inline: 0;
    padding-inline: 14px;
}
/* Owner, 2026-07-14 (third pass, FINAL): the mobile TILE style is the canon on
   EVERY viewport — each operation its own outlined plaque («чтобы они были
   обведены»), no hairline-divided flat ledger (that was «наслоение»). Desktop
   keeps the richer row content (the #DX ref etc. — «места хватает»); mobile
   still trims it in its own block below. */
.home-financials .home-feed-list {
    gap: 8px;
    padding: 12px 14px 14px;
}
.home-financials .home-feed-row,
.home-financials .home-feed-row + .home-feed-row {
    background: var(--portal-surface-soft);
    border: 1px solid var(--portal-border);
    border-radius: 12px;
    animation: none !important;
}
.home-financials .home-feed-link {
    padding-block: 12px;
}
/* The neutral order-cart chip shares the tile fill (surface-soft on
   surface-soft) — give it a contrasting plate + ring so its circle reads;
   tinted movement arrows get the matching ring treatment. */
html .home-feed-icon,
html .home-feed-icon.order,
html.dark .home-feed-icon,
html.dark .home-feed-icon.order {
    background: var(--portal-surface);
    border: 1px solid var(--portal-border);
}
html .home-feed-icon.movement.positive,
html.dark .home-feed-icon.movement.positive {
    background: var(--portal-good-soft);
    border: 1px solid color-mix(in srgb, var(--portal-good) 35%, transparent);
}
html .home-feed-icon.movement.negative,
html.dark .home-feed-icon.movement.negative {
    background: color-mix(in srgb, var(--portal-danger) 16%, transparent);
    border: 1px solid color-mix(in srgb, var(--portal-danger) 35%, transparent);
}
.home-feed-icon {
    width: 38px;
    height: 38px;
    border-radius: 11px;
    display: grid;
    place-items: center;
}
/* Override the base sheet's hardcoded light mint/red fills (e.g. #e9fbf2), which
   read as near-white blobs on the dark graphite. Orders are a neutral chip; only
   money movement carries colour — green in, red out. */
.home-feed-icon,
.home-feed-icon.order {
    background: var(--portal-surface-soft);
    color: var(--portal-muted);
}
.home-feed-icon.positive,
.home-feed-icon.movement.positive {
    background: var(--portal-good-soft);
    color: var(--portal-good);
}
.home-feed-icon.negative,
.home-feed-icon.movement.negative {
    background: color-mix(in srgb, var(--portal-danger) 16%, transparent);
    color: var(--portal-danger);
}
/* The base re-tints these under html.dark with its OLD teal accent
   (rgba(47,125,116,.18)) at higher specificity (html.dark .home-feed-icon.order),
   which leaked GREEN onto the order chip and the positive-money chip. Re-assert
   the neutral / semantic fills at matching specificity. */
html.dark .home-feed-icon,
html.dark .home-feed-icon.order {
    background: var(--portal-surface-soft);
    color: var(--portal-muted);
}
html.dark .home-feed-icon.positive,
html.dark .home-feed-icon.movement.positive {
    background: var(--portal-good-soft);
    color: var(--portal-good);
}
html.dark .home-feed-icon.negative,
html.dark .home-feed-icon.movement.negative {
    background: color-mix(in srgb, var(--portal-danger) 16%, transparent);
    color: var(--portal-danger);
}
/* Feed chips — a small, clearly-defined tile (not the old chunky coloured circle,
   but not a near-invisible hairline either: the transparent + border-soft version
   vanished in dark). A solid surface fill and a visible --portal-border give the
   icon a crisp container; money direction reads from the GLYPH colour (green in /
   red out). Placed after the fills above (equal specificity → wins). */
html .home-feed-icon,
html.dark .home-feed-icon,
html .home-feed-icon.order,
html.dark .home-feed-icon.order,
html .home-feed-icon.positive,
html.dark .home-feed-icon.positive,
html .home-feed-icon.negative,
html.dark .home-feed-icon.negative,
html .home-feed-icon.movement.positive,
html.dark .home-feed-icon.movement.positive,
html .home-feed-icon.movement.negative,
html.dark .home-feed-icon.movement.negative {
    width: 34px;
    height: 34px;
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
    border: var(--portal-rule-w) solid var(--portal-border);
}
html .home-feed-icon,
html.dark .home-feed-icon,
html .home-feed-icon.order,
html.dark .home-feed-icon.order {
    color: var(--portal-muted);
}
html .home-feed-icon.positive,
html.dark .home-feed-icon.positive,
html .home-feed-icon.movement.positive,
html.dark .home-feed-icon.movement.positive {
    color: var(--portal-good);
}
html .home-feed-icon.negative,
html.dark .home-feed-icon.negative,
html .home-feed-icon.movement.negative,
html.dark .home-feed-icon.movement.negative {
    color: var(--portal-danger);
}
/* The base's generic `button` rule glows TEAL (box-shadow rgba(47,125,116,.18)),
   which leaks a green halo onto neutral glass toggles that don't override it:
   the mobile nav-toggle and the status/date filter dropdowns. Neutralise it. */
html .nav-toggle,
html .nav-toggle:hover,
html .nav-toggle:focus-visible,
html .portal-select-button,
html .portal-select-button:hover,
html .portal-select-button:focus-visible,
html .portal-select[data-dropdown].open .portal-select-button,
html .portal-date-button,
html .portal-date-button:hover,
html .portal-date-button:focus-visible {
    box-shadow: var(--portal-shadow-sm);
}
.home-feed-copy strong {
    font-weight: 600;
    color: var(--portal-text);
}
.home-feed-copy small {
    color: var(--portal-muted);
}
.amount.positive,
.home-feed-row .amount.positive {
    color: var(--portal-good);
    font-weight: 700;
}
.amount.negative {
    color: var(--portal-danger);
    font-weight: 700;
}
/* Balance-movement amounts read like the transactions ledger — a lighter,
   slightly smaller Inter figure, not a heavy headline number. */
.home-feed-row .amount {
    font-size: 14px;
}
/* Right column of a movement row: the amount with the running balance under it. */
.home-feed-right {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 2px;
    min-width: 0;
}
.home-feed-right .home-feed-after {
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.home-feed-ref,
.home-feed-bit {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    display: inline-block;
}
.home-feed-row .amount.positive,
.home-feed-row .amount.negative,
.home-feed-row .amount.neutral {
    font-weight: 500;
}
.metric-empty {
    color: var(--portal-muted);
}
/* Fill the body BELOW the header and centre the message there. In-flow (not
   absolute) with its own min-height so an empty card can't collapse to header
   height and let the centred text overlap the title. */
.home-feed-empty {
    flex: 1 1 auto;
    min-height: 120px;
    display: grid;
    place-items: center;
    text-align: center;
    color: var(--portal-muted);
    padding: 24px;
}

/* ---------------------------------------------------------------------------
   8. Buttons, inputs, selects, filters
   --------------------------------------------------------------------------- */
button,
.tool-link {
    font-family: var(--portal-font-ui);
}
/* primary action = the brand gradient («основной градиент»). It used to be inked
   graphite because the old indigo accent was too loud for a full-width CTA; the
   forest accent is dark enough to carry it, so the primary button IS the brand. */
.login-form button,
.security-step-next,
.portal-modal button[type="submit"] {
    background: var(--portal-accent-gradient);
    color: var(--portal-accent-ink);
    border-radius: 12px;
    font-weight: 700;
    letter-spacing: -.005em;
    box-shadow: var(--portal-shadow-sm);
}
.login-form button:hover,
.security-step-next:hover,
.portal-modal button[type="submit"]:hover {
    filter: brightness(1.06);
    box-shadow: var(--portal-shadow);
}
html.dark .login-form button,
html.dark .security-step-next {
    background: var(--portal-accent-gradient);
    color: var(--portal-accent-ink);
}

/* One field shell for the whole site — every text input, select, search box,
   custom dropdown and date picker rests on the same soft surface + hairline and
   blooms the same emerald halo on focus. The message composer (the reference
   the rest of the UI echoes) shares the exact same focus language below. */
input,
select,
.ns-filter-grid input,
.ns-filter-grid select,
.ns-id-input,
.messenger-search input,
.catalog-search input,
.portal-select-button,
.portal-date-button {
    border-radius: var(--portal-radius-sm);
    border: 1px solid var(--portal-border);
    background: var(--portal-surface-soft);
    color: var(--portal-text);
    transition:
        border-color var(--mo-fast) var(--mo-ease),
        box-shadow var(--mo-fast) var(--mo-ease),
        background-color var(--mo-fast) var(--mo-ease),
        transform var(--mo-fast) var(--mo-out);
}
input:focus,
select:focus,
.ns-filter-grid input:focus,
.ns-filter-grid select:focus,
.ns-id-input:focus,
.catalog-search input:focus,
.portal-select-button:focus-visible,
.portal-date-button:focus-visible,
.messenger-search input:focus,
.messenger-reply:focus-within .messenger-field {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
    background: var(--portal-surface);
}
.ns-filter-grid label {
    color: var(--portal-muted);
    letter-spacing: .1em;
    font-weight: 700;
}

.portal-select-button,
.portal-date-button {
    font-weight: 500;
    color: var(--portal-text);
}
.portal-select-button:hover,
.portal-date-button:hover {
    border-color: var(--portal-accent);
}
.portal-select-menu,
.portal-date-menu,
.dropdown-menu {
    border-radius: 13px;
    border: 1px solid var(--portal-border);
    box-shadow: var(--portal-shadow-lg);
    background: var(--portal-surface);
}
.dropdown-option.active,
.portal-select-menu .active {
    color: var(--portal-accent-soft-ink);
}
/* Datepicker "Clear" row: a quiet full-width reset under the calendar grid,
   shown only while a day is selected (so the range filter can be lifted). */
.portal-date-menu .cal-foot {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--portal-border);
}
.portal-date-menu .cal-clear {
    width: 100%;
    padding: 7px 10px;
    border: 1px solid var(--portal-border);
    border-radius: 9px;
    background: var(--portal-surface);
    color: var(--portal-muted);
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
    transition: border-color var(--portal-mo-fast) var(--portal-ease),
        color var(--portal-mo-fast) var(--portal-ease);
}
.portal-date-menu .cal-clear:hover {
    border-color: var(--portal-danger);
    color: var(--portal-danger);
}

.ghost-button {
    border: 1px solid var(--portal-border);
    border-radius: 11px;
    background: var(--portal-surface);
    color: var(--portal-text);
    font-weight: 600;
}
.ghost-button:hover {
    border-color: var(--portal-accent);
    color: var(--portal-accent-soft-ink);
}
.ghost-danger {
    border-radius: 11px;
}
/* The base sheet's generic `button` rule ships a teal glow
   (box-shadow rgba(47,125,116,.18)) that read as a green aura around the
   red-outlined logout button. Neutral resting shadow; the hover/focus halo is
   danger-tinted to match the button's own voice. */
html .ghost-danger,
html.dark .ghost-danger {
    box-shadow: var(--portal-shadow-sm);
}
html .ghost-danger:hover,
html.dark .ghost-danger:hover {
    box-shadow: 0 8px 18px color-mix(in srgb, var(--portal-danger) 18%, transparent);
}
html .ghost-danger:focus-visible,
html.dark .ghost-danger:focus-visible {
    outline: 2px solid var(--portal-danger);
    outline-offset: 2px;
    box-shadow: none;
}

/* ---------------------------------------------------------------------------
   9. Tables (orders / transactions / marketplace)
   --------------------------------------------------------------------------- */
.ns-table-wrap,
.mp-account-health-wrap,
.mp-channel-summary-wrap,
.mp-automation-wrap,
.mp-sales-trend-wrap,
.mp-product-performance-wrap,
.mp-customer-performance-wrap {
    background: var(--portal-surface);
    border-radius: var(--portal-radius);
}
.ns-data-table th {
    color: var(--portal-muted);
    letter-spacing: .08em;
    text-transform: uppercase;
    font-weight: 700;
    /* No solid fill, and a THIN header rule (not the 1.5px strong one — that bold
       line read as a doubled edge). The header reads from its uppercase labels
       over a plain hairline, the same weight as the row separators. */
    background: transparent;
    border-bottom: var(--portal-rule-w) solid var(--portal-border);
}
.ns-data-table td {
    color: var(--portal-text);
}
/* iOS Safari won't clip a scroll container's content to border-radius, so a framed
   horizontally-scrollable table shows a seam at the rounded corner (the persistent
   "double edge"). Fix WITHOUT losing the frame: the wrap clips its rounded corner
   (overflow:hidden) and an inner layer does the horizontal scroll. Only wraps that
   carry the inner .ns-table-scroll opt in (via :has); others are unaffected. */
.ns-table-wrap:has(> .ns-table-scroll) { overflow: hidden; }
.ns-table-scroll { overflow-x: auto; overscroll-behavior: none; }
/* Empty-state row ("Нет записей"): the base `.empty` forces a fixed 56px box,
   leaving the Orders empty row taller than a normal data row. Drop the fixed
   height so an empty table reads at the same row height as a populated one
   (e.g. the Transactions rows). */
.ns-data-table td.empty {
    height: auto;
}
/* Pointer-only: on touch iOS keeps :hover on the row under the finger, which
   reads as a row selected by nobody (the base sheet's tr:hover rules are guarded
   the same way). */
@media (hover: hover) and (pointer: fine) {
    /* Row highlight lives in the LINE, not a fill (the "Rule & Light" hand): the
       hovered row is bracketed top and bottom in accent-line, drawn on the TD —
       a reliable box, unlike a <tr> under border-collapse — so nothing is tinted
       under the numbers and the layout never shifts. */
    .ns-data-table tbody tr.ns-row-link td {
        transition: border-color var(--mo-fast) var(--mo-ease),
                    box-shadow var(--mo-fast) var(--mo-ease);
    }
    .ns-data-table tbody tr.ns-row-link:hover {
        background: transparent;
    }
    .ns-data-table tbody tr.ns-row-link:hover td {
        border-bottom-color: var(--portal-accent-line);
        box-shadow: inset 0 1px 0 0 var(--portal-accent-line);
    }
}
.ns-data-table .amount.positive,
.txn-amount.positive {
    color: var(--portal-good);
}
.txn-amount.danger {
    color: var(--portal-danger);
}
.ns-pagination button {
    border-radius: 10px;
    border: 1px solid var(--portal-border);
    background: var(--portal-surface);
    color: var(--portal-text);
    font-weight: 600;
}
.ns-pagination button:hover:not(:disabled) {
    border-color: var(--portal-accent);
    color: var(--portal-accent-soft-ink);
}

/* ---------------------------------------------------------------------------
   10. Marketplaces — tab bar, metrics, pulse
   --------------------------------------------------------------------------- */
.mp-tabbar {
    background: var(--portal-surface);
    border: 1px solid var(--portal-border);
    border-radius: 999px;
    box-shadow: var(--portal-shadow-sm);
    padding: 5px;
    gap: 4px;
    width: max-content;
}
.mp-tab {
    border-radius: 999px;
    color: var(--portal-muted);
    font-weight: 600;
    padding: 8px 16px;
}
.mp-tab:hover {
    color: var(--portal-text);
}
.mp-tab.active {
    background: var(--portal-accent);
    color: #fff;
    box-shadow: 0 6px 16px -10px var(--portal-accent-ring);
}
html.dark .mp-tab.active {
    color: var(--portal-accent-ink);
}
.mp-tab.active .mp-tab-dot {
    background: currentColor;
    opacity: 1;
}
.mp-tab-dot.ok {
    background: var(--portal-good);
}
.mp-metric strong,
.mp-pulse-cell > strong {
    font-family: var(--portal-font-figure);
    font-weight: 800;
    letter-spacing: -.02em;
    color: var(--portal-text);
}
.mp-pulse-cell > span,
.mp-metric span:first-child {
    color: var(--portal-muted);
    letter-spacing: .1em;
    text-transform: uppercase;
    font-size: 11px;
    font-weight: 600;
}
.mp-chip {
    border-radius: 999px;
    border: 1px solid var(--portal-border);
    background: var(--portal-surface-soft);
    color: var(--portal-muted);
    font-weight: 600;
}
.mp-table-link:hover {
    text-decoration: underline;
    text-underline-offset: 3px;
}

/* ---------------------------------------------------------------------------
   11. Inbox / messenger
   --------------------------------------------------------------------------- */
/* Conversation list */
.messenger-thread.active {
    background: var(--portal-accent-soft);
    box-shadow: inset 3px 0 0 var(--portal-accent);
}
.messenger-thread:hover {
    background: var(--portal-hover);
}
.messenger-thread-buyer {
    font-weight: 700;
    color: var(--portal-text);
}
.messenger-dot {
    background: var(--portal-accent);
}
.messenger-avatar {
    border-radius: 12px;
}
.messenger-search input {
    border-radius: 999px;
}
.messenger-unread-toggle {
    border-radius: 999px;
    border: 1px solid var(--portal-border);
    font-weight: 600;
}
.messenger-unread-toggle.active {
    background: var(--portal-accent);
    border-color: var(--portal-accent);
    color: #fff;
}
html.dark .messenger-unread-toggle.active {
    color: var(--portal-accent-ink);
}

/* Message bubbles — compact, defined, with quiet timestamps. */
.messenger-messages {
    padding: 18px 20px;
    gap: 5px;
}
.messenger-bubble {
    max-width: 70%;
    padding: 9px 13px 7px;
    border-radius: 16px;
    box-shadow: var(--portal-shadow-sm);
    line-height: 1.45;
}
.messenger-bubble.in {
    background: var(--portal-surface-soft);
    border: 1px solid var(--portal-border-soft);
    border-bottom-left-radius: 5px;
}
.messenger-bubble.out {
    background: var(--portal-accent-soft);
    border: 1px solid transparent;
    border-bottom-right-radius: 5px;
}
.messenger-bubble time {
    margin-top: 2px;
    font-size: var(--portal-text-xs);
    opacity: .5;
    text-align: right;
}
.messenger-bubble.in time {
    text-align: left;
}
.messenger-bubble.out time {
    color: var(--portal-accent-soft-ink);
    opacity: .65;
}

/* Composer — the armed send button picks up the accent with a gentle lift. */
.messenger-field {
    border-radius: 22px;
    box-shadow: var(--portal-shadow-sm);
}
.messenger-reply .messenger-send:enabled {
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
}
.messenger-reply .messenger-send:enabled:hover {
    filter: brightness(1.06);
}

/* Glass + translucency across the messenger chrome (standard glass material).
   The layout owns the backdrop blur; nested controls use the translucent glass
   fill so they layer over it without a second (muddy) blur pass. */
html .messenger-layout {
    /* PERF: was blur(16px) over the whole scrolling conversation. --portal-glass is
       ~.9 alpha (messages are opaque anyway), so a heavier near-opaque fill reads as
       the same frosted panel without a full-panel backdrop blur repainting on scroll. */
    background-color: color-mix(in srgb, var(--portal-glass) 94%, var(--portal-surface));
    border: 1px solid rgba(255, 255, 255, .08);
    box-shadow: var(--portal-shadow), inset 0 1px 0 rgba(255, 255, 255, .05);
}
html:not(.dark) .messenger-layout {
    border-color: rgba(18, 32, 27, .08);
}
.messenger-search input,
.messenger-field {
    background: var(--portal-glass);
    border-color: rgba(255, 255, 255, .10);
}
html:not(.dark) .messenger-search input,
html:not(.dark) .messenger-field {
    border-color: rgba(18, 32, 27, .10);
}
/* Clickable buyer name → their marketplace profile. */
.messenger-head-name-link {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--mo-fast) var(--mo-ease);
}
/* The ↗ glyph at rest signals the name opens an external profile (clickable). */
.messenger-head-name-ext {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
    color: var(--portal-muted);
    transition: color var(--mo-fast) var(--mo-ease);
}
.messenger-head-name-link:hover,
.messenger-head-name-link:hover .messenger-head-name-ext {
    color: var(--portal-accent-soft-ink);
}
/* Marker on messages sent automatically (bot auto-reply / auto-delivery). */
.messenger-auto-tag {
    display: block;
    margin-top: 4px;
    font-size: var(--portal-text-xs);
    font-weight: 700;
    letter-spacing: .03em;
    color: var(--portal-warn);
    opacity: .92;
    text-transform: lowercase;
}

/* Buyer info aside */
.messenger-info-title {
    color: var(--portal-muted);
    letter-spacing: .12em;
}
.messenger-buyer-card dd {
    color: var(--portal-text);
}

/* Empty state — a calm, premium "no conversation selected" view: a soft glowing
   mark wrapped in a faint orbit ring (the globe/wprovider motif of the portal). */
.messenger-empty-state {
    gap: 10px;
    max-width: 340px;
}
.messenger-empty-icon {
    position: relative;
    width: 72px;
    height: 72px;
    background: radial-gradient(circle at 50% 42%, var(--portal-accent-soft) 0%, rgba(0, 0, 0, 0) 72%);
    color: var(--portal-accent-soft-ink);
    box-shadow: 0 16px 36px -18px var(--portal-accent-ring);
    margin-bottom: 14px;
}
.messenger-empty-icon::after {
    content: "";
    position: absolute;
    inset: -13px;
    border-radius: 50%;
    border: 1px solid var(--portal-border);
    opacity: .65;
}
.messenger-empty-icon svg {
    width: 30px;
    height: 30px;
}
.messenger-empty-title {
    font-size: 17px;
    font-weight: 700;
}
.messenger-empty-sub {
    font-size: var(--portal-text-base);
}

/* ---------------------------------------------------------------------------
   12. Settings / profile / sessions
   --------------------------------------------------------------------------- */
.settings-list .settings-row {
    border-radius: var(--portal-radius-sm);
    border: 1px solid var(--portal-border);
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
}
.settings-list .settings-row:hover {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-shadow);
}
.settings-row-label {
    color: var(--portal-text);
    font-weight: 600;
}
.settings-row-value {
    color: var(--portal-muted);
}
.session-row .session-current {
    color: var(--portal-accent-soft-ink);
    font-weight: 700;
}
.session-device-icon,
.session-icon-svg {
    color: var(--portal-muted);
}

/* Profile — glass identity hero + readout rows (phase 7). Every value is a
   --portal-* token, so light/dark come free and there is no fixed colour to
   pair; the cards themselves join the glass group above (.ns-profile-card /
   .metric-card / .security-card), so blur + drift + reduced-motion are already
   handled there — nothing new to re-disable. */
.profile-identity-section {
    gap: var(--portal-space-4);
}
.profile-identity {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--portal-space-4);
    padding: var(--portal-space-6);
}
.profile-avatar.avatar-initial {
    width: 52px;
    height: 52px;
    font-size: var(--portal-text-2xl);
    color: var(--portal-accent-ink);
    flex: none;
}
.profile-identity-copy {
    display: grid;
    gap: var(--portal-space-1);
    flex: 1 1 12rem;
    min-width: 0;
}
.profile-identity-name {
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-xl);
    font-weight: var(--portal-weight-bold);
    letter-spacing: -.012em;
    color: var(--portal-text);
    overflow-wrap: anywhere;
}
.profile-identity-handle,
.profile-identity-line {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    overflow-wrap: anywhere;
}
.profile-identity-badges {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--portal-space-2);
}
.profile-stat-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--portal-space-4);
}
.profile-overview-card {
    display: grid;
    gap: var(--portal-space-5);
    padding: var(--portal-space-6);
}
.profile-readouts {
    display: grid;
    gap: var(--portal-space-3);
}
.profile-readout {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--portal-space-4);
}
.profile-readout > span {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
}
.profile-readout-value {
    color: var(--portal-text);
    font-weight: var(--portal-weight-semibold);
    text-align: right;
    overflow-wrap: anywhere;
}
.profile-manage-link {
    justify-self: start;
}
.profile-revoke-all {
    display: flex;
    justify-content: flex-end;
    margin-top: var(--portal-space-3);
}

/* ---------------------------------------------------------------------------
   13. Pills, flash messages
   --------------------------------------------------------------------------- */
.pill {
    border-radius: 999px;
    font-weight: 700;
    letter-spacing: .01em;
}
.pill.good {
    background: var(--portal-good-soft);
    color: var(--portal-good);
}
html.dark .pill.good {
    color: var(--portal-good);
}
/* Flash toasts read as standard portal plaques — a neutral surface card with a
   small semantic icon chip — instead of the old solid green/red tinted band. */
.flash {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-radius: 14px;
    border: 1px solid var(--portal-border);
    background: var(--portal-surface);
    color: var(--portal-text);
    box-shadow: var(--portal-shadow);
    font-weight: 600;
    font-size: var(--portal-text-base);
}
.flash-icon {
    flex: none;
    width: 34px;
    height: 34px;
    border-radius: 10px;
    display: grid;
    place-items: center;
}
.flash-icon svg {
    width: 17px;
    height: 17px;
}
.flash.success .flash-icon {
    background: var(--portal-good-soft);
    color: var(--portal-good);
}
.flash.danger .flash-icon {
    background: color-mix(in srgb, var(--portal-danger) 14%, transparent);
    color: var(--portal-danger);
}
.flash-copy {
    display: grid;
    gap: 4px;
    min-width: 0;
    flex: 1;
}
.flash-copy strong {
    font-weight: 600;
    color: var(--portal-text);
}
.flash-action {
    width: fit-content;
    color: var(--portal-accent-soft-ink);
    text-decoration: none;
    font-weight: 700;
    font-size: var(--portal-text-base);
}
.flash-action:hover {
    text-decoration: underline;
}
/* html-prefixed so the quiet close outranks the base solid-button rules. A fixed
   square is essential: the base sheet stretches bare <button>s to full width, which
   otherwise squeezed the copy to 0 and wrapped the text one word per line. Sits in
   the top-right corner (standard toast) with a proper × glyph. */
html .flash-close,
html.dark .flash-close {
    flex: none;
    align-self: flex-start;
    appearance: none;
    display: grid;
    place-items: center;
    width: 30px;
    min-width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--portal-muted);
    opacity: .8;
    cursor: pointer;
    box-shadow: none;
    transition: background-color var(--mo-fast) var(--mo-ease), color var(--mo-fast) var(--mo-ease), opacity var(--mo-fast) var(--mo-ease);
}
html .flash-close svg {
    width: 15px;
    height: 15px;
}
html .flash-close:hover,
html.dark .flash-close:hover {
    opacity: 1;
    background: var(--portal-hover);
    color: var(--portal-text);
}
/* Neutral surface for both tones (beats the base sheet's tinted panels); the
   icon chip alone carries the green/red semantics. */
.flash.success,
.flash.danger,
html.dark .flash.success,
html.dark .flash.danger {
    background: var(--portal-surface);
    color: var(--portal-text);
    border-color: var(--portal-border);
}

/* ---------------------------------------------------------------------------
   14. Login — balanced centred card (frosted glass, text wordmark)
   --------------------------------------------------------------------------- */
/* Flex-centre the card. The old grid shell had a single auto-sized track that
   collapsed to the panel's content width (~266px) — that was the "squished"
   card. Flex + an explicit panel width keeps it a comfortable 420px. */
.login-shell {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 100dvh;
    padding: clamp(20px, 5vw, 56px);
    /* Login is a static screen — cancel the global `main` rise/fade-in so the
       card just appears, with no movement (also keeps language swaps seamless). */
    animation: none;
}
/* Plain frosted glass: translucent fill + blur + hairline border only. No white
   sheen overlay, no inner highlight, no accent top-bar — just the glass. Wider
   (560px) so the card reads horizontal rather than portrait. */
.login-panel {
    width: min(560px, 100%);
    max-width: none;
    display: grid;
    gap: 20px;
    /* Symmetric top/bottom padding so the card has equal breathing room above the
       wordmark and below the language row. */
    padding: 34px clamp(28px, 5vw, 44px);
    border-radius: 22px;
    border: 1px solid rgba(255, 255, 255, .05);
    background: var(--portal-glass);
    -webkit-backdrop-filter: blur(26px) saturate(155%);
    backdrop-filter: blur(26px) saturate(155%);
    box-shadow: var(--portal-shadow-lg);
    position: relative;
    overflow: hidden;
}
.login-brand {
    display: flex;
    justify-content: center;
    margin: 0;
}
.login-brand-word {
    font-family: var(--portal-heading-font);
    font-weight: 800;
    /* Bigger, more substantial wordmark — it anchors the card now that there's no
       "Welcome back" heading above the form. */
    font-size: 34px;
    letter-spacing: -.03em;
    color: var(--portal-text);
}
.login-head {
    display: flex;
    flex-direction: column;
    gap: 6px;
    text-align: center;
}
.login-head h1 {
    font-family: var(--portal-heading-font);
    font-size: 25px;
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1.1;
    color: var(--portal-text);
    margin: 0;
}
.login-head p {
    margin: 0;
    font-size: 14px;
    color: var(--portal-muted);
}
.login-form {
    display: grid;
    gap: 18px;
}
.login-form label {
    display: grid;
    gap: 8px;
}
.login-form label span {
    color: var(--portal-muted);
    font-weight: 700;
    letter-spacing: .09em;
    font-size: 11px;
    text-transform: uppercase;
}
.login-form input {
    border-radius: 13px;
    min-height: 50px;
    padding: 0 16px;
    /* Standard input text: the wrapping <label> is bold-uppercase (weight 750) and
       the input was inheriting that via `font: inherit`, so typed text looked
       heavy/condensed. Force a normal UI weight + family. */
    font-family: var(--portal-font-ui);
    font-size: 16px;
    font-weight: 400;
    letter-spacing: normal;
    background: var(--portal-surface-soft);
    border: 1px solid var(--portal-border);
}
.login-form input:focus {
    border-color: var(--portal-accent);
    box-shadow: 0 0 0 3px var(--portal-accent-ring);
    outline: none;
}
.login-form button {
    margin-top: 6px;
    min-height: 50px;
    font-size: 15px;
    font-weight: 700;
    border-radius: 13px;
}
/* Field hints on the invite sign-up: what a code is, and that a lost password
   cannot be mailed back on a platform with no mail. Quiet enough not to compete
   with the label above it. */
.login-form label small {
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: 12px;
    font-weight: 400;
    letter-spacing: normal;
    text-transform: none;
}
/* The invite-only notice above the sign-up form. Reads as a statement of fact,
   not an error, so it borrows the soft surface rather than a flash colour. */
.register-closed {
    display: grid;
    gap: 6px;
    margin-bottom: 20px;
    padding: 14px 16px;
    border: 1px solid var(--portal-border);
    border-radius: 13px;
    background: var(--portal-surface-soft);
}
.register-closed strong {
    font-size: 14px;
    font-weight: 700;
    color: var(--portal-text);
}
.register-closed span {
    font-size: 13px;
    line-height: 1.5;
    color: var(--portal-muted);
}
/* The invite code itself: monospace and wide-tracked so O/0-style misreads are
   impossible when it is dictated or retyped. */
.login-form input[name="invite_code"] {
    font-family: var(--portal-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
    font-size: 17px;
    letter-spacing: .16em;
    text-transform: uppercase;
}
/* Language row sits centred under the form, with a hairline divider so it reads
   as a distinct footer rather than crowding the button. */
.login-controls {
    justify-content: center;
    margin-top: 4px;
    padding-top: 22px;
    border-top: 1px solid var(--portal-border-soft);
}
/* Centre the language row (a stray margin-left:auto from the base sheet was
   pinning it to the right edge). */
.login-controls .language-switch {
    margin-left: 0;
}

/* ---------------------------------------------------------------------------
   15. Service state — THE one placeholder for every non-content view:
   "page under development", access-locked, no profile, top-up-via-operator,
   errors/404. One centred block: a quiet muted glyph (no disc — the calm
   original look), a heading, a subtitle and an optional action, so all of them
   read as one family. `.wip-page` is an alias so legacy callers/tests match.
   --------------------------------------------------------------------------- */
.ns-state,
.wip-page {
    display: grid;
    justify-items: center;
    align-content: center;
    gap: var(--portal-space-3);
    /* Fill the viewport area below the topbar so the block is truly centred. */
    min-height: calc(100vh - 152px);
    text-align: center;
    padding: var(--portal-space-6);
}
/* A bare, quiet muted glyph — the calm grey mark of the original placeholder,
   not a tinted disc. Static (no hover/press). */
.ns-state-icon {
    display: grid;
    place-items: center;
    color: var(--portal-muted);
    opacity: .5;
    margin: 0 0 var(--portal-space-1);
}
.ns-state-icon svg { width: 48px; height: 48px; display: block; }
/* Error tone: the same glyph in the danger key. */
.ns-state--error .ns-state-icon { color: var(--portal-danger); opacity: .6; }
.ns-state h1,
.wip-page h1 {
    font-size: clamp(22px, 1.9vw, 28px);
    font-weight: 600;
    color: var(--portal-text);
    margin: 0;
}
.ns-state p,
.wip-page p {
    color: var(--portal-muted);
    max-width: 44ch;
    margin: 0;
    line-height: 1.5;
}
.ns-state-action {
    margin-top: var(--portal-space-2);
    display: inline-flex;
    gap: var(--portal-space-2);
}
/* The action is a link styled as a ghost button; the base .ghost-button was
   built for <button> and does not centre its text as an <a>, which read as a
   lopsided pill. Centre it on both axes here. */
.ns-state-action .ghost-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    min-height: 42px;
    padding: 0 var(--portal-space-5);
}
/* Compact: the same block without the full-viewport centring, for use inside a
   dialog or a narrow column (e.g. the top-up modal's operator fallback). */
.ns-state--compact {
    min-height: 0;
    gap: var(--portal-space-2);
    padding: var(--portal-space-4) var(--portal-space-2);
}
.ns-state--compact .ns-state-icon svg { width: 40px; height: 40px; }
/* Legacy bare glyph — kept for any caller still emitting it. */
.wip-page-icon {
    width: 42px;
    height: 42px;
    color: var(--portal-muted);
    opacity: .5;
    margin: 0 0 4px;
}

/* ---------------------------------------------------------------------------
   16. Page enter + reduced motion + scrollbars
   --------------------------------------------------------------------------- */
main {
    animation: wprovider-rise var(--mo-slow) var(--mo-out) both;
}
@keyframes wprovider-rise {
    from { opacity: 0; transform: translateY(7px); }
    to { opacity: 1; transform: translateY(0); }
}
* {
    scrollbar-color: var(--portal-border) transparent;
}
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
    }
    /* Null the press transform at the TOKEN, not per rule: with transitions cut to
       .001ms an un-eased scale is a one-frame jolt, which is exactly the motion
       this media query exists to remove. Doing it here covers every consumer of
       --mo-press / --portal-mo-press in both sheets at once. */
    :root { --portal-mo-press: none; --mo-press: none; }
    .quick-action-card:hover,
    .balance-chip:hover { transform: none; }
}

/* Quick-action cards stay still on hover — only the border eases to accent
   (the base's calm "no lift" behaviour). No raise. */

/* ---------------------------------------------------------------------------
   17. Specificity overrides — match the base's `html:not(.dark) …` rules for
       the hero card and active nav so the wprovider treatment wins cleanly.
   --------------------------------------------------------------------------- */
html:not(.dark) .sidebar-link.active {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
    box-shadow: none;
}
html:not(.dark) .sidebar-link.active .nav-icon {
    color: var(--portal-accent-soft-ink);
}
html.dark .sidebar-link.active {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
html.dark .sidebar-link.active .nav-icon {
    color: var(--portal-accent-soft-ink);
}

/* Balance card — the PRIMARY metric. Same neutral liquid-glass material as every
   other plaque (neutral fill + drifting neutral sheen, neutral hairline). Stated
   explicitly at hero specificity so the base sheet's coloured hero gradient can't
   show through. No per-card tint — uniform with the cards below. */
html:not(.dark) .metric-card--hero.total-balance-card,
html.dark .metric-card--hero.total-balance-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    /* Honest geometry. The 220%/85% -15% offset existed under an animation that
       slid the sheen across the panel; with that animation gone the oversized
       gradient froze into a visible band. */
    background-size: 100% 100%;
    background-position: 0 0;
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius);
    /* The SAME material as the two tiles beside it. This selector is (0,3,1) and
       so beat the generic `.metric-card { box-shadow: var(--portal-rim) }` — which
       left the balance card as the only plate on the dashboard with no lit top
       edge, sitting next to two that had one. A card is a card: if the hero ever
       needs to outrank its neighbours it does so by SIZE, not by wearing a
       different material. */
    box-shadow: var(--portal-rim);
}
html:not(.dark) .metric-card--hero.total-balance-card span,
html.dark .metric-card--hero.total-balance-card span {
    color: var(--portal-muted);
}
html:not(.dark) .metric-card--hero.total-balance-card strong,
html.dark .metric-card--hero.total-balance-card strong {
    color: var(--portal-financial-text);
}
html:not(.dark) .metric-card--hero.total-balance-card small,
html.dark .metric-card--hero.total-balance-card small {
    color: var(--portal-muted);
}

/* ---------------------------------------------------------------------------
   18. Surface material — one TRANSLUCENT GLASS for every panel/card, so the canvas
       (grain + wash) shows faintly through them all: THE core "Steam-style" look
       the owner wants uniform across the app. The shared fill is --portal-card-glass
       (~50% alpha) + the neutral sheen — the SAME recipe the Steam/catalog/home cards
       already use. NO backdrop-blur here: translucency is a cheap fill, the live blur
       (which was the scroll cost) stays off. Card edges are read by the border +
       shadow, not by an opaque block. (Menus/modals/inputs keep their own opaque
       fills for legibility — they are not in this list.) */
.metric-card,
.quick-action-card,
.home-feed-card,
.ns-filter-grid,
.ns-table-wrap,
.security-card,
.sessions-card,
.ns-profile-card,
.mp-pulse-card,
.mp-overview-status-card,
.mp-activity-card,
.mp-event-card,
.mp-account-health-card,
.mp-channel-summary-card,
.mp-automation-card,
.mp-sales-trend-card,
.mp-product-performance-card,
.mp-customer-performance-card,
.mp-inbox-workload-card,
.mp-order-workload-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
}
/* Neutral glass + drifting light sheen — the instrument tier ONLY (html-prefixed
   so it outranks the base sheet's opaque/coloured card backgrounds). The sheen is
   the neutral specular; only the light layer moves, the glass colour stays put.
   !important re-enables motion past the base's `.app-shell *{animation:none}`.
   Every other surface now falls back to the solid --portal-surface fill above. */
html .mp-pulse-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    /* PERF: static frost — no live backdrop-blur / 19s drift (see the balance hero). */
}
/* wallet + quick-action cards carry higher-specificity coloured backgrounds in the
   base/light sheets; re-assert the SAME translucent glass fill (section 18) at
   matching specificity in both themes so they show the canvas through like every
   other card (owner: unify to the Steam glass). No live blur — translucency only. */
html:not(.dark) .metric-card.wallet-card,
html.dark .metric-card.wallet-card,
html:not(.dark) .quick-action-card,
html.dark .quick-action-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    animation: none !important;
}
/* Home summary ONLY: the wallet metric tiles adopt the balance hero's glass
   surface (same fill + drifting sheen + soft throw), so the summary row reads as
   ONE material instead of a glass balance beside solid siblings (owner: «плитка
   баланса отличается по цвету — сделай остальные такие же»). The .ns-home-summary
   scope outranks the solid reset just above; marketplace wallet cards stay solid. */
html:not(.dark) .ns-home-summary .metric-card.wallet-card,
html.dark .ns-home-summary .metric-card.wallet-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    /* PERF: static frost — matches the hero, which also dropped its live blur/drift,
       so the summary row stays uniform (owner's «сделай остальные такие же») at zero
       scroll cost. */
    box-shadow: var(--portal-shadow-lg);
}
/* The sheen drifts slowly across the panel like light through liquid. Only the
   background-position moves; the glass fill stays put. */
/* ---------------------------------------------------------------------------
   18b. Glass chips/controls — neutral glass with the same drifting light sheen,
        so they shimmer as one material with the cards. Only TRANSLUCENT glass
        surfaces wave; solid-filled buttons (primary CTA, active tab) and the
        transparent ghost buttons stay still — a sheen has nothing to drift over
        a solid or transparent fill.
   --------------------------------------------------------------------------- */
html .language-switch.lang-row,
html .theme-switch.lang-row,
html .balance-chip,
html .ns-pagination button,
html:not(.dark) .sidebar-collapse,
html.dark .sidebar-collapse,
html .portal-select-button,
html .portal-date-button,
html .mp-tabbar,
html .messenger-unread-toggle {
    background-color: var(--portal-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
}
/* Respect reduced-motion: re-disable the drift at matching specificity (the base
   reduced-motion block can't, since our animation is !important). */
@media (prefers-reduced-motion: reduce) {
    html .metric-card,
    html .quick-action-card,
    html .home-feed-card,
    html .security-card,
    html .sessions-card,
    html .ns-profile-card,
    html .mp-pulse-card,
    html .mp-overview-status-card,
    html .mp-activity-card,
    html .mp-event-card,
    html .mp-account-health-card,
    html .mp-channel-summary-card,
    html .mp-automation-card,
    html .mp-sales-trend-card,
    html .mp-product-performance-card,
    html .mp-customer-performance-card,
    html .mp-inbox-workload-card,
    html .mp-order-workload-card,
    html .home-financials .home-feed-row,
    html .language-switch.lang-row,
    html .balance-chip,
    html .ghost-button,
    html .ghost-danger,
    html .ns-pagination button,
    html .sidebar-collapse,
    html .portal-select-button,
    html .portal-date-button,
    html .mp-tabbar,
    html .messenger-unread-toggle,
    html .depuid-method,
    html .dep-field-action,
    html .dep-field-row,
    html .depuid-copy-action,
    html .login-form button,
    html .security-step-next,
    html .portal-modal button[type="submit"],
    html .mp-tab.active,
    html .cat-tile,
    html .cat-region,
    html .cat-facet,
    html .cat-cart-chip,
    html .cat-buybar,
    html .cat-plan-chip,
    html:not(.dark) .cat-sort .portal-select-button,
    html.dark .cat-sort .portal-select-button,
    html .metric-card--hero.total-balance-card,
    html .ns-home-summary .metric-card.wallet-card { animation: none !important; }
}

/* ---------------------------------------------------------------------------
   18d. Home news — editorial cards: a flat surface tile with a thin accent rule
        along the top, an uppercase category kicker (dot + label) + date, a strong
        title and a 2-line body. Calm (no drift); a quiet hover.
   --------------------------------------------------------------------------- */
.home-news-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
}
@media (max-width: 980px) {
    .home-news-grid { grid-template-columns: 1fr; }
}
.home-news-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 20px 20px 18px;
    border-radius: var(--portal-radius);
    border: 1px solid var(--portal-border);
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
    overflow: hidden;
    transition: border-color var(--mo-fast) var(--mo-ease),
                box-shadow var(--mo-base) var(--mo-ease);
}
/* thin accent rule along the top edge, fading to the right */
.home-news-card::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--portal-accent) 0%, color-mix(in srgb, var(--portal-accent) 0%, transparent) 75%);
    opacity: .75;
    transition: opacity var(--mo-fast) var(--mo-ease);
}
@media (hover: hover) {
    .home-news-card:hover {
        border-color: color-mix(in srgb, var(--portal-accent) 38%, var(--portal-border));
        box-shadow: var(--portal-shadow);
    }
    .home-news-card:hover::before { opacity: 1; }
}
.home-news-meta {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-top: 2px;
}
/* category → uppercase kicker with a leading dot (not a filled pill) */
.home-news-tag {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 0;
    background: none;
    color: var(--portal-accent-soft-ink);
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .1em;
    text-transform: uppercase;
}
.home-news-tag::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 999px;
    background: var(--portal-accent);
    flex: 0 0 auto;
}
.home-news-date {
    color: var(--portal-muted);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}
.home-news-title {
    margin: 0;
    font-family: var(--portal-heading-font);
    font-weight: 700;
    font-size: 17px;
    letter-spacing: -.01em;
    line-height: 1.28;
    color: var(--portal-text);
}
.home-news-body {
    margin: 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-base);
    line-height: 1.5;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ---------------------------------------------------------------------------
   18e. Liquid-glass primary buttons — translucent, frosted, with a beveled glass
        rim and a faint accent glow. In Chromium the frost is a REFRACTED glass
        (the SVG #container-glass filter via backdrop-filter:url, embedded in the
        page templates); elsewhere it falls back to a plain blur frost. This is the
        native equivalent of the React "liquid glass button" — same effect, our
        server-rendered + CSS stack (no React/Tailwind here).
   --------------------------------------------------------------------------- */
html.dark .login-form button,
html.dark .security-step-next,
html.dark .portal-modal button[type="submit"],
html:not(.dark) .login-form button,
html:not(.dark) .security-step-next,
html:not(.dark) .portal-modal button[type="submit"] {
    position: relative;
    overflow: hidden;
    border-radius: 13px;
    border: 1px solid color-mix(in srgb, var(--portal-accent) 70%, transparent);
    color: var(--portal-accent-ink);
    font-weight: 700;
    /* Near-opaque so the deep accent reads as the BRAND. At the old 62% the
       forest green washed out to pale sage over a white card and dropped
       white-on-accent to ~3.5:1; the frost + beveled rim still carry the glass. */
    background-color: color-mix(in srgb, var(--portal-accent) 90%, transparent);
    background-image: var(--portal-accent-gradient);
    -webkit-backdrop-filter: blur(7px) saturate(160%);
    backdrop-filter: blur(7px) saturate(160%);
}
/* NOTE: the refracted SVG-glass enhancement (backdrop-filter:url(#container-glass))
   was removed for performance — an feTurbulence+feDisplacement+2×blur graph re-run
   over the backdrop every frame tanked scroll on mobile. The plain blur()+saturate()
   frost above carries the glass look at a fraction of the cost. */
/* beveled glass rim — dark */
html.dark .login-form button,
html.dark .security-step-next,
html.dark .portal-modal button[type="submit"] {
    box-shadow:
        0 2px 10px rgba(0, 0, 0, .45),
        inset 2px 2px 0.6px -2.5px rgba(255, 255, 255, .55),
        inset -2px -2px 0.6px -2.5px rgba(255, 255, 255, .5),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, .32),
        inset -1px -1px 1px -0.5px rgba(255, 255, 255, .32),
        inset 0 0 12px 5px rgba(255, 255, 255, .05),
        0 0 18px -3px color-mix(in srgb, var(--portal-accent) 60%, transparent);
}
/* beveled glass rim — light */
html:not(.dark) .login-form button,
html:not(.dark) .security-step-next,
html:not(.dark) .portal-modal button[type="submit"] {
    box-shadow:
        0 2px 10px rgba(20, 33, 28, .16),
        inset 2px 2px 0.6px -2.5px rgba(255, 255, 255, .95),
        inset -2px -2px 0.6px -2.5px rgba(255, 255, 255, .6),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, .6),
        inset -1px -1px 1px -0.5px rgba(255, 255, 255, .4),
        0 0 18px -4px color-mix(in srgb, var(--portal-accent) 45%, transparent);
}
html .login-form button:hover,
html .security-step-next:hover,
html .portal-modal button[type="submit"]:hover {
    filter: brightness(1.08);
}

/* ---------------------------------------------------------------------------
   19. Mobile — sidebar collapses into a fixed bottom tab bar (≤860px).
   --------------------------------------------------------------------------- */
@media (max-width: 860px) {
    /* Liquid-glass bottom bar — the SAME material as the top-bar controls (balance
       chip + the language/theme pill groups): translucent glass fill, the drifting
       sheen glow, a backdrop blur that refracts the page behind it, a hairline rim
       and a soft floating shadow. Detached from the screen edges into a rounded
       pill so the whole mobile chrome — top cluster + bottom bar — reads as one
       floating liquid-glass set. */
    html .sidebar {
        left: 12px !important;
        right: 12px !important;
        bottom: max(10px, env(safe-area-inset-bottom)) !important;
        width: auto !important;
        gap: 2px !important;
        padding: 6px 6px !important;
        overflow: hidden;
        border-radius: 999px;
        border: 1px solid rgba(18, 32, 27, .10);
        border-top: 1px solid rgba(18, 32, 27, .10);
        border-right: none !important;
        background-color: var(--portal-glass) !important;
        background-image: var(--portal-sheen) !important;
        background-repeat: no-repeat;
        -webkit-backdrop-filter: blur(16px) saturate(150%);
        backdrop-filter: blur(16px) saturate(150%);
        box-shadow: 0 14px 40px rgba(20, 24, 22, .20), inset 0 1px 0 rgba(255, 255, 255, .14);
    }
    html.dark .sidebar {
        border-color: rgba(255, 255, 255, .06);
        box-shadow: 0 16px 44px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .06);
    }
    /* SVG-glass enhancement removed for performance: this bar is FIXED over the
       scrolling catalog, so the #container-glass filter graph re-ran over the whole
       moving backdrop every frame — the top scroll-jank source on mobile. The plain
       blur()+saturate() frost above keeps the look. */
    /* the sidebar is a compact bottom tab bar here — no room for the legal line. */
    .sidebar-legal { display: none; }
    /* Icon-only liquid-glass bar: no labels; each icon sits in a big, generously
       rounded slot, and THAT slot is the highlight — a substantial rounded pill
       around the icon like a native app tab bar. Not a tight little circle, and
       never the old square: every full-cell background is killed below, so nothing
       square can flash on tap. */
    /* Labels hidden but kept for screen readers (display:none strips the a11y name). */
    .sidebar-link span {
        position: absolute !important;
        width: 1px; height: 1px;
        margin: -1px; padding: 0; border: 0;
        overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
        white-space: nowrap;
    }
    html .sidebar-link {
        min-height: 0 !important;
        padding: 6px 0 !important;
        gap: 0 !important;
    }
    /* The icon slot IS the highlight surface — big + softly rounded. */
    html .sidebar-link .nav-icon {
        box-sizing: border-box;
        /* Perfect circle: square slot + equal padding on every side, so the icon
           sits dead-centre and the ball is symmetric top↔bottom and side↔side.
           Kept compact so the bar doesn't eat too much of the screen. */
        width: 48px !important;
        height: 48px !important;
        padding: 12px;
        border-radius: 999px;
        /* Release curve; the press-in override sits with the press rules below.
           `transform` is named explicitly: this is a shorthand, so leaving it out
           would silently drop the icon spring inherited from the desktop rule —
           the same trap that killed the press on .sidebar-link. */
        transition: background-color var(--mo-press-out) var(--mo-ease-press-out),
                    box-shadow var(--mo-base) var(--mo-ease),
                    color var(--mo-fast) var(--mo-ease),
                    transform var(--mo-press-out) var(--mo-ease-press-out);
    }
    /* Kill EVERY full-cell background (rest / hover / :active / .is-pressing /
       .active) — the ONLY highlight is the icon slot, so no square ever flashes. */
    html .sidebar-link,
    html .sidebar-link:hover,
    html .sidebar-link:active,
    html .sidebar-link.active,
    html .sidebar-link.active:active,
    html .sidebar-link.is-pressing {
        background: transparent !important;
        box-shadow: none !important;
    }
    /* …and that includes the press bloom, which is a full-cell background by
       another name: on the tab bar it drew exactly the square this block exists
       to prevent. The 48px slot carries the whole press here. */
    html .sidebar-link::after {
        display: none !important;
    }
    /* Active tab = soft-accent slot + accent glyph. No ring: same call as the
       desktop nav (section 29) — the current section is a state, not a control,
       and colour alone carries it. */
    html .sidebar-link.active .nav-icon {
        background: var(--portal-accent-soft);
        box-shadow: none;
    }
    html .sidebar-link.active,
    html .sidebar-link.active .nav-icon { color: var(--portal-accent-soft-ink); }
    /* Pressing a non-active tab fills the slot with a quiet neutral tint (a grey
       press highlight like the reference); reliable via .is-pressing AND :active.
       The 48px slot is a small control, so it takes the FULL --portal-press step,
       not the halved row tint. */
    html .sidebar-link:not(.active).is-pressing .nav-icon,
    html .sidebar-link:not(.active):active .nav-icon {
        background: var(--portal-press);
    }
    /* The tab you are already on had NO press feedback at all here: the cell
       background is killed above and :not(.active) skipped its slot. It deepens
       its own accent, matching the desktop nav. */
    html .sidebar-link.active.is-pressing .nav-icon,
    html .sidebar-link.active:active .nav-icon {
        background: var(--portal-accent-press);
    }
    /* Press IN — fast + accelerating, mirroring the desktop nav. The slot is a
       48px circle here rather than a 19px glyph, so it dips shallower (.93): the
       same .82 that reads as a crisp tap on a small icon reads as a lurch on a
       ball this size. */
    html .sidebar-link.is-pressing .nav-icon,
    html .sidebar-link:active .nav-icon {
        transform: scale(.93);
        transition-duration: var(--mo-press-in);
        transition-timing-function: var(--mo-ease-press-in);
    }
    /* Content clearance so nothing hides behind the floating bar. Generous on
       purpose: the bar floats ~74px tall incl. its offset, and the last row must
       stay comfortably TAPPABLE above it — a tight gap made the page feel like it
       "wouldn't scroll to the end" (iOS rubber-bands straight back). */
    html main { padding-bottom: calc(124px + env(safe-area-inset-bottom)) !important; }
}

/* ---------------------------------------------------------------------------
   Mobile chrome polish — kill the press "spring" (highlight-only feedback) and
   give the top controls the bottom bar's rounded-rectangle glass shape.
   --------------------------------------------------------------------------- */
@media (max-width: 860px) {
    /* Press feedback is the HIGHLIGHT ALONE — no scale/spring dip on tap. --mo-press
       is used only in `transform: var(--mo-press)`, so nulling it here removes every
       press scale on mobile while leaving all the background/border highlights.
       The canonical token is nulled alongside its alias: a handful of one-off rules
       (the deposit sheet, the catalog "more" chip, the doc-copy button, the topup
       CTA, the modal close, the connect CTA, the assistant launcher) read
       --portal-mo-press DIRECTLY and so kept a scale on phones that nothing else
       had — the gap this comment already assumed was closed. */
    :root { --mo-press: none !important; --portal-mo-press: none !important; }
    /* Fully-rounded chrome — the standard, familiar look: the top controls (balance
       chip + language / theme groups) and their inner pills are full pills, matching
       the bottom bar which is now a rounded stadium with a round active slot. */
    html .balance-chip,
    html .language-switch.lang-row,
    html .theme-switch.lang-row {
        border-radius: 999px;
    }
    html .lang-row .lang-pill,
    html .theme-switch .theme-pill {
        border-radius: 999px;
    }
}

/* ---------------------------------------------------------------------------
   20. Access tiers — sections the user's permission level can't open are shown
       but locked. Sidebar items grey out, gain a padlock and stop responding;
       home cards get taped off with a caution-tape band + a "locked" badge.
   --------------------------------------------------------------------------- */
/* Locked sidebar row — flex (not the base grid) so the padlock sits at the end. */
.sidebar-link.locked {
    display: flex;
    align-items: center;
    gap: 10px;
    opacity: .42;
    /* Keep the row hoverable (no `pointer-events: none`, which would suppress the
       cursor) so the not-allowed cursor and the "why" title tooltip both show. The
       row is an inert <span> with no href, so it never navigates on click. */
    cursor: not-allowed;
    -webkit-user-select: none;
    user-select: none;
}
/* Locked rows must never read as interactive: neutralise the base hover highlight
   (tinted background + accent icon) so hovering only surfaces the not-allowed
   cursor and the tooltip. */
.sidebar-link.locked:hover:not(.active) {
    background: transparent;
}
.sidebar-link.locked:hover:not(.active) .nav-icon {
    color: var(--portal-muted);
}
.sidebar-link.locked .nav-lock {
    width: 15px;
    height: 15px;
    margin-left: auto;
    color: var(--portal-muted);
    flex: 0 0 auto;
}
.app-shell.collapsed .sidebar-link.locked {
    display: grid;
    grid-template-columns: 1fr;
    justify-items: center;
    gap: 0;
}
.app-shell.collapsed .sidebar-link.locked .nav-lock {
    display: none;
}

/* Locked = quiet and inert. Keep the same grid flow as enabled quick actions so
   the title/body do not jump when a card becomes unavailable. The overlay is
   absolutely positioned, so it does not need to change the card layout. */
.quick-action-card.locked {
    display: grid;
    position: relative;
    overflow: hidden;
    /* Keep the card hoverable (no `pointer-events: none`, which would suppress the
       cursor) so the not-allowed cursor and the "why" tooltip show. It's an inert
       <div> (aria-disabled, no href) and JS prefetch/press both skip
       [aria-disabled='true'], so it never navigates or lights a press highlight. */
    cursor: not-allowed;
    /* !important beats the html-prefixed liquid-glass rules (glass fill + emerald
       glow) so the locked card reads as a flat, inert grey surface. */
    background: var(--portal-surface-soft) !important;
    background-image: none !important;
    border-color: var(--portal-border);
    box-shadow: none;
    animation: none !important;
}
/* Grey the text lines. */
.quick-action-card.locked > strong,
.quick-action-card.locked > small {
    opacity: .42;
    filter: grayscale(1);
}
/* The section glyph stays on locked cards — muted and colourless — so the card
   keeps its identity; the padlock pins to the OPPOSITE (top-right) corner, so
   the two never collide. (It was fully hidden once, which left the card bare.) */
.quick-action-card.locked > span:not(.lock-overlay) {
    /* Keep the corner watermark as a faint identity mark — only a touch stronger
       than the .06 on a live card so it reads as muted, not a bright glyph shouting
       on a disabled card. */
    opacity: .12 !important;
    filter: grayscale(1);
}
/* Locked cards are inert: hovering must not warm the border to accent or brighten/
   shift the watermark (that would read as interactive). */
html .quick-action-card.locked:hover {
    border-color: var(--portal-border);
}
html .quick-action-card.locked:hover > span:not(.lock-overlay) {
    transform: none;
}
.quick-action-card.locked > strong {
    display: block;
    margin-top: 0;
}
.quick-action-card.locked > small {
    display: block;
}
/* Overlay just hosts the corner padlock. width/height:auto resets the base
   `.quick-action-card span { width:46px }`, so inset:0 fills the whole card
   (otherwise the overlay collapses to a 46px box, top-left). */
.quick-action-card.locked .lock-overlay {
    position: absolute;
    inset: 0;
    /* Explicit 100% (not auto): as an abs-positioned child of the card GRID the
       auto height resolved to 0 and the padlock sagged to the card's middle,
       overlapping the icon/text on narrow cards. */
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
    background: none;
    box-shadow: none;
    border: none;
    border-radius: inherit;
    pointer-events: none;
}
/* Neutralise the inherited emerald icon-box (.quick-action-card span, l.5627)
   on spans inside the overlay; the badge rule below wins on higher specificity. */
.quick-action-card.locked .lock-overlay span {
    width: auto;
    height: auto;
    background: none;
    border: none;
    box-shadow: none;
    border-radius: 0;
    color: inherit;
    display: inline;
}
/* A small grey padlock in the top-right corner — the whole "locked" signal,
   mirroring the muted padlock on locked sidebar rows. No overlap with the text. */
.quick-action-card.locked .lock-overlay .lock-badge {
    position: absolute;
    top: 16px;
    right: 18px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 9px;
    background: var(--portal-surface);
    border: 1px solid var(--portal-border);
    color: var(--portal-muted);
}
.quick-action-card.locked .lock-overlay .lock-badge svg {
    width: 15px;
    height: 15px;
    color: var(--portal-muted);
    flex: 0 0 auto;
}
/* Keep the "Недоступно" word for screen readers, but the padlock carries the
   message visually (same as the sidebar). */
.quick-action-card.locked .lock-overlay .lock-badge > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* ---------------------------------------------------------------------------
   22. Site footer — legal / company info.
   --------------------------------------------------------------------------- */
.portal-footer {
    /* match main's content frame so the footer lines up with the cards and reads
       symmetrically (centred, equal side padding). width:100% so it STRETCHES to
       the max-width like main, instead of shrinking to its content. */
    width: 100%;
    max-width: 1180px;
    margin: 28px auto 0;
    padding: 22px 20px 30px;
    border-top: 1px solid var(--portal-border);
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.5;
}
.portal-footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.portal-footer-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}
.portal-footer-brand strong {
    font-family: var(--portal-heading-font);
    font-weight: 800;
    letter-spacing: .04em;
    color: var(--portal-text);
}
.portal-footer-links {
    display: flex;
    align-items: center;
    gap: 18px;
}
.portal-footer-links a {
    color: var(--portal-muted);
    text-decoration: none;
    font-weight: 600;
    transition: color var(--mo-fast) var(--mo-ease);
}
.portal-footer-links a:hover {
    color: var(--portal-accent-soft-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.portal-footer-fine {
    margin: 8px 0 0;
    opacity: .85;
}
@media (max-width: 640px) {
    .portal-footer-row { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ---------------------------------------------------------------------------
   23. Quick action cards — refreshed style. No blue icon chip: the glyph becomes
       a large, faint MONOCHROME watermark bleeding off the corner; the title
       leads; a corner arrow signals the link. At rest everything is neutral —
       the accent appears only on hover (arrow + border).
   --------------------------------------------------------------------------- */
html .quick-action-card {
    position: relative;
    overflow: hidden;
    align-content: start;
    padding: 20px 22px;
    /* The corner glyph bleeds off the bottom-right, so it reaches (width - bleed)
       back into the card from the right edge. The copy reserves exactly that lane
       below, which is why both are derived from these two numbers instead of being
       eyeballed separately — a % max-width can't track the glyph and the copy WAS
       sitting on it. */
    --qa-mark-w: 100px;
    --qa-mark-bleed: 22px;
    --qa-mark-reach: calc(var(--qa-mark-w) - var(--qa-mark-bleed));
    /* Tighter than the base's 11px: the title and its body are ONE sentence
       ("Пополнить баланс" → "быстро и безопасно — …"), so they read as a single
       block rather than a heading with a detached blurb underneath. */
    gap: 6px;
}
/* icon glyph → big ghosted corner watermark (not a chip). :not(.lock-overlay) so
   the locked badge wrapper is left alone. */
html .quick-action-card > span:not(.lock-overlay) {
    position: absolute;
    right: calc(-1 * var(--qa-mark-bleed));
    bottom: -24px;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: var(--portal-text);
    opacity: .06;
    pointer-events: none;
    transition: opacity var(--mo-base) var(--mo-ease), transform var(--mo-base) var(--mo-out);
}
html .quick-action-card > span:not(.lock-overlay) .quick-action-icon {
    width: var(--qa-mark-w);
    height: var(--qa-mark-w);
    stroke-width: 1;
}
/* title leads, sits above the watermark */
html .quick-action-card > strong {
    position: relative;
    z-index: 1;
    font-size: 18px;
    font-weight: 700;
    letter-spacing: -.01em;
}
/* The body continues the title as one sentence ("Пополнить баланс" → "быстро и
   безопасно — …"); the seam is carried by the copy itself (lowercase opening + the
   dash the owner wrote), NOT by a CSS-injected mark. A generated lead-in dash was
   tried and rejected. */
html .quick-action-card > small {
    position: relative;
    z-index: 1;
    /* Stop where the glyph starts: full content width, minus the glyph's reach,
       plus the padding the reach already covers. Tracks any card width. */
    max-width: calc(100% - var(--qa-mark-reach) + 22px);
}
/* corner arrow — the "open" cue */
html .quick-action-card::after {
    content: "\2192"; /* → */
    position: absolute;
    top: 16px;
    right: 18px;
    font-size: 18px;
    font-weight: 600;
    line-height: 1;
    color: var(--portal-muted);
    opacity: .65;
    transition: color var(--mo-fast) var(--mo-ease),
                transform var(--mo-fast) var(--mo-out),
                opacity var(--mo-fast) var(--mo-ease);
}
html .quick-action-card.locked::after { display: none; }
/* hover: accent appears (arrow + border); the watermark brightens, stays mono */
html .quick-action-card:hover {
    border-color: color-mix(in srgb, var(--portal-accent) 38%, var(--portal-border));
}
@media (hover: hover) {
    html .quick-action-card:hover::after {
        color: var(--portal-accent-soft-ink);
        opacity: 1;
        transform: translateX(3px);
    }
    html .quick-action-card:hover > span:not(.lock-overlay) {
        opacity: .11;
        transform: translate(-3px, -5px);
    }
}

/* ---------------------------------------------------------------------------
   24. Balance / funding page — the available USD readout (hero) plus the Bybit
       USDT top-up instructions as a clean numbered-step card: a soft circular
       badge per step, hairline dividers, and a highlighted accent panel for the
       operator UID (the one value the client copies). When the channel isn't set
       up yet, the steps show muted "awaiting setup" status pills instead of UIDs.
   --------------------------------------------------------------------------- */
.bal {
    display: grid;
    gap: 16px;
}

/* — Hero: balance readout (left) + funding-channel status chip (right) — */
.bal-hero {
    position: relative;
    overflow: hidden;
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: stretch;
    gap: 28px;
    padding: 28px 30px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius);
    box-shadow: var(--portal-shadow);
    background-color: var(--portal-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
}
html .bal-hero,
html .bal-route {
    /* same drifting neutral sheen as every other glass panel; !important re-enables
       motion past the base's `.app-shell *{animation:none}`. */
}
.bal-eyebrow {
    display: block;
    color: var(--portal-muted);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
}
.bal-hero-readout {
    display: grid;
    gap: 11px;
    align-content: center;
    min-width: 0;
}
.bal-amount {
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    letter-spacing: -.025em;
    font-size: clamp(40px, 4.4vw, 58px);
    line-height: .96;
    color: var(--portal-financial-text);
}
.bal-live {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--portal-muted);
    font-size: 13px;
}
.bal-live-dot {
    width: 7px;
    height: 7px;
    flex: none;
    border-radius: 999px;
    background: var(--portal-good);
}
html .bal-live-dot {
    animation: bal-pulse 2.6s ease-out infinite !important;
}

/* funding-channel chip — replaces the old single-item "tabs" bar */
.bal-channel {
    display: grid;
    gap: 13px;
    align-content: center;
    min-width: 236px;
    padding: 17px 19px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
}
.bal-channel-id {
    display: flex;
    align-items: center;
    gap: 12px;
}
.bal-channel-glyph {
    width: 40px;
    height: 40px;
    flex: none;
    padding: 9px;
    border-radius: 11px;
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
.bal-channel-id strong {
    display: block;
    font-family: var(--portal-heading-font);
    font-weight: 700;
    font-size: 16px;
    line-height: 1.15;
    color: var(--portal-text);
}
.bal-channel-id small {
    display: block;
    color: var(--portal-muted);
    font-size: 12px;
}
.bal-channel-status {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 13px;
    font-weight: 600;
}
.bal-status-dot {
    width: 7px;
    height: 7px;
    flex: none;
    border-radius: 999px;
}
.bal-channel[data-state="ready"] .bal-channel-status { color: var(--portal-good); }
.bal-channel[data-state="ready"] .bal-status-dot { background: var(--portal-good); }
.bal-channel[data-state="setup"] .bal-channel-status { color: var(--portal-warn); }
.bal-channel[data-state="setup"] .bal-status-dot { background: var(--portal-warn); }

/* — Card holding the deposit steps — */
.bal-route {
    position: relative;
    overflow: hidden;
    padding: 26px 30px 28px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius);
    box-shadow: var(--portal-shadow);
    background-color: var(--portal-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
}
.bal-route-head {
    display: grid;
    gap: 8px;
    margin-bottom: 24px;
}
.bal-route-head h2 {
    margin: 0;
    font-family: var(--portal-heading-font);
    font-weight: 700;
    letter-spacing: -.01em;
    font-size: clamp(20px, 1.8vw, 24px);
    line-height: 1.15;
    color: var(--portal-text);
}
.bal-route-lead {
    margin: 0;
    max-width: 64ch;
    color: var(--portal-muted);
    line-height: 1.55;
    font-size: 14px;
}

/* — Stepped deposit instructions: numbered badges, hairline dividers between
     steps, and a highlighted operator-UID panel (the one thing to copy). — */
.bal-steps {
    list-style: none;
    margin: 0;
    padding: 0;
}
.bal-step {
    display: grid;
    grid-template-columns: 28px minmax(0, 1fr);
    gap: 16px;
    padding: 22px 0;
    border-top: 1px solid var(--portal-border);
}
.bal-step:first-child {
    border-top: 0;
    padding-top: 4px;
}
.bal-step-num {
    width: 28px;
    height: 28px;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    font-size: 13px;
}
.bal-step--pending .bal-step-num {
    background: var(--portal-surface-soft);
    color: var(--portal-muted);
    border: 1.5px dashed var(--portal-border);
}
.bal-step-body {
    display: grid;
    gap: 12px;
    padding-top: 3px;
    min-width: 0;
}
.bal-step-title {
    font-family: var(--portal-heading-font);
    font-weight: 600;
    font-size: 16px;
    letter-spacing: -.005em;
    color: var(--portal-text);
}
.bal-step-note {
    margin: 0;
    max-width: 64ch;
    color: var(--portal-muted);
    font-size: 14px;
    line-height: 1.5;
}

/* sender UID — a plain read-only reference field */
.bal-field {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 13px 16px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
}
.bal-field code {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-size: 18px;
    letter-spacing: .02em;
    color: var(--portal-text);
}

/* operator UID — the highlighted accent panel the client sends USDT to */
.bal-operator {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    gap: 6px 16px;
    padding: 16px 18px;
    border: 1px solid color-mix(in srgb, var(--portal-accent) 42%, var(--portal-border));
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent-soft);
}
.bal-operator-key {
    grid-column: 1;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--portal-accent-soft-ink);
}
.bal-operator-uid {
    grid-column: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    font-size: 24px;
    letter-spacing: .01em;
    color: var(--portal-text);
}
.bal-operator-note {
    grid-column: 1 / -1;
    margin: 6px 0 0;
    color: var(--portal-muted);
    font-size: 13px;
    line-height: 1.5;
}
/* Bordered button (reference style). Selector carries .bal-operator so it outranks
   the base sheet's `html.dark button { background: accent }` fill. */
.bal-operator .bal-copy {
    grid-column: 2;
    grid-row: 1 / 3;
    align-self: center;
    flex: none;
    padding: 10px 18px;
    border: 1px solid color-mix(in srgb, var(--portal-accent) 55%, var(--portal-border));
    border-radius: 10px;
    background: var(--portal-surface);
    color: var(--portal-accent-soft-ink);
    font-family: var(--portal-font-ui);
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    box-shadow: none;
    transition: border-color var(--mo-fast) var(--mo-ease),
                color var(--mo-fast) var(--mo-ease),
                background var(--mo-fast) var(--mo-ease),
                transform var(--mo-fast) var(--mo-out);
}
@media (hover: hover) {
    .bal-operator .bal-copy:hover {
        background: color-mix(in srgb, var(--portal-accent) 14%, var(--portal-surface));
        border-color: var(--portal-accent);
    }
}
.bal-operator .bal-copy:active {
    transform: var(--mo-press);
}
.bal-operator .bal-copy:focus-visible {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}

/* minimum-transfer line under the operator panel */
.bal-meta-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
}
.bal-meta-row span {
    color: var(--portal-muted);
    font-size: 14px;
}
.bal-meta-row strong {
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    font-size: 15px;
    color: var(--portal-text);
}

/* awaiting-setup status pill on dormant steps */
.bal-pending-status {
    justify-self: start;
    display: inline-flex;
    align-items: center;
    padding: 7px 14px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--portal-warn) 15%, transparent);
    color: var(--portal-warn);
    font-size: 13px;
    font-weight: 600;
}

@media (max-width: 860px) {
    .bal-hero {
        grid-template-columns: 1fr;
        gap: 22px;
        padding: 22px;
    }
    .bal-channel {
        min-width: 0;
    }
    .bal-route {
        padding: 22px 20px 24px;
    }
    .bal-operator {
        grid-template-columns: 1fr;
    }
    .bal-operator-uid {
        font-size: 21px;
    }
    .bal-operator .bal-copy {
        grid-column: 1;
        grid-row: auto;
        justify-self: start;
        margin-top: 4px;
    }
}

@media (prefers-reduced-motion: reduce) {
    html .bal-hero,
    html .bal-route,
    html .bal-live-dot {
        animation: none !important;
    }
}

@keyframes bal-pulse {
    0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--portal-good) 55%, transparent); }
    70%  { box-shadow: 0 0 0 7px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
}

/* ---------------------------------------------------------------------------
   25. Phone fit — compact header + login controls (<=520px)
       The language switch is a 3-pill segmented row (flag + EN/中文/RU) and the
       theme switch is two icon pills. On a phone the balance chip + language row
       + theme switch overran the topbar, so the language pills were squeezed
       until their flags overlapped; on the login card the same controls' min
       width pushed the whole panel wider than the screen (then clipped by the
       panel's overflow:hidden, cutting off the button and the last flag).
       On narrow screens we drop the language LABELS — the flag alone names the
       language — tighten both rows, and show the balance value without its
       "Balance:" label. The cluster then fits comfortably down to 320px. */
@media (max-width: 520px) {
    /* Flag-only language pills: the EN/中文/RU text is what made the row too wide;
       the flag is a sufficient cue and the active pill still highlights. */
    .lang-row .lang-pill-code { display: none !important; }
    /* Flag-only + icon-only pills become perfect CIRCLES: a square 36px slot so the
       active fill AND the accent press-ring read as a round highlight — the language
       and theme switchers now match each other and the bottom-nav ball. flex:none
       stops the 3-flag row from shrinking a pill back into an oval. */
    .lang-row .lang-pill,
    html .theme-switch .theme-pill {
        width: 36px;
        height: 36px;
        min-height: 36px;
        padding: 0;
        gap: 0;
        justify-content: center;
        flex: none;
    }
    /* 3:2, matching the artwork. This said 20x15 (4:3) from when the chip briefly
       took the shared 4:3 image set; against the 3:2 inline flags it letterboxed
       them inside a rounded box, so the rounding stopped following the flag and
       a sliver of pill showed above and below it. */
    .lang-pill-flag { width: 21px; height: 14px; }

    /* Tighten both segmented rows (language + theme) so three flags + two theme
       icons + the balance read sit on one comfortable line. */
    .language-switch.lang-row,
    .theme-switch.lang-row {
        gap: 2px;
        padding: 3px 4px;
        min-height: 40px;
    }

    /* Topbar cluster: keep the "Balance" label on the chip; tight gaps + padding
       so labelled balance + flag-only language + theme still share one line. The
       top padding folds in env(safe-area-inset-top): in the installed standalone
       app (translucent status bar) it drops the controls below the clock/battery;
       in plain Safari the inset is 0, so nothing changes. */
    .topbar { gap: 6px !important; padding: calc(10px + env(safe-area-inset-top)) 12px 10px !important; }
    .topbar-actions { gap: 6px; }
    .balance-chip { padding: 0 12px !important; }

    /* Login footer controls share the compact rows; let them wrap as a final
       safety net so they can never force the card wider than the screen. */
    .login-controls {
        flex-wrap: wrap;
        gap: 8px;
    }
}

/* ---------------------------------------------------------------------------
   26. Phone home + bottom nav — trim to essentials, fix clipping/scroll/align
       The mobile shell had accumulated several issues: the bottom bar couldn't
       scroll (a stale overflow:hidden won the cascade) so Inbox + Transactions
       were trapped off-screen while two WIP placeholders (Balance, Profile) took
       their slots; the 3 summary cards left an empty 4th grid cell; the
       quick-action corner-watermark clipped to stray notches; and the feed-row
       amount dropped below the title, left-aligned. Fixed together below.
   --------------------------------------------------------------------------- */
@media (max-width: 860px) {
    /* Bottom tab bar: exactly five core tabs — Home / Catalog / Orders /
       Transactions / Settings — spread evenly across the full width. Nothing
       scrolls and nothing gets cut off. Steam top-up, Balance and the
       locked/secondary sections are dropped: Balance lives in the top-bar
       balance chip, Steam in the home quick actions. */
    .sidebar {
        overflow-x: hidden !important;
        overflow-y: hidden !important;
        justify-content: space-between;
        touch-action: manipulation;
    }
    .sidebar-link[data-nav="steam"],
    .sidebar-link[data-nav="balance"],
    .sidebar-link[data-nav="profile"],
    .sidebar-link[data-nav="inbox"],
    .sidebar-link[data-nav="api_docs"],
    .sidebar-link[data-nav="marketplaces"] { display: none !important; }
    /* DOM puts the store group (catalog) after Settings — order the tabs
       Home, Catalog, then the account tabs. */
    .sidebar-link[data-nav="home"] { order: -2 !important; }
    .sidebar-link[data-nav="catalog"] { order: -1 !important; }

    /* Summary: two identical, symmetric cards — Balance + Last top-up. "Total
       orders" (card 2) is hidden on phones (orders live one tap away on the
       Orders tab); the last balance top-up is more useful at a glance. */
    .overview .ns-home-summary .metric-card:nth-child(2) { display: none !important; }
    /* Equal height AND filled — label pinned top, caption pinned bottom — so the
       shorter card has no lopsided empty space at the bottom. */
    .overview .ns-home-summary { align-items: stretch !important; }
    .overview .ns-home-summary .metric-card { align-content: space-between !important; }
    /* Make the two cards truly identical so the numbers line up: reserve a 2-line
       label area (one label wraps, the other doesn't) and use the SAME value size
       in both (the hero balance defaults to 30px, the top-up to 19px → the two
       "$1.00" read at different sizes and heights). */
    .overview .ns-home-summary .metric-card > span {
        min-height: 2.5em;
        line-height: 1.25;
    }
    .overview .ns-home-summary .metric-card > strong {
        font-size: 26px !important;
        line-height: 1.1;
    }

    /* Quick-action cards: match the PC version — a title-leading card with the
       big ghosted corner watermark + corner arrow. Reset the <=640 "icon title /
       icon body" area reassignment (which left an empty icon column) so the card
       flows exactly like desktop; the watermark keeps its desktop rule. */
    .quick-action-card {
        grid-template-columns: 1fr !important;
        grid-template-areas: none !important;
        grid-template-rows: auto auto !important;
        align-content: start !important;
        padding: 20px 22px !important;
        min-height: 116px !important;
    }
    .quick-action-card > strong,
    .quick-action-card > small { grid-area: auto !important; }
    .quick-action-card > small { max-width: 82% !important; }

    /* Feed rows: keep the amount on the RIGHT (the <=640 rule dropped it below the
       title, left-aligned — that was the "crooked" row). Title ellipsises. */
    .home-financials .home-feed-row {
        grid-template-columns: 42px minmax(0, 1fr) max-content !important;
        align-items: center;
    }
    .home-financials .home-feed-row .amount {
        grid-column: 3 !important;
        justify-self: end !important;
        align-self: center !important;
    }
    .home-financials .home-feed-copy strong {
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    /* Tiles + icon rings are GLOBAL now (see the canon note at the top-level
       .home-financials rules) — here only the mobile-specific bit remains: the
       legacy mobile sheet pads the card itself (16px), which would inset the
       header divider; zero it so the divider runs edge to edge like desktop. */
    .home-financials .home-feed-card {
        padding: 0 !important;
    }
    /* Phone feed rows are a two-line summary (owner's pick): title+amount,
       date+balance. The #DX ref is hidden here — it lives in Transactions and
       in the tapped-open detail; without it the meta never wraps or collides. */
    .home-financials .home-feed-ref,
    .home-financials .home-feed-sep {
        display: none;
    }
    .home-financials .home-feed-copy small {
        line-height: 1.45;
    }
    /* A long right column (Balance: $100,898.9608) can squeeze the meta —
       let the date wrap at its own space (date / time) instead of overlapping
       the neighbour; the copy column may actually shrink (min-width: 0). */
    .home-financials .home-feed-copy {
        min-width: 0;
    }
    .home-financials .home-feed-copy .home-feed-bit {
        white-space: normal;
    }
    .home-financials .home-feed-row {
        align-items: center;
    }
    .home-financials .home-feed-row .amount,
    .home-financials .home-feed-right {
        align-self: center !important;
        margin-top: 0;
    }
}

/* ============================================================
   Deposit / top-up — step 1 (method & amount). Published page.
   Tokens: --portal-* (auto-adapt to dark theme). Provider selector
   reuses the shared [data-dropdown] open/close JS.
   ============================================================ */
.dep1 {
    display: block;
}
.dep1-card {
    background-color: var(--portal-glass, var(--portal-surface));
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius);
    box-shadow: var(--portal-shadow);
    padding: 26px 30px 28px;
}
.dep1-stepper {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-size: 12px;
    font-weight: 600;
    color: var(--portal-accent-soft-ink);
    background: var(--portal-accent-soft);
    border-radius: 999px;
    padding: 6px 14px;
    margin-bottom: 22px;
}
.dep1-stepper b {
    font-weight: 800;
    font-variant-numeric: tabular-nums;
}
.dep1-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
    gap: 30px 36px;
    align-items: start;
}
.dep1-left {
    display: flex;
    flex-direction: column;
    gap: 26px;
}
.dep1-right {
    display: flex;
    flex-direction: column;
    gap: 22px;
}
.dep1-eyebrow {
    margin: 0 0 9px;
    font-size: 12px;
    font-weight: 700;
    letter-spacing: .055em;
    text-transform: uppercase;
    color: var(--portal-muted);
}
.dep1-balance {
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    letter-spacing: -.025em;
    font-size: clamp(34px, 3.6vw, 46px);
    line-height: .96;
    color: var(--portal-financial-text, var(--portal-text));
}
.dep1-after {
    margin: 12px 0 0;
    font-size: 13px;
    color: var(--portal-muted);
}
.dep1-after strong {
    font-weight: 700;
    color: var(--portal-text);
    font-variant-numeric: tabular-nums;
}
.dep1-due {
    margin: 0;
    color: var(--portal-muted);
}
.dep1-due strong {
    color: var(--portal-text);
    font-variant-numeric: tabular-nums;
}

/* provider selector */
.dep1-method-wrap {
    position: relative;
}
.dep1-method {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 13px;
    padding: 13px 15px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
    cursor: pointer;
    text-align: left;
    transition: border-color var(--mo-fast, 140ms) var(--mo-ease, ease),
                background var(--mo-fast, 140ms) var(--mo-ease, ease);
}
.dep1-method:hover {
    border-color: color-mix(in srgb, var(--portal-accent) 45%, var(--portal-border));
    background: var(--portal-hover);
}
.dep1-method-wrap.open .dep1-method {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
    background: var(--portal-surface);
}
.dep1-logo {
    width: 38px;
    height: 38px;
    flex: none;
    border-radius: 10px;
    background: #0b0e11;
    display: flex;
    align-items: center;
    justify-content: center;
}
.dep1-logo--ton { background: #0098ea; }
.dep1-logo--card { background: var(--portal-surface-soft); border: 1px solid var(--portal-border); }
.dep1-logo-mark {
    position: relative;
    color: #fff;
    font-weight: 800;
    font-size: 17px;
    line-height: 1;
}
.dep1-logo--card .dep1-logo-mark { color: var(--portal-muted); }
.dep1-logo-dot {
    position: absolute;
    top: -2px;
    right: -5px;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: #f7a600;
}
.dep1-method-id {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
    flex: 1;
}
.dep1-method-name {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-weight: 700;
    font-size: 15px;
    color: var(--portal-text);
}
.dep1-method-sub {
    font-size: var(--portal-text-sm);
    color: var(--portal-muted);
}
.dep1-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--portal-good);
}
.dep1-chev {
    width: 18px;
    height: 18px;
    flex: none;
    color: var(--portal-muted);
    transition: transform var(--mo-base, 210ms) var(--mo-ease, ease);
}
.dep1-method-wrap.open .dep1-chev {
    transform: rotate(180deg);
    color: var(--portal-accent-soft-ink);
}
/* dropdown menu — animated open (handled here so design hand-off just draws states) */
.dep1-menu {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    z-index: 40;
    background: var(--portal-surface);
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    box-shadow: var(--portal-shadow-lg);
    padding: 6px;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(-8px) scale(.99);
    transform-origin: top center;
    transition: opacity var(--mo-base, 210ms) var(--mo-ease, ease),
                transform var(--mo-slow, 340ms) var(--mo-spring, cubic-bezier(.34, 1.4, .64, 1)),
                visibility var(--mo-base, 210ms) step-end;
}
.dep1-method-wrap.open .dep1-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateY(0) scale(1);
    transition: opacity var(--mo-base, 210ms) var(--mo-ease, ease),
                transform var(--mo-slow, 340ms) var(--mo-spring, cubic-bezier(.34, 1.4, .64, 1)),
                visibility 0s;
}
.dep1-opt {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 11px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    cursor: pointer;
    text-align: left;
    transition: background var(--mo-fast, 140ms) var(--mo-ease, ease);
}
.dep1-opt + .dep1-opt { margin-top: 2px; }
.dep1-opt:hover { background: var(--portal-hover); }
.dep1-opt.is-active { background: var(--portal-accent-soft); }
.dep1-opt.is-soon { cursor: default; opacity: .6; }
.dep1-opt.is-soon:hover { background: transparent; }
.dep1-opt .dep1-logo { width: 32px; height: 32px; border-radius: 9px; }
.dep1-opt .dep1-logo-mark { font-size: 14px; }
.dep1-opt-check {
    margin-left: auto;
    color: var(--portal-accent-soft-ink);
    font-weight: 800;
    font-size: 15px;
}
.dep1-soon {
    margin-left: auto;
    font-size: var(--portal-text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--portal-warn);
    background: color-mix(in srgb, var(--portal-warn) 14%, var(--portal-surface));
    border-radius: 999px;
    padding: 3px 9px;
}

/* amount */
.dep1-amount {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
    transition: border-color var(--mo-fast, 140ms) var(--mo-ease, ease),
                box-shadow var(--mo-fast, 140ms) var(--mo-ease, ease),
                background var(--mo-fast, 140ms) var(--mo-ease, ease);
}
.dep1-amount:focus-within {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
    background: var(--portal-surface);
}
.dep1-amount-input {
    flex: 1;
    min-width: 0;
    border: 0;
    background: transparent;
    outline: none;
    color: var(--portal-text);
    font-family: var(--portal-font-number);
    font-variant-numeric: tabular-nums;
    font-weight: 800;
    font-size: 26px;
    letter-spacing: -.01em;
    padding: 0;
}
.dep1-amount-suf {
    flex: none;
    font-size: 13px;
    font-weight: 600;
    color: var(--portal-muted);
}
.dep1-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: 11px;
}
.dep1-chip {
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--portal-text);
    background: var(--portal-surface-soft);
    border: 1px solid var(--portal-border-soft);
    border-radius: 999px;
    padding: 7px 15px;
    cursor: pointer;
    transition: background var(--mo-fast, 140ms) var(--mo-ease, ease),
                border-color var(--mo-fast, 140ms) var(--mo-ease, ease),
                color var(--mo-fast, 140ms) var(--mo-ease, ease),
                transform var(--mo-fast, 140ms) var(--mo-ease, ease);
}
.dep1-chip:hover { border-color: color-mix(in srgb, var(--portal-accent) 40%, var(--portal-border)); }
.dep1-chip:active { transform: scale(.96); }
.dep1-chip.is-sel {
    background: var(--portal-accent-soft);
    border-color: transparent;
    color: var(--portal-accent-soft-ink);
}
.dep1-chip--exact {
    border-color: color-mix(in srgb, var(--portal-accent) 55%, transparent);
    color: var(--portal-accent-soft-ink);
    background: var(--portal-surface);
}
.dep1-chip--exact.is-sel {
    background: var(--portal-accent-soft);
    border-color: transparent;
}

/* footer / continue */
.dep1-foot {
    margin-top: 26px;
}
.dep1-continue {
    display: inline-flex;
    align-items: center;
    gap: 9px;
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-weight: 700;
    font-size: 15px;
    border: 0;
    border-radius: var(--portal-radius-sm);
    padding: 13px 26px;
    text-decoration: none;
    box-shadow: 0 8px 18px -8px var(--portal-accent-ring);
    transition: background var(--mo-fast, 140ms) var(--mo-ease, ease),
                transform var(--mo-fast, 140ms) var(--mo-ease, ease),
                box-shadow var(--mo-base, 210ms) var(--mo-ease, ease);
}
.dep1-continue:hover {
    background: var(--portal-accent-dark);
    box-shadow: 0 12px 24px -8px var(--portal-accent-ring);
}
.dep1-continue:active { transform: scale(.98); }
.dep1-continue-arrow {
    width: 18px;
    height: 18px;
    transition: transform var(--mo-base, 210ms) var(--mo-ease, ease);
}
.dep1-continue:hover .dep1-continue-arrow { transform: translateX(3px); }

@media (max-width: 720px) {
    .dep1-card { padding: 20px 18px 22px; }
    .dep1-grid { grid-template-columns: 1fr; gap: 24px; }
    .dep1-continue { width: 100%; justify-content: center; }
}

/* Bybit official mark: SVG provides the dark squircle, so the tile is transparent */
.dep1-logo--bybit{background:transparent;}
.dep1-logo-svg{width:100%;height:100%;display:block;}

/* Deposit step 1 — phone polish. The base @media(720px) stacks the grid; these
   finer rules stop overflow and keep touch targets comfortable on small screens. */
@media (max-width: 600px){
  .dep1{display:block;}
  .dep1-card{padding:18px 16px 20px;border-radius:14px;}
  .dep1-stepper{font-size:11px;padding:5px 12px;margin-bottom:16px;}
  .dep1-grid{gap:22px;}
  .dep1-balance{font-size:clamp(30px,9vw,38px);}
  .dep1-amount{padding:11px 14px;}
  .dep1-amount-input{font-size:22px;min-width:0;}
  .dep1-amount-suf{font-size:12px;white-space:nowrap;}
  .dep1-method{padding:11px 12px;gap:11px;}
  .dep1-method-name{font-size:14px;}
  .dep1-method-sub{font-size:var(--portal-text-xs);}
  .dep1-chips{gap:7px;}
  .dep1-chip{padding:7px 14px;font-size:13px;}
  .dep1-eyebrow{font-size:11px;margin-bottom:7px;}
  .dep1-foot{margin-top:22px;}
  .dep1-continue{width:100%;justify-content:center;padding:14px 22px;font-size:15px;}
}
/* Clear the fixed mobile bottom tab bar so the Continue button is never hidden. */
@media (max-width: 860px){
  .dep1{margin-bottom:8px;}
}

/* Session location: restore the compact pin marker from the original list. */
.session-location{display:flex !important;align-items:center;gap:5px;line-height:1.35;min-width:0;}
/* A thin monochrome pin (mask, tinted by the row's muted colour) instead of the
   📍 emoji — the portal keeps emoji out of its own UI chrome. */
.session-location::before{
    content:"" !important;
    flex:none;
    width:12px;height:12px;
    background:currentColor;
    -webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z'/%3E%3Ccircle cx='12' cy='10' r='2.6'/%3E%3C/svg%3E") center/contain no-repeat;
    mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z'/%3E%3Ccircle cx='12' cy='10' r='2.6'/%3E%3C/svg%3E") center/contain no-repeat;
    opacity:.75;
}

/* Provider logos as real brand images (Tonkeeper/TON, USDT) on a clean tile */
.dep1-logo--img{background:var(--portal-surface);border:1px solid var(--portal-border-soft);overflow:hidden;}
.dep1-logo-img{width:100%;height:100%;object-fit:contain;padding:4px;display:block;}

/* Mobile: amount chips stay a small wrapping row, never full-width stacked */
@media (max-width: 600px){
  .dep1-chips{display:flex !important;flex-flow:row wrap !important;gap:7px !important;justify-content:flex-start;}
  .dep1-chip{width:auto !important;min-width:0 !important;flex:0 0 auto !important;padding:7px 14px;font-size:13px;}
}

/* (1) Amount field: the wrapper already shows ONE focus ring (:focus-within);
   kill the input's own focus outline so there is no second inner frame. */
.dep1-amount-input:focus,
.dep1-amount-input:focus-visible{outline:none !important;box-shadow:none !important;}

/* (2) Mobile nav: a locked tab must line up with the others. Match the normal
   grid layout and drop the padlock here (grey state already signals locked),
   so it no longer sits taller / out of row. */
@media (max-width: 920px){
  .sidebar-link.locked{display:grid;grid-template-columns:22px minmax(0,1fr);gap:12px;}
  .sidebar-link.locked .nav-lock{display:none;}
}

/* ===================== Deposit steps 2 & 3 ===================== */
/* Entrance (base app-shell disables animation; re-enable with !important) */
.dep2,.dep3{display:block;animation:wprovider-rise var(--mo-slow,340ms) var(--mo-out,cubic-bezier(0,0,.2,1)) both !important;}

/* Step 2 — requisite rows */
.dep2-rows{display:flex;flex-direction:column;}
.dep2-row{display:flex;align-items:center;gap:16px;padding:15px 2px;border-bottom:1px solid var(--portal-border-soft);min-height:30px;}
.dep2-row:last-child{border-bottom:0;}
.dep2-k{flex:0 0 auto;width:180px;font-size:13px;font-weight:600;color:var(--portal-muted);}
.dep2-val{display:flex;align-items:center;gap:12px;flex:1;min-width:0;flex-wrap:wrap;}
.dep2-uid{font-family:var(--portal-font-number);font-variant-numeric:tabular-nums;font-weight:800;font-size:20px;letter-spacing:.02em;color:var(--portal-text);}
.dep2-saved{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.02em;color:var(--portal-good);background:var(--portal-good-soft);border-radius:999px;padding:4px 10px;}
.dep2-pending{font-size:13px;font-weight:600;color:var(--portal-warn);}
.dep2-net{font-weight:700;font-size:14px;color:var(--portal-text);}
.dep2-amount{font-family:var(--portal-font-number);font-variant-numeric:tabular-nums;font-weight:800;font-size:20px;color:var(--portal-accent-soft-ink);}
.dep2-row--accent{background:var(--portal-accent-soft);border-radius:11px;border-bottom:0;padding-left:14px;padding-right:14px;margin:2px 0;}
.dep2-row--accent .dep2-k{color:var(--portal-accent-soft-ink);}
.dep2-saveform{display:flex;gap:9px;flex:1;min-width:0;}
.dep2-input{flex:1;min-width:0;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);background:var(--portal-surface-soft);padding:11px 14px;font-size:15px;font-weight:600;color:var(--portal-text);outline:none;transition:border-color var(--mo-fast,140ms) var(--mo-ease),box-shadow var(--mo-fast,140ms) var(--mo-ease),background var(--mo-fast,140ms) var(--mo-ease);}
.dep2-input::placeholder{color:var(--portal-soft-muted,#9ca3af);font-weight:500;}
.dep2-input:focus{border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);background:var(--portal-surface);}
.dep2-input--txid{flex:1;}
.dep2-savebtn{flex:none;background:var(--portal-accent);color:var(--portal-accent-ink);font-weight:700;font-size:14px;border:0;border-radius:var(--portal-radius-sm,11px);padding:0 20px;cursor:pointer;transition:background var(--mo-fast,140ms) var(--mo-ease),transform var(--mo-fast,140ms) var(--mo-ease);}
.dep2-savebtn:hover{background:var(--portal-accent-dark);}
.dep2-savebtn:active{transform:scale(.97);}
.dep2-copy{flex:none;font-size:var(--portal-text-sm);font-weight:600;color:var(--portal-accent-soft-ink);background:var(--portal-surface);border:1px solid color-mix(in srgb,var(--portal-accent) 50%,var(--portal-border));border-radius:10px;padding:8px 14px;cursor:pointer;transition:background var(--mo-fast,140ms) var(--mo-ease);}
.dep2-copy:hover{background:var(--portal-accent-soft);}
.dep2-confirm{display:flex;flex-direction:column;}
.dep2-note{font-size:var(--portal-text-sm);color:var(--portal-muted);line-height:1.5;margin:14px 0 0;}
.dep2-foot{display:flex;gap:10px;margin-top:18px;align-items:center;}

/* ghost/secondary continue button */
.dep1-continue--ghost{background:var(--portal-surface);color:var(--portal-text);border:1px solid var(--portal-border);box-shadow:none;}
.dep1-continue--ghost:hover{background:var(--portal-surface-soft);box-shadow:none;}
.dep1-continue-arrow--back{transition:transform var(--mo-base,210ms) var(--mo-ease);}
.dep1-continue--ghost:hover .dep1-continue-arrow--back{transform:translateX(-3px);}

/* Step 3 — status timeline */
.dep3-timeline{display:flex;align-items:flex-start;gap:0;padding:8px 0 4px;}
.dep3-node{display:flex;flex-direction:column;align-items:center;gap:9px;flex:0 0 auto;width:74px;text-align:center;}
.dep3-dot{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;flex:none;border:2px solid var(--portal-border);background:var(--portal-surface);color:var(--portal-muted);}
.dep3-dot svg{width:22px;height:22px;}
.dep3-node--done .dep3-dot{background:var(--portal-good);border-color:var(--portal-good);color:#fff;}
.dep3-node--now .dep3-dot{border-color:var(--portal-accent);color:var(--portal-accent-soft-ink);background:var(--portal-accent-soft);animation:dep-pulse 1.6s var(--mo-ease) infinite !important;}
.dep3-node--next .dep3-dot{opacity:.7;}
@keyframes dep-pulse{0%,100%{box-shadow:0 0 0 0 var(--portal-accent-ring);}50%{box-shadow:0 0 0 7px transparent;}}
.dep3-nlabel{font-size:var(--portal-text-xs);font-weight:600;color:var(--portal-muted);line-height:1.25;}
.dep3-node--done .dep3-nlabel,.dep3-node--now .dep3-nlabel{color:var(--portal-text);}
.dep3-bar{flex:1;height:2px;background:var(--portal-border-soft);margin-top:22px;min-width:14px;}
.dep3-bar--done{background:var(--portal-good);}
.dep3-success{display:flex;align-items:center;gap:14px;background:var(--portal-good-soft);border-radius:14px;padding:16px 18px;margin-top:22px;animation:dep-pop var(--mo-slow,340ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1)) both !important;}
@keyframes dep-pop{from{opacity:0;transform:scale(.96);}to{opacity:1;transform:none;}}
.dep3-success-ck{width:38px;height:38px;flex:none;border-radius:50%;background:var(--portal-good);color:#fff;display:flex;align-items:center;justify-content:center;}
.dep3-success-ck svg{width:22px;height:22px;}
.dep3-success>div{display:flex;flex-direction:column;gap:2px;}
.dep3-success strong{font-size:var(--portal-text-lg);font-weight:800;}
.dep3-success span{font-size:var(--portal-text-sm);}

/* Mobile */
@media (max-width: 600px){
  .dep2-row{flex-direction:column;align-items:flex-start;gap:7px;padding:13px 2px;}
  .dep2-k{width:auto;}
  .dep2-row--accent{padding:12px 14px;}
  .dep2-saveform{width:100%;}
  .dep2-foot{flex-direction:column-reverse;align-items:stretch;}
  .dep2-foot .dep1-continue{width:100%;justify-content:center;}
  .dep3-node{width:62px;}
  .dep3-dot{width:40px;height:40px;}
  .dep3-nlabel{font-size:var(--portal-text-xs);}
  .dep3-bar{margin-top:19px;min-width:8px;}
}

/* ===== Step 2 — click-to-copy rows + compact save icon ===== */
.dep2-row--copy{cursor:pointer;border-radius:11px;padding-left:12px;padding-right:12px;border-bottom:1px solid var(--portal-border-soft);transition:background var(--mo-fast,140ms) var(--mo-ease),color var(--mo-fast,140ms) var(--mo-ease),transform var(--mo-fast,140ms) var(--mo-ease);}
.dep2-row--copy:hover,.dep2-row--copy:focus-visible{background:var(--portal-accent-soft);outline:none;border-bottom-color:transparent;}
.dep2-row--copy:hover .dep2-k,.dep2-row--copy:hover .dep2-uid,.dep2-row--copy:hover .dep2-net{color:var(--portal-accent-soft-ink);}
.dep2-row--copy:active{transform:scale(.994);}
.dep2-copyic{position:relative;margin-left:auto;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex:none;color:var(--portal-muted);}
.dep2-copyic svg{position:absolute;width:18px;height:18px;transition:opacity var(--mo-fast,140ms) var(--mo-ease),transform var(--mo-base,210ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1));}
.dep2-row--copy:hover .dep2-copyic{color:var(--portal-accent-soft-ink);}
.dep2-ic-check{opacity:0;transform:scale(.4);color:var(--portal-good);}
.dep2-row.is-copied{background:var(--portal-good-soft) !important;border-bottom-color:transparent;}
.dep2-row.is-copied .dep2-ic-copy{opacity:0;transform:scale(.4);}
.dep2-row.is-copied .dep2-ic-check{opacity:1;transform:scale(1);}
.dep2-row.is-copied .dep2-k,.dep2-row.is-copied .dep2-uid,.dep2-row.is-copied .dep2-net,.dep2-row.is-copied .dep2-amount{color:var(--portal-good) !important;transition:color var(--mo-fast,140ms) var(--mo-ease);}
.dep2-row--accent.dep2-row--copy:hover{background:color-mix(in srgb,var(--portal-accent) 16%,var(--portal-surface));}
.dep2-saveic{flex:none;width:44px;height:44px;display:inline-flex;align-items:center;justify-content:center;background:var(--portal-accent);color:var(--portal-accent-ink);border:0;border-radius:var(--portal-radius-sm,11px);cursor:pointer;transition:background var(--mo-fast,140ms) var(--mo-ease),transform var(--mo-fast,140ms) var(--mo-ease);}
.dep2-saveic svg{width:20px;height:20px;}
.dep2-saveic:hover{background:var(--portal-accent-dark);}
.dep2-saveic:active{transform:scale(.92);}

/* ===== Step 3 — JS-driven timeline states + reveal ===== */
.dep3-dot{transition:background var(--mo-base,210ms) var(--mo-ease),border-color var(--mo-base,210ms) var(--mo-ease),color var(--mo-base,210ms) var(--mo-ease),box-shadow var(--mo-base,210ms) var(--mo-ease),transform var(--mo-slow,340ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1));}
.dep3-node.is-done .dep3-dot{background:var(--portal-good);border-color:var(--portal-good);color:#fff;transform:scale(1.05);}
.dep3-node.is-now .dep3-dot{border-color:var(--portal-accent);color:var(--portal-accent-soft-ink);background:var(--portal-accent-soft);animation:dep-pulse 1.5s var(--mo-ease) infinite !important;}
.dep3-node.is-fail .dep3-dot{background:var(--portal-danger);border-color:var(--portal-danger);color:#fff;animation:none !important;}
.dep3-node.is-done .dep3-nlabel,.dep3-node.is-now .dep3-nlabel{color:var(--portal-text);}
.dep3-bar{transition:background var(--mo-slow,340ms) var(--mo-ease);}
.dep3-bar.is-done{background:var(--portal-good);}
.dep3-status{font-size:13px;color:var(--portal-muted);text-align:center;margin:18px 0 0;}
.dep3-success[hidden],.dep3-fail[hidden]{display:none !important;}
.dep3-fail{display:flex;align-items:center;gap:14px;background:color-mix(in srgb,var(--portal-danger) 11%,var(--portal-surface));border:1px solid color-mix(in srgb,var(--portal-danger) 30%,transparent);border-radius:14px;padding:16px 18px;margin-top:18px;animation:dep-pop var(--mo-slow,340ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1)) both !important;}
.dep3-fail-x{width:38px;height:38px;flex:none;border-radius:50%;background:var(--portal-danger);color:#fff;font-weight:800;font-size:20px;display:flex;align-items:center;justify-content:center;}
.dep3-fail>div{display:flex;flex-direction:column;gap:2px;}
.dep3-fail strong{font-size:15px;font-weight:800;color:var(--portal-danger);}
.dep3-fail span{font-size:var(--portal-text-sm);color:var(--portal-muted);}

/* ============================================================
   Balance top-up refresh: keep the published dep1/dep2/dep3 flow, but bring the
   surfaces, controls, buttons, typography, and status colors back to the
   wprovider glass system. Placed after the original deposit block so it wins over
   legacy global button fills, especially in dark mode.
   ============================================================ */
html .dep1-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    border-color: color-mix(in srgb, var(--portal-border) 88%, transparent);
    box-shadow: var(--portal-shadow);
    -webkit-backdrop-filter: blur(10px) saturate(145%);
    backdrop-filter: blur(10px) saturate(145%);
}
html .dep1-stepper {
    min-height: 28px;
    padding: 5px 12px;
    border: 1px solid color-mix(in srgb, var(--portal-accent) 34%, transparent);
    background-color: color-mix(in srgb, var(--portal-accent) 14%, transparent);
    background-image: var(--portal-sheen);
    background-size: 180% 180%;
    color: var(--portal-accent-soft-ink);
    font-size: var(--portal-text-xs);
    font-weight: 700;
    letter-spacing: .01em;
}
html .dep1-eyebrow,
html .dep2-k,
html .dep3-nlabel {
    font-family: var(--portal-font-ui);
    letter-spacing: .055em;
}
html .dep1-eyebrow {
    font-size: 11px;
    line-height: 1.35;
}
html .dep1-balance {
    font-size: clamp(32px, 3.1vw, 42px);
}
html .dep1-after,
html .dep1-due,
html .dep2-note,
html .dep3-status {
    font-size: var(--portal-text-sm);
    line-height: 1.5;
}
html .dep1-method,
html.dark .dep1-method,
html:not(.dark) .dep1-method,
html .dep1-amount,
html .dep1-menu,
html .dep2-input,
html .dep2-row--copy,
html .dep2-row--accent,
html .dep3-dot {
    background-color: color-mix(in srgb, var(--portal-card-glass) 86%, transparent);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 200% 200%;
    background-position: 85% -15%;
    border-color: var(--portal-border);
    color: var(--portal-text);
    box-shadow: none;
    -webkit-backdrop-filter: blur(8px) saturate(145%);
    backdrop-filter: blur(8px) saturate(145%);
}
html .dep1-method {
    min-height: 58px;
    padding: 11px 14px;
    gap: 12px;
}
html .dep1-method:hover {
    background-color: color-mix(in srgb, var(--portal-card-glass) 82%, var(--portal-hover));
    border-color: color-mix(in srgb, var(--portal-accent) 38%, var(--portal-border));
}
html .dep1-method-wrap.open .dep1-method {
    background-color: color-mix(in srgb, var(--portal-card-glass) 78%, var(--portal-surface));
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
html .dep1-logo {
    width: 34px;
    height: 34px;
    border-radius: 10px;
}
html .dep1-method-name {
    font-size: 14px;
    font-weight: 700;
    color: var(--portal-text);
}
html .dep1-method-sub {
    font-size: 12px;
    color: var(--portal-muted);
}
html .dep1-status-dot {
    width: 7px;
    height: 7px;
}
html .dep1-amount {
    min-height: 54px;
    padding: 11px 14px;
}
html .dep1-amount-input {
    font-size: 24px;
}
html .dep1-amount-suf {
    font-size: 12px;
}
html .dep1-chip,
html.dark .dep1-chip,
html:not(.dark) .dep1-chip {
    min-height: 32px;
    padding: 6px 13px;
    border: 1px solid var(--portal-border-soft);
    background-color: color-mix(in srgb, var(--portal-card-glass) 72%, transparent);
    background-image: var(--portal-sheen);
    background-size: 180% 180%;
    color: var(--portal-text);
    box-shadow: none;
    -webkit-backdrop-filter: blur(6px) saturate(140%);
    backdrop-filter: blur(6px) saturate(140%);
}
html .dep1-chip:hover {
    border-color: color-mix(in srgb, var(--portal-accent) 42%, var(--portal-border));
    color: var(--portal-accent-soft-ink);
}
html .dep1-chip.is-sel,
html .dep1-chip--exact.is-sel {
    background-color: color-mix(in srgb, var(--portal-accent) 18%, var(--portal-card-glass));
    border-color: color-mix(in srgb, var(--portal-accent) 36%, transparent);
    color: var(--portal-accent-soft-ink);
}
html .dep1-opt,
html.dark .dep1-opt,
html:not(.dark) .dep1-opt {
    color: var(--portal-text);
    background: transparent;
    box-shadow: none;
}
html .dep1-opt:hover {
    background: var(--portal-hover);
}
html .dep1-opt.is-active {
    background: color-mix(in srgb, var(--portal-accent) 14%, transparent);
}
html .dep1-continue,
html.dark .dep1-continue,
html:not(.dark) .dep1-continue,
html .dep2-saveic,
html.dark .dep2-saveic,
html:not(.dark) .dep2-saveic {
    position: relative;
    overflow: hidden;
    min-height: 44px;
    border: 1px solid color-mix(in srgb, var(--portal-accent) 64%, transparent);
    border-radius: 13px;
    /* Near-opaque so the deep accent reads as the BRAND. At the old 62% the
       forest green washed out to pale sage over a white card and dropped
       white-on-accent to ~3.5:1; the frost + beveled rim still carry the glass. */
    background-color: color-mix(in srgb, var(--portal-accent) 90%, transparent);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: 14px;
    font-weight: 750;
    -webkit-backdrop-filter: blur(7px) saturate(160%);
    backdrop-filter: blur(7px) saturate(160%);
    box-shadow:
        0 2px 10px rgba(0, 0, 0, .22),
        inset 2px 2px 0.6px -2.5px rgba(255, 255, 255, .75),
        inset -2px -2px 0.6px -2.5px rgba(255, 255, 255, .38),
        inset 1px 1px 1px -0.5px rgba(255, 255, 255, .42),
        inset -1px -1px 1px -0.5px rgba(255, 255, 255, .28),
        0 0 18px -4px color-mix(in srgb, var(--portal-accent) 52%, transparent);
}
html .dep1-continue:hover,
html .dep2-saveic:hover {
    filter: brightness(1.08);
    background-color: color-mix(in srgb, var(--portal-accent) 70%, transparent);
}
html .dep1-continue:active,
html .dep1-continue.is-pressing,
html .dep2-saveic:active,
html .dep2-saveic.is-pressing {
    transform: var(--mo-press);
    filter: brightness(.95);
}
html .dep1-continue--ghost,
html.dark .dep1-continue--ghost,
html:not(.dark) .dep1-continue--ghost {
    border-color: var(--portal-border);
    background-color: color-mix(in srgb, var(--portal-card-glass) 82%, transparent);
    color: var(--portal-text);
    box-shadow: var(--portal-shadow-sm);
}
html .dep1-continue--ghost:hover {
    background-color: color-mix(in srgb, var(--portal-card-glass) 70%, var(--portal-hover));
    color: var(--portal-accent-soft-ink);
}
html .dep2-row {
    padding-block: 13px;
}
html .dep2-row--copy {
    border: 1px solid transparent;
    border-bottom-color: var(--portal-border-soft);
}
html .dep2-row--copy:hover,
html .dep2-row--copy:focus-visible {
    background-color: color-mix(in srgb, var(--portal-accent) 13%, var(--portal-card-glass));
    border-color: color-mix(in srgb, var(--portal-accent) 30%, transparent);
}
html .dep2-row--copy:active,
html .dep2-row--copy.is-pressing {
    transform: var(--mo-press);
}
html .dep2-row--accent {
    border: 1px solid color-mix(in srgb, var(--portal-accent) 34%, transparent);
    background-color: color-mix(in srgb, var(--portal-accent) 14%, var(--portal-card-glass));
}
html .dep2-row--accent .dep2-k,
html .dep2-amount {
    color: var(--portal-accent-soft-ink);
}
html .dep2-uid,
html .dep2-amount {
    font-size: 18px;
}
html .dep2-saved {
    background: color-mix(in srgb, var(--portal-good) 13%, transparent);
    color: var(--portal-good);
    border: 1px solid color-mix(in srgb, var(--portal-good) 28%, transparent);
}
html .dep3-timeline {
    align-items: flex-start;
    padding: 10px 0 6px;
}
html .dep3-dot {
    width: 44px;
    height: 44px;
    border-radius: 14px;
    border-width: 1px;
}
html .dep3-icon {
    width: 22px;
    height: 22px;
}
html .dep3-node.is-done .dep3-dot,
html .dep3-node--done .dep3-dot {
    background-color: color-mix(in srgb, var(--portal-accent) 18%, var(--portal-card-glass));
    border-color: color-mix(in srgb, var(--portal-accent) 42%, transparent);
    color: var(--portal-accent-soft-ink);
    transform: scale(1.04);
}
html .dep3-node.is-now .dep3-dot,
html .dep3-node--now .dep3-dot {
    background-color: color-mix(in srgb, var(--portal-accent) 13%, var(--portal-card-glass));
    border-color: var(--portal-accent);
    color: var(--portal-accent-soft-ink);
}
html .dep3-bar {
    height: 2px;
    margin-top: 22px;
    border-radius: 999px;
    background: var(--portal-border-soft);
}
html .dep3-bar.is-done,
html .dep3-bar--done {
    background: color-mix(in srgb, var(--portal-accent) 76%, transparent);
}
html .dep3-success {
    border: 1px solid color-mix(in srgb, var(--portal-good) 32%, transparent);
    background-color: color-mix(in srgb, var(--portal-good) 12%, var(--portal-card-glass));
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 190% 190%;
    color: var(--portal-text);
    -webkit-backdrop-filter: blur(8px) saturate(145%);
    backdrop-filter: blur(8px) saturate(145%);
}
html .dep3-success-ck {
    background: color-mix(in srgb, var(--portal-good) 18%, transparent);
    border: 1px solid color-mix(in srgb, var(--portal-good) 36%, transparent);
    color: var(--portal-good);
}
html .dep3-success strong {
    color: var(--portal-text);
    font-size: var(--portal-text-lg);
}
html .dep3-success span {
    color: var(--portal-muted);
}
main:has(.dep1, .dep2, .dep3) .flash.success {
    border-color: color-mix(in srgb, var(--portal-good) 32%, transparent);
    background-color: color-mix(in srgb, var(--portal-good) 12%, var(--portal-card-glass));
    background-image: var(--portal-sheen);
    color: var(--portal-text);
    -webkit-backdrop-filter: blur(8px) saturate(145%);
    backdrop-filter: blur(8px) saturate(145%);
}

/* Standalone ?view=deposit page only — the finances page renders the SAME sheet
   inside its deposit dialog, and an unqualified :has() match there uncapped that
   page's width and stretched the dialog to viewport height. */
html main:has(.dep1--uid:not(.dep1--embedded)) {
    align-content: stretch;
}
html .dep1--uid:not(.dep1--embedded) {
    min-height: calc(100dvh - 116px);
    display: grid;
}
html .depuid-shell {
    display: grid;
    gap: 18px;
    align-content: start;
}
/* A top-up sheet is a short form — held to a form's measure so a 9-digit UID
   field doesn't stretch a metre wide on a desktop monitor. The dialog already
   constrains it; this gives the standalone page the same proportions. */
html .dep1--uid:not(.dep1--embedded) .depuid-shell {
    max-width: 560px;
}
html .depuid-hero,
html .depuid-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    border: 1px solid color-mix(in srgb, var(--portal-border) 88%, transparent);
    box-shadow: var(--portal-shadow);
    -webkit-backdrop-filter: blur(10px) saturate(145%);
    backdrop-filter: blur(10px) saturate(145%);
}
html .depuid-hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(230px, 320px);
    gap: 22px;
    align-items: stretch;
    border-radius: var(--portal-radius);
    padding: 24px 30px;
}
html .depuid-title {
    margin: 0;
    color: var(--portal-text);
    font-family: var(--portal-font-display);
    font-size: 28px;
    line-height: 1.12;
    font-weight: 800;
    letter-spacing: 0;
}
html .depuid-lead {
    max-width: 780px;
    margin: 12px 0 0;
    color: var(--portal-muted);
    font-size: 14px;
    line-height: 1.55;
}
html .depuid-balance {
    display: flex;
    min-width: 0;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end;
    text-align: right;
}
html .depuid-balance-line {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-top: 12px;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
}
html .depuid-balance-line strong {
    color: var(--portal-text);
    font-variant-numeric: tabular-nums;
}
html .depuid-grid {
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1.08fr) minmax(0, .92fr);
    grid-template-rows: minmax(218px, .92fr) minmax(228px, 1fr);
    gap: 18px;
    align-items: stretch;
}
html .depuid-card {
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    border-radius: var(--portal-radius);
    padding: 22px;
}
html .depuid-card-head {
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 0;
    margin-bottom: 14px;
}
html .depuid-card-head h3 {
    margin: 0;
    color: var(--portal-text);
    font-size: 17px;
    line-height: 1.2;
    font-weight: 800;
}
html .depuid-card-head .dep1-eyebrow {
    margin-bottom: 4px;
}
html .depuid-card-icon {
    width: 42px;
    height: 42px;
    flex: none;
}
html .depuid-card-icon .dep1-logo {
    width: 42px;
    height: 42px;
    border-radius: 12px;
}
html .depuid-muted {
    margin: 0;
    color: var(--portal-muted);
    font-size: 13px;
    line-height: 1.5;
}
html .depuid-form {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 10px;
    margin-top: auto;
    padding-top: 22px;
    align-items: center;
}
html .depuid-input {
    min-height: 44px;
}
html .depuid-save {
    min-width: 150px;
    justify-content: center;
}
html .depuid-requisite {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto 30px;
    gap: 14px;
    align-items: center;
    margin-top: auto;
    min-height: 66px;
    padding: 14px 16px;
    border-radius: 14px;
    border: 1px solid var(--portal-border);
    background-color: color-mix(in srgb, var(--portal-card-glass) 82%, transparent);
    background-image: var(--portal-sheen);
    -webkit-backdrop-filter: blur(8px) saturate(145%);
    backdrop-filter: blur(8px) saturate(145%);
}
html .depuid-requisite > span {
    color: var(--portal-muted);
    font-size: 12px;
    font-weight: 750;
    text-transform: uppercase;
    letter-spacing: .055em;
}
html .depuid-requisite--pending {
    grid-template-columns: 1fr;
    align-content: center;
    border-color: color-mix(in srgb, var(--portal-warn) 34%, transparent);
    color: var(--portal-warn);
}
html .depuid-requisite--pending strong {
    color: var(--portal-warn);
    font-size: 15px;
}
html .depuid-facts {
    display: grid;
    gap: 10px;
    margin-top: 10px;
}
html .depuid-facts > div {
    display: flex;
    justify-content: space-between;
    gap: 18px;
    align-items: center;
    min-height: 46px;
    padding: 11px 0;
    border-bottom: 1px solid var(--portal-border-soft);
}
html .depuid-facts > div:last-child {
    border-bottom: 0;
}
html .depuid-facts span {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
}
html .depuid-facts strong {
    color: var(--portal-text);
    font-size: var(--portal-text-base);
    text-align: right;
}
html .depuid-steps {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 14px;
    list-style: none;
    padding: 0;
    margin: 10px 0 0;
}
html .depuid-step {
    min-width: 0;
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    gap: 12px;
    align-items: flex-start;
    padding-top: 14px;
    border-top: 1px solid var(--portal-border-soft);
}
html .depuid-step-icon {
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 14px;
    color: var(--portal-accent-soft-ink);
    border: 1px solid color-mix(in srgb, var(--portal-accent) 38%, transparent);
    background-color: color-mix(in srgb, var(--portal-accent) 14%, var(--portal-card-glass));
}
html .depuid-icon {
    width: 22px;
    height: 22px;
}
html .depuid-step strong,
html .depuid-step span {
    display: block;
}
html .depuid-step strong {
    color: var(--portal-text);
    font-size: var(--portal-text-base);
    line-height: 1.35;
}
html .depuid-step span {
    margin-top: 4px;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.45;
}
html .dep1--uid {
    display: block;
}
html .depuid-shell {
    display: grid;
    gap: 18px;
    align-content: start;
    width: 100%;
}
/* Which rail this sheet uses — one compact identity row, not a masthead. */
html .depuid-method-row {
    display: flex;
    align-items: center;
    gap: var(--portal-space-2);
    min-width: 0;
}
html .depuid-method-row .dep1-logo-svg {
    display: block;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    border-radius: var(--portal-radius-xs);
}
html .depuid-method-id {
    min-width: 0;
    display: grid;
    gap: 1px;
}
html .depuid-method-id strong {
    color: var(--portal-text);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-base);
    line-height: 1.15;
    font-weight: var(--portal-weight-bold);
    letter-spacing: 0;
}
html .depuid-method-id span {
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
    line-height: 1.2;
}
/* A plain bordered panel — no sheen, no saturate, no drifting glow. The calm
   surface the redesign asks for (the shimmer/saturation was the "yellowish"). */
html .depuid-panel {
    display: grid;
    gap: var(--portal-space-4);
    padding: var(--portal-space-5);
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-md);
    background-color: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
}
/* The Bybit "contact your manager" honest-mode fallback now renders through the
   shared .ns-state service-state component (section 15), not a bespoke card. */
/* ---------------------------------------------------------------------------
   Deposit FIELD ROWS — the sheet's whole vocabulary, one shape reused.

   micro-label  →  value row  →  hint

   «Your UID» (an input + Save) and «our UID» (a value + Copy) are deliberately
   the SAME object: one you fill in, one you copy. It reads as an exchange rather
   than a lesson, and it is the shape the portal already uses on /login and in the
   operations filters — nothing new invented. This replaced numbered 34px step
   discs and an accent-gradient plaque carrying a 28px UID, which together pushed
   the dialog to 760px and made it scroll.
   --------------------------------------------------------------------------- */
html .dep-field {
    display: grid;
    gap: 6px;
    min-width: 0;
}
html .dep-field-label {
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
    line-height: 1.2;
    font-weight: var(--portal-weight-bold);
    letter-spacing: .08em;
    text-transform: uppercase;
}
/* The row itself: one bordered control, whatever sits inside it.
   A copy row is a <button>, and the legacy base sheet fills EVERY button with a
   solid accent under `html.dark button` — the known gotcha that drowns bare
   buttons in dark. Hence the html-prefixed pair, which outranks it in both
   themes. Do not collapse these back into a single `html .dep-field-row`. */
html:not(.dark) .dep-field-row,
html.dark .dep-field-row {
    display: flex;
    align-items: stretch;
    justify-content: space-between;
    gap: 0;
    width: 100%;
    min-width: 0;
    min-height: 44px;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    /* Translucent glass instead of a flat soft fill, with the same hairline
       top-highlight + soft shadow the rest of the site's controls carry. */
    background-color: color-mix(in srgb, var(--portal-glass) 78%, transparent);
    box-shadow: var(--portal-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .05);
    text-align: left;
    transition:
        border-color var(--portal-mo-fast) var(--portal-ease),
        box-shadow var(--portal-mo-fast) var(--portal-ease);
}
html .dep-field-row--copy:hover {
    box-shadow: var(--portal-shadow), inset 0 1px 0 rgba(255, 255, 255, .06);
}
html .dep-field-row--form:focus-within {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
html .dep-field-input {
    flex: 1 1 auto;
    min-width: 0;
    width: 100%;
    padding: 0 var(--portal-space-4);
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--portal-text);
    font-family: var(--portal-font-figure);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
    font-variant-numeric: tabular-nums lining-nums;
    outline: none;
}
html .dep-field-input::placeholder {
    color: color-mix(in srgb, var(--portal-muted) 86%, transparent);
    font-weight: var(--portal-weight-normal);
}
/* The read side of a row: the requisite itself, in the numeric face. Sized to
   read clearly at a glance without becoming a billboard. */
html:not(.dark) .dep-field-value,
html.dark .dep-field-value {
    display: flex;
    align-items: center;
    min-width: 0;
    padding: var(--portal-space-2) var(--portal-space-4);
    color: var(--portal-text);
    font-family: var(--portal-font-figure);
    font-size: var(--portal-text-lg);
    line-height: 1.3;
    font-weight: var(--portal-weight-bold);
    font-variant-numeric: tabular-nums lining-nums;
    overflow-wrap: anywhere;
}
/* Wallet addresses are long and character-exact — mono, one step down. */
html .dep-field--addr .dep-field-value {
    font-family: var(--portal-font-mono);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    white-space: nowrap;
    overflow: hidden;
    overflow-wrap: normal;
    text-overflow: ellipsis;
}
html .dep-field-hint {
    margin: 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.45;
}
/* Solid primary attached flush to the input — accent fill + accent-ink, which
   flips in dark. Same button as everywhere else, just square on one side.
   Selector matches the shape of the generic `.portal-modal button[type=submit]`
   pair (which fills at 62% alpha) so this wins on source order instead of a
   specificity arms race — the sheet's save button must read as solid as the
   «Пополнить» button that opened it. */
html:not(.dark) .portal-modal button.dep-field-action,
html.dark .portal-modal button.dep-field-action,
html .dep-field-action,
html.dark .dep-field-action {
    flex: none;
    min-width: 0;
    /* The legacy base sheet stretches EVERY button to width:100% under 760px,
       which collapsed the UID input to a sliver beside a full-width «Save». */
    width: auto;
    padding: 0 var(--portal-space-5);
    border: 0;
    /* A QUIET accent segment, not a loud filled pill: transparent over the row's
       glass, a hairline divider on the left, accent text. Matches the calmer
       balance hero — the deposit stops shouting with a saturated «Save». */
    border-left: 1px solid var(--portal-border);
    border-radius: 0;
    background: transparent;
    color: var(--portal-accent-soft-ink);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-bold);
    white-space: nowrap;
    cursor: pointer;
    transition:
        background-color var(--portal-mo-fast) var(--portal-ease),
        color var(--portal-mo-fast) var(--portal-ease);
}
html:not(.dark) .portal-modal button.dep-field-action:hover,
html.dark .portal-modal button.dep-field-action:hover,
html .dep-field-action:hover,
html.dark .dep-field-action:hover {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
html .dep-field-action:focus-visible {
    outline: none;
    box-shadow: var(--portal-focus-ring);
}
html .dep-field-action:active { transform: var(--portal-mo-press); }
/* A copy row is a real <button>, so Enter/Space work without any extra JS.
   Grid reserves the right-side copy lane, so long wallet addresses can wrap
   inside their own column instead of visually running under the copy control. */
html:not(.dark) .dep-field-row--copy,
html.dark .dep-field-row--copy {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    cursor: pointer;
}
@media (hover: hover) {
    html .dep-field-row--copy:hover { border-color: var(--portal-accent); }
}
html .dep-field-row--copy:focus-visible {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
/* Memo is the one genuinely dangerous field (a transfer without it is lost), so
   it — and only it — carries the warn tone. */
html .dep-field--warn .dep-field-label { color: var(--portal-warn); }
html .dep-field--warn .dep-field-row {
    border-color: color-mix(in srgb, var(--portal-warn) 58%, var(--portal-border));
}
html .dep-field--warn .dep-field-hint {
    color: color-mix(in srgb, var(--portal-warn) 82%, var(--portal-text));
}
/* Not-yet-configured operator requisite: stated plainly, no fake value. */
html .dep-field--pending .dep-field-value {
    color: var(--portal-warn);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-md);
}
/* The closing sentence of the sheet (what happens after you send). */
html .dep-sheet-foot {
    margin: 0;
    padding-top: var(--portal-space-3);
    border-top: 1px solid var(--portal-border-soft);
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.45;
}
/* The «Copy» half of a copy row — visually the twin of .dep-field-action, so the
   two rows match: a flush-attached control on the right edge of the field. Quiet
   by default (it is not the primary action of the screen), filled on hover. */
html:not(.dark) .depuid-copy-action,
html.dark .depuid-copy-action {
    position: relative;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--portal-space-2);
    padding: 0 var(--portal-space-4);
    border: 0;
    border-left: 1px solid var(--portal-border);
    border-radius: 0;
    background: transparent;
    color: var(--portal-accent-soft-ink);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-bold);
    white-space: nowrap;
    box-shadow: none;
    transition:
        background-color var(--portal-mo-fast) var(--portal-ease),
        color var(--portal-mo-fast) var(--portal-ease);
}
html .depuid-copy-action .dep2-copyic {
    margin-left: 0;
}
/* Hovering (or focusing) the copy row fills the control, so it clearly reads as
   the button to press — not just a static outline. */
@media (hover: hover) {
    html .dep-field-row--copy:hover .depuid-copy-action {
        background: var(--portal-accent);
        color: var(--portal-accent-ink);
    }
}
html .dep-field-row--copy:focus-visible .depuid-copy-action {
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
}
/* «Copied» label swaps in for «Copy» only while .is-copied is set. */
html .depuid-copy-label--done { display: none; }
html .is-copied .depuid-copy-label--idle { display: none; }
html .is-copied .depuid-copy-label--done { display: inline; }
html .is-copied .dep2-ic-copy {
    opacity: 0;
    transform: scale(.4);
}
html .is-copied .dep2-ic-check {
    opacity: 1;
    transform: scale(1);
}
/* On copy ONLY this control lights up green (with the check + «Скопировано») —
   the UID / address beside it keeps its own colour, so it is unmistakably THIS
   button that confirmed, not the whole row. Transparent bg overrides the hover
   fill for a clean green label even while the pointer is still on it. */
html .dep-field-row--copy.is-copied .depuid-copy-action {
    background: transparent;
    color: var(--portal-good);
}
/* Wallet addresses are exact, long strings; the copy action is icon-only here
   so the address gets the width and never fights a text label. */
html .dep-field--addr .depuid-copy-action {
    align-self: center;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    margin-right: var(--portal-space-1);
    padding: 0;
    border-left: 0;
    border-radius: var(--portal-radius-xs);
    color: var(--portal-muted);
}
html .dep-field--addr .depuid-copy-label {
    display: none;
}
html .dep-field--addr .depuid-copy-action .dep2-copyic {
    width: var(--portal-space-6);
    height: var(--portal-space-6);
}
@media (hover: hover) {
    html .dep-field--addr .dep-field-row--copy:hover .depuid-copy-action {
        background: var(--portal-surface-soft);
        color: var(--portal-text);
    }
}
html .dep-field--addr .dep-field-row--copy:focus-visible .depuid-copy-action {
    background: var(--portal-surface-soft);
    color: var(--portal-text);
}
html .dep-field--addr .dep-field-row--copy.is-copied .depuid-copy-action {
    background: transparent;
    color: var(--portal-good);
}
/* ---------------------------------------------------------------------------
   Deposit method selector + payment sheets + claim tracker (/portal/balance).
   The selector follows the asset/network list pattern used by crypto apps:
   logo, method title, short subline, and a right-aligned settlement/minimum
   column. The selected method opens its requisite sheet below.
   --------------------------------------------------------------------------- */
html .dep-pay {
    gap: 18px;
}
/* Payment rails: first asset, then network. No decorative border maze. */
html .dep-methods-switch {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--portal-space-4);
    align-self: stretch;
    width: 100%;
    max-width: 100%;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}
html .dep-selector-block {
    display: grid;
    gap: var(--portal-space-2);
    min-width: 0;
}
html .dep-selector-title {
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
}
html .dep-asset-switch {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--portal-space-2);
    min-width: 0;
}
html .dep-asset-pill {
    position: relative;
    display: block;
    min-width: 0;
}
html .dep-asset-pill input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
    pointer-events: none;
}
html .dep-asset-pill-body {
    box-sizing: border-box;
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    gap: var(--portal-space-2);
    min-height: var(--portal-space-8);
    min-width: 0;
    padding: var(--portal-space-2) var(--portal-space-3);
    overflow: hidden;
    border-radius: var(--portal-radius-sm);
    background: transparent;
    cursor: pointer;
    transition:
        background-color var(--portal-mo-fast) var(--portal-ease),
        color var(--portal-mo-fast) var(--portal-ease);
}
html .dep-method-pill-body::before {
    content: "";
    position: absolute;
    left: 0;
    top: var(--portal-space-2);
    bottom: var(--portal-space-2);
    width: calc(var(--portal-rule-w) * 3);
    border-radius: var(--portal-radius-pill);
    background: transparent;
}
@media (hover: hover) {
    html .dep-asset-pill input:not(:checked) + .dep-asset-pill-body:hover {
        background: var(--portal-hover);
    }
}
html .dep-asset-pill input:checked + .dep-asset-pill-body,
html .dep-method-pill input:checked + .dep-method-pill-body {
    color: var(--portal-text);
    background: color-mix(in srgb, var(--portal-surface-soft) 84%, transparent);
    box-shadow: none;
}
html .dep-method-pill input:checked + .dep-method-pill-body::before {
    background: var(--portal-accent);
}
html .dep-asset-pill input:focus-visible + .dep-asset-pill-body,
html .dep-method-pill input:focus-visible + .dep-method-pill-body {
    box-shadow: var(--portal-focus-ring);
}
html .dep-asset-ic {
    display: inline-flex;
    flex: none;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
}
html .dep-asset-ic .dep-brand-logo {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
html .dep-asset-copy {
    display: grid;
    min-width: 0;
    gap: var(--portal-rule-w);
}
html .dep-asset-title,
html .dep-asset-sub {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
html .dep-asset-title {
    color: var(--portal-text);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-bold);
}
html .dep-asset-sub {
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-medium);
}
html .dep-network-group {
    display: grid;
    gap: 0;
    min-width: 0;
}
html .dep-network-group[hidden] {
    display: none !important;
}
html .dep-methods-list-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: var(--portal-space-3);
    padding: 0 var(--portal-space-1) var(--portal-space-2);
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
}
html .dep-method-pill {
    position: relative;
    display: block;
    min-width: 0;
}
html .dep-method-pill input {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
    pointer-events: none;
}
html .dep-method-pill-body {
    box-sizing: border-box;
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--portal-space-3);
    min-height: calc(var(--portal-space-8) + var(--portal-space-4));
    width: 100%;
    min-width: 0;
    padding: var(--portal-space-2) var(--portal-space-2) var(--portal-space-2) var(--portal-space-4);
    overflow: hidden;
    border: 0;
    border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
    border-radius: var(--portal-radius-sm);
    background: transparent;
    color: var(--portal-text);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    white-space: normal;
    cursor: pointer;
    transition:
        color var(--portal-mo-fast) var(--portal-ease),
        background-color var(--portal-mo-fast) var(--portal-ease),
        border-color var(--portal-mo-fast) var(--portal-ease),
        box-shadow var(--portal-mo-fast) var(--portal-ease);
}
html .dep-method-pill:last-child .dep-method-pill-body {
    border-bottom-color: transparent;
}
/* Press = a background dip, never a scale (owner's rule). */
html .dep-method-pill-body:active {
    background: var(--portal-press);
}
@media (hover: hover) {
    html .dep-method-pill input:not(:checked) + .dep-method-pill-body:hover {
        color: var(--portal-text);
        background: var(--portal-hover);
    }
}
html .dep-method-pill input:checked + .dep-method-pill-body {
    border-bottom-color: transparent;
}
html .dep-method-ic {
    display: inline-flex;
    flex: none;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
}
html .dep-method-ic svg {
    display: block;
    width: 100%;
    height: 100%;
}
html .dep-method-ic .dep-brand-logo {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
}
/* The Bybit mark ships wrapped in the 38px .dep1-logo tile — cap the whole
   wrapper to the pill's icon slot so it aligns with the stroke glyphs. */
html .dep-method-ic--bybit .dep1-logo {
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    border-radius: var(--portal-radius-xs);
}
html .dep-method-ic--bybit .dep1-logo-svg {
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    border-radius: var(--portal-radius-xs);
}
html .dep-method-copy {
    display: grid;
    min-width: 0;
    gap: var(--portal-space-1);
}
html .dep-method-title,
html .dep-method-sub,
html .dep-method-meta-main,
html .dep-method-meta-sub {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
html .dep-method-title {
    color: var(--portal-text);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-bold);
}
html .dep-method-sub {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-medium);
}
html .dep-method-meta {
    display: grid;
    justify-items: end;
    min-width: max-content;
    gap: var(--portal-space-1);
    text-align: right;
}
html .dep-method-meta-main {
    color: var(--portal-text);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-bold);
}
html .dep-method-meta-sub {
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-medium);
}
html .dep-method-pane {
    display: grid;
    gap: var(--portal-space-4);
}
html .dep-method-pane[hidden] {
    display: none !important;
}
/* Chain-method tile in the pane header. The mark itself carries the brand; the
   tile stays neutral so the sheet does not become a coloured badge board. */
html .dep-net-logo {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    border: 1px solid var(--portal-border-soft);
    border-radius: var(--portal-radius-xs);
    background: var(--portal-surface);
    color: var(--portal-text);
}
html .dep-net-logo svg,
html .dep-net-logo .dep-brand-logo {
    width: var(--portal-space-6);
    height: var(--portal-space-6);
    object-fit: contain;
}
html .dep-brand-logo {
    display: block;
    overflow: visible;
}
/* Scannable QR of the deposit address (reuses the theme-safe .totp-qr-image
   base). Centered under the copyable address; the address text is the source of
   truth, the QR is a convenience. */
html .dep-addr-qr {
    display: flex;
    justify-content: center;
}
html .dep-addr-qr .totp-qr-image {
    max-width: 152px;
}
/* Classic payment-sheet rows: label left, value right, hairline between. */
html .dep-sheet-rows {
    display: grid;
    margin: 0;
}
html .dep-sheet-rows > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 11px 0;
    border-bottom: 1px solid var(--portal-border-soft);
}
html .dep-sheet-rows > div:first-child {
    border-top: 1px solid var(--portal-border-soft);
}
html .dep-sheet-rows dt {
    margin: 0;
    color: var(--portal-muted);
    font-size: 13px;
    font-weight: 600;
    white-space: nowrap;
}
html .dep-sheet-rows dd {
    margin: 0;
    min-width: 0;
    color: var(--portal-text);
    /* Values are figures/codes — the numeric face, per the type roles. */
    font-family: var(--portal-font-figure);
    font-size: var(--portal-text-base);
    font-weight: 700;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
}
/* Claim form: amount + optional TXID + liquid-glass submit. */
html .dep-claim {
    display: grid;
    gap: 12px;
}
html .dep-claim-title {
    margin: 0;
    color: var(--portal-muted);
    font-size: 11px;
    line-height: 1.2;
    font-weight: 850;
    letter-spacing: .08em;
    text-transform: uppercase;
}
html .dep-claim-form {
    display: grid;
    grid-template-columns: minmax(120px, 170px) minmax(0, 1fr) auto;
    gap: 10px;
    align-items: end;
}
html .dep-claim-field {
    display: grid;
    gap: 6px;
    min-width: 0;
}
html .dep-claim-field > span {
    color: var(--portal-muted);
    font-size: 11px;
    font-weight: 750;
    letter-spacing: .06em;
    text-transform: uppercase;
    white-space: nowrap;
}
html .dep-claim-input {
    box-sizing: border-box;
    min-height: 46px;
    width: 100%;
    padding: 0 14px;
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: color-mix(in srgb, var(--portal-surface-soft) 74%, transparent);
    color: var(--portal-text);
    font-family: var(--portal-font-figure);
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1, "lnum" 1;
    font-size: 14px;
    font-weight: 650;
    outline: none;
}
html .dep-claim-input::placeholder {
    color: color-mix(in srgb, var(--portal-muted) 86%, transparent);
    font-weight: 500;
}
html .dep-claim-input:focus {
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
/* No native number spinners — the system draws its own quiet fields. */
html .dep-claim-input[type="number"] {
    appearance: textfield;
    -moz-appearance: textfield;
}
html .dep-claim-input::-webkit-outer-spin-button,
html .dep-claim-input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
/* Explicit light+dark pair: the base sheet's `html.dark button` accent flood
   would otherwise repaint this button in dark mode. */
/* Standard solid primary CTA — same vocabulary as every other portal primary
   (accent fill + accent-ink), no glass/glow/saturate/specular. */
html .dep-claim-submit,
html.dark .dep-claim-submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 46px;
    padding: 0 20px;
    border: 0;
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-base);
    font-weight: 700;
    white-space: nowrap;
    box-shadow: none;
    cursor: pointer;
    transition: background-color var(--mo-fast) var(--mo-ease);
}
@media (hover: hover) {
    html .dep-claim-submit:hover,
    html.dark .dep-claim-submit:hover {
        background: var(--portal-accent-dark);
    }
}
html .dep-claim-submit:focus-visible,
html.dark .dep-claim-submit:focus-visible {
    outline: none;
    box-shadow: var(--portal-focus-ring);
}
html .dep-claim-submit:active,
html.dark .dep-claim-submit:active {
    transform: var(--portal-mo-press);
}
html .dep-claim-note {
    margin: 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.45;
}

@media (max-width: 600px) {
    html .dep1-card {
        padding: 18px 16px 20px;
    }
    html .dep1-method {
        min-height: 54px;
    }
    html .dep1-logo {
        width: 32px;
        height: 32px;
    }
    html .dep1-amount-input {
        font-size: 22px;
    }
    html .dep3-dot {
        width: 40px;
        height: 40px;
        border-radius: 13px;
    }
}

@media (max-width: 980px) {
    html .dep1--uid {
        min-height: auto;
    }
    html .depuid-panel {
        padding: 24px;
    }
}

@media (max-width: 600px) {
    /* Clears the mobile tab bar — the dialog floats above it, so not embedded. */
    html .dep1--uid:not(.dep1--embedded) {
        padding-bottom: calc(76px + env(safe-area-inset-bottom));
    }
    html .depuid-shell {
        gap: 12px;
    }
    html .depuid-method-row {
        flex-wrap: wrap;
        gap: 10px 13px;
    }
    html .depuid-panel {
        gap: 14px;
        padding: 16px;
        border-radius: 14px;
    }
    /* Field rows stay one line on mobile — the value simply gets a step smaller
       so a 9-digit UID and the copy control still share the row. */
    html .dep-field-row {
        min-height: 46px;
    }
    html .dep-field-value {
        font-size: var(--portal-text-md);
        padding: var(--portal-space-2) var(--portal-space-3);
    }
    html .dep-field-input {
        padding: 0 var(--portal-space-3);
        font-size: var(--portal-text-base);
    }
    html .dep-field-action,
    html .depuid-copy-action {
        padding: 0 var(--portal-space-3);
        font-size: var(--portal-text-sm);
    }
    html .dep-pay {
        gap: 14px;
    }
    html .dep-methods-switch {
        grid-template-columns: 1fr;
    }
    html .dep-method-pill-body {
        min-height: calc(var(--portal-space-8) + var(--portal-space-3));
        padding: var(--portal-space-2) var(--portal-space-2) var(--portal-space-2) var(--portal-space-4);
    }
    html .dep-asset-pill-body {
        grid-template-columns: auto auto;
        justify-content: center;
        min-height: var(--portal-space-8);
        padding: var(--portal-space-2);
    }
    html .dep-asset-sub {
        display: none;
    }
    html .dep-method-pane {
        gap: 14px;
    }
    html .dep-net-logo {
        width: var(--portal-space-7);
        height: var(--portal-space-7);
        border-radius: var(--portal-radius-xs);
    }
    html .dep-net-logo svg,
    html .dep-net-logo .dep-brand-logo {
        width: var(--portal-space-6);
        height: var(--portal-space-6);
    }
    html .dep-field--addr .dep-field-value {
        font-size: var(--portal-text-sm);
    }
    /* The claim form stacks: amount, TXID, full-width submit. */
    html .dep-claim-form {
        grid-template-columns: 1fr;
        gap: 12px;
    }
    html .dep-claim-submit {
        width: 100%;
        min-height: 48px;
    }
    html .dep-sheet-rows > div {
        padding: 9px 0;
    }
}

/* ===================== Catalog storefront ===================== */
.cat-page{display:block;}
/* `.cat-back` is a BEHAVIOUR marker (portal.js reads it to restore the grid's
   scroll position), not a look — the look is `.detail-back`. All it owns is the
   gap down to whatever heading follows. */
.cat-back{margin:0 0 12px;}
.cat-heading{margin:0 0 18px;}
.cat-heading h2{font-size:clamp(28px,2vw,34px);font-weight:760;line-height:1.08;letter-spacing:-.01em;margin:0 0 6px;display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
.cat-heading p{font-size:15px;color:var(--portal-muted);margin:0;font-weight:600;}
.cat-toolbar{display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap;}
/* Toolbar = one slim row above the grid: the live counter on the left, quiet
   text-style dropdowns (stock/sort, no boxes) and the cart chip on the right —
   the marketplace pattern; the sidebar carries the actual filters. */
.cat-toolbar-row{display:flex;align-items:center;justify-content:flex-end;gap:16px;min-height:40px;margin-bottom:12px;flex-wrap:wrap;}
.cat-toolbar-row .cat-result-line{margin:0 auto 0 2px;text-align:left;}
.cat-sort{display:inline-flex;align-items:center;gap:9px;color:var(--portal-text);}
.cat-sort-icon{width:17px;height:17px;flex:none;color:var(--portal-muted);}
/* The sort trigger is a glass chip — the same liquid material as the balance
   chip (glass fill + drifting sheen + glow), stated as an explicit light/dark
   pair so the base sheet's solid button fills can't bleed through. */
html:not(.dark) .cat-sort .portal-select-button,
html.dark .cat-sort .portal-select-button{border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);min-height:38px;padding:0 13px;font-size:13px;font-weight:700;color:var(--portal-text);box-shadow:none;background-color:var(--portal-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;transition:color var(--mo-fast) var(--mo-ease),border-color var(--mo-fast) var(--mo-ease);}
html:not(.dark) .cat-sort .portal-select-button:hover,
html.dark .cat-sort .portal-select-button:hover{color:var(--portal-accent-soft-ink);border-color:color-mix(in srgb,var(--portal-accent) 45%,var(--portal-border));}
html:not(.dark) .cat-sort .portal-select-button:focus-visible,
html.dark .cat-sort .portal-select-button:focus-visible{box-shadow:var(--portal-focus-ring);}
.cat-sort .portal-select-menu{left:auto;right:0;min-width:250px;}
/* THE BACK CONTROL — one per portal, chevron plus the destination's name.
   Muted at rest, accent on hover; a tap shows NO ring (only keyboard
   :focus-visible does, for a11y).

   There were three of these. `.cat-back-round` drew a bare 32px chevron with no
   label on the catalog mastheads, and `.cat-back` drew a bordered grey chip on
   the catalog list pages — so one gesture had three drawings, and the catalog's
   was the only one that never said where it went (the destination lived in a
   title/aria-label, i.e. nowhere a mouse user looks). Both are gone; the
   catalog now renders this component with a `cat-back` marker class that
   portal.js keys on for scroll restoration. Do not add a fourth. */
.detail-back-chev{flex:none;display:grid;place-items:center;border:0;background:none;box-shadow:none;color:var(--portal-muted);border-radius:8px;transition:color var(--mo-fast) var(--mo-ease);-webkit-tap-highlight-color:transparent;}
.detail-back-chev svg{width:24px;height:24px;}
.detail-back:hover .detail-back-chev{color:var(--portal-accent-soft-ink);background:none;}
.detail-back:active .detail-back-chev{color:var(--portal-accent-soft-ink);}
.detail-back{display:inline-flex;align-items:center;gap:8px;text-decoration:none;color:var(--portal-muted);font-size:var(--portal-text-base);font-weight:var(--portal-weight-semibold);-webkit-tap-highlight-color:transparent;}
.detail-back:hover{color:var(--portal-accent-soft-ink);text-decoration:none;}
.detail-back:focus{outline:none;}
.detail-back:focus-visible{outline:none;}
.detail-back:focus-visible .detail-back-chev{box-shadow:var(--portal-focus-ring);}
/* Brand masthead: logo plate + name on the left, the toolbar on the SAME line
   to the right — one aligned row instead of a heading floating over a gap. */
.cat-brand-head{display:flex;align-items:center;justify-content:space-between;gap:18px;margin:4px 0 18px;flex-wrap:wrap;}
.cat-brand-id{display:flex;align-items:center;gap:14px;min-width:0;}
.cat-brand-mark{flex:none;display:grid;place-items:center;width:48px;height:48px;border:1px solid var(--portal-border);border-radius:13px;background:linear-gradient(165deg,#fdfdfe,#eef0f6);box-shadow:var(--portal-shadow-sm);}
.cat-brand-mark img{max-width:68%;max-height:68%;object-fit:contain;}
/* A COUNTRY FLAG in the masthead plate (eSIM leaves) renders as the same compact
   rounded chip the selection list uses — not a bare rectangle stretched across
   the plate. */
/* eSIM masthead — the country flag IS the brand mark here, so it gets the top of
   the ladder. 30x23 was not 4:3 and cover cropped it; 36x27 is exact. */
/* The masthead plate is light in BOTH themes, so this takes the non-flipping
   plate ring — the theme-flipping one resolved to white-on-white here. */
.cat-brand-mark img[src*="/flags/"]{width:36px;height:auto;aspect-ratio:4/3;max-width:none;max-height:none;object-fit:cover;border-radius:4px;box-shadow:0 0 0 1px var(--portal-flag-ring-plate);}
.cat-brand-mark img[src*="/ea.png"],
.cat-brand-mark img[src*="/blizzard-mark"],
.cat-brand-mark img[src*="/pokemon-mark"]{max-width:84%;max-height:84%;}
.cat-brand-name{min-width:0;display:grid;gap:2px;}
/* padding-bottom gives descenders (the "g" in "gift card") room before the
   overflow:hidden clip edge — a tight line-height:1.1 otherwise shaves them. */
.cat-brand-name h2{margin:0;font-family:var(--portal-heading-font);font-size:clamp(24px,1.8vw,30px);font-weight:780;line-height:1.1;letter-spacing:-.015em;color:var(--portal-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding-bottom:3px;}
.cat-brand-sub{margin:0;font-size:13px;font-weight:650;color:var(--portal-muted);}
/* The live result count now lives UNDER the brand title as a subtitle — drop its
   standalone margins so it aligns to the title's left edge and sits tight to it. */
.cat-brand-name .cat-result-line{margin:2px 0 0;}
.cat-brand-head .cat-toolbar-row{margin:0;flex:1 1 auto;min-height:0;}
/* Region cards: the flag + region NAME lead — one glance tells the editions
   apart (the brand logo already owns the masthead). Same liquid glass. */
.cat-region-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(235px,1fr));gap:14px;}
html .cat-region{display:flex;align-items:center;gap:13px;min-height:76px;padding:14px 16px;border:1px solid var(--portal-border);border-radius:var(--portal-radius,16px);text-decoration:none;color:var(--portal-text);box-shadow:var(--portal-shadow);background-color:var(--portal-card-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;transition:border-color var(--mo-fast) var(--mo-ease),box-shadow var(--mo-base) var(--mo-ease);}
html .cat-region[hidden]{display:none !important;}
/* The neutral hairline is always there. The ACCENT (сиреневая) border only lights
   up on a real interaction, never a stray touch: on desktop it warms on hover
   (mouse only — gated so a sticky touch-hover can't leave it stuck); on touch it
   appears via .is-pressing, which portal.js adds ONLY on a genuine press and drops
   the instant a finger moves (scroll/brush) — so an accidental swipe shows nothing,
   and a real tap keeps the accent through the page transition. No scale/dip. */
@media (hover:hover){html .cat-region:hover{border-color:var(--portal-accent);text-decoration:none;}}
html .cat-region.is-pressing{border-color:var(--portal-accent);}
/* ---------------------------------------------------------------------------
   FLAGS — one primitive, one geometry.

   Every asset in static/flags/ is a flag-icons vector on a uniform
   viewBox="0 0 640 480": exactly 4:3, all 271 of them, no exceptions. So the one
   rule that matters is that the BOX is 4:3 as well. Get that right and the
   object-fit question disappears (fill/cover/contain all agree), which is what
   removes the crop the select pills had at 26x19, the 2.5% horizontal stretch
   the region cards had at 26x20, and the transparent letterbox slivers that let
   a light mat bleed along the flag's edge.

   The hairline is a NON-INSET spread shadow, never a border. A border with
   box-sizing:border-box eats 2px from each dimension — 18.5% of a 24x18 flag —
   and an inset shadow paints over the outermost row of artwork. A spread shadow
   sits entirely outside the border box and costs zero pixels of picture.

   Radius stays at or below height/10. At 4px on a 21px-tall flag each corner
   clips a quarter-disc, and on AU/US/NZ that corner IS the canton.

   Size ladder — all exact 4:3, all landing on whole device pixels at 2x:
     24x18 inline + dropdown options · 28x21 tile badge · 36x27 region card
   Below ~28px wide the AU fimbriation drops under one device pixel and the
   Southern Cross stars go to mush, which is why the badge is the size it is.

   Keyed on the src rather than a class so it also catches the eSIM masthead,
   which renders a bare <img> with no flag class of its own. */
img[src*="/flags/"]{
    display:block;
    width:24px;
    height:auto;
    aspect-ratio:4/3;
    max-width:none;
    max-height:none;
    border:0;
    /* APP-ICON PROPORTION, HOUSE MATERIAL.
       The radius is ~28% of the height — the proportion an iOS icon uses, and
       the thing that actually makes a small rectangle read as an object rather
       than as a printed swatch. Every size below re-states it so the ratio holds
       instead of a fixed px drifting off-proportion as the chip grows.
       What sits ON that shape is deliberately NOT the iOS glass bevel (specular
       top + shaded bottom): this product's material is already written down —
       "deep ink, lit edge" — so the chip gets ONE hairline with a lit top edge
       and a low, tight shadow. Same restraint as every other surface here:
       hierarchy on line weight, not on stacked gloss. */
    /* The radius now MATCHES THE ARTWORK. twemoji draws its own ~15%-of-height
       corner radius into every flag and leaves the corners transparent, so this
       value is no longer a style choice — it is what keeps the hairline ring
       running along the flag's actual edge instead of cutting a squarer path
       across its rounded corner. 3px on an 18px-tall chip is that 15%; the
       28 and 36px steps restate it so the ratio holds as the chip grows. */
    border-radius:3px;
    object-fit:cover;
    /* OUTER ring, and this is a bug fix, not a taste change. This rule used to
       say `inset`, on the reasoning that an edge drawn inside the flag reads as
       the flag's own edge rather than as a frame around a sticker. That reads
       well and never rendered: an inset box-shadow on a REPLACED element is
       painted underneath the replaced content, so on an opaque flag it was
       hidden by the artwork itself. Verified in a browser across BH/JP/FI/PL/AE
       — zero visible edge on any of them, which is exactly why the owner kept
       reporting flags bleeding into the plate (2026-08-04).
       An outer 1px hairline is visible AND costs no artwork, where the inset one
       would have cost the outermost pixel row had it worked at all. */
    box-shadow:0 0 0 1px var(--portal-flag-ring);
}

/* The region flag sits ON the card, it is not an object resting on a tray. The
   40x40 rounded plate (border + soft fill + 11px radius) around a 28px rounded
   flag was a chip inside a chip — the most app-icon-looking thing on the page.
   The box is kept at 40x40 purely so the row's metrics do not move, but its
   chrome is gone; the globe fallback below still needs the centring. */
.cat-region-flag{flex:none;display:grid;place-items:center;width:40px;height:40px;border:0;border-radius:0;background:none;}
.cat-region-flag img[src*="/flags/"]{width:28px;border-radius:3px;}
.cat-region-flag svg{display:block;width:20px;height:20px;color:var(--portal-muted);}
.cat-region-body{min-width:0;display:grid;gap:2px;}
.cat-region-body strong{font-family:var(--portal-heading-font);font-size:15px;font-weight:800;letter-spacing:-.01em;line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-region-body small{font-size:12px;font-weight:600;color:var(--portal-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-region-chev{flex:none;margin-left:auto;width:16px;height:16px;color:var(--portal-muted);opacity:.7;transition:color var(--mo-fast) var(--mo-ease),transform var(--mo-fast) var(--mo-out),opacity var(--mo-fast) var(--mo-ease);}
@media (hover:hover){.cat-region:hover .cat-region-chev{color:var(--portal-accent-soft-ink);opacity:1;transform:translateX(3px);}}
.cat-sort .dropdown-option{display:flex;align-items:center;gap:10px;}
/* The active sort carries a check, like the reference menus. */
.cat-sort .dropdown-option.active::after{content:"";flex:none;margin-left:auto;width:15px;height:15px;background:var(--portal-accent);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 13l5 5 11-12'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 13l5 5 11-12'/%3E%3C/svg%3E") center/contain no-repeat;}
/* Faceted layout: a sticky filter sidebar (the marketplace pattern — checkbox
   sections with per-option counts and a selected-count badge) beside the grid. */
.cat-page--faceted{display:grid;grid-template-columns:268px minmax(0,1fr);gap:18px;align-items:start;}
/* The filter rail is pinned below the 60px sticky topbar and scrolls on its OWN
   when the list is long (e.g. the Xbox "Игра" list), so the product grid scrolls
   independently instead of the two moving as one block. overscroll-behavior stops
   the page from scrolling once the rail hits its end. */
.cat-side{display:grid;align-content:start;gap:14px;position:sticky;top:76px;min-width:0;padding:2px 10px;}
/* The grid column starts level with the SORT facet's divider line, not with the
   sidebar's own top: the first card's top edge must sit ON that line («карточки
   начинаются с первой черточки»). 32px = .cat-side padding-top (2px) +
   .cat-facet-head min-height (30px, border-box) — change those, change this. */
.cat-main{min-width:0;padding-top:32px;}
/* Facet section: a BARE region, not a floating glass box. The filter column now
   reads as one quiet instrument panel — sections held by a title-rule and space
   (the "Rule & Light" hand), not three frosted cards with shadows. */
html .cat-facet{border:0;border-radius:0;box-shadow:none;padding:0;background:none;-webkit-backdrop-filter:none;backdrop-filter:none;animation:none !important;}
/* Scroll performance: a brand/region page paints dozens of these cards at once,
   and a per-card backdrop-filter + a perpetual liquid-glow animation forced the
   compositor to re-blur 40+ layers every frame — that was the scroll jank. Over
   the solid app background the 2.5px blur is a visual no-op, so drop the blur and
   the animation on the high-count cards (the static glass tint + sheen remain).
   Also clears a backdrop-filter clip artifact inside the scrolling filter rail. */
/* PERF: kill live backdrop-blur + the perpetual liquid-glow on every catalog surface
   that scrolls or sticks — each re-blurred/re-painted per frame while flicking the
   grid (multiplied by cards on screen). The static fill + sheen carry the frosted
   look (the 2.5px blur was a visual no-op over the near-uniform canvas — the pattern
   already shipped for .cat-region/.cat-facet, now extended to the surfaces it missed:
   the sticky buybar, package cards, the Steam card, and the cart/plan chips). */
html .cat-region, html .cat-facet,
html .cat-buybar, html .cat-pkg-card, html .steam-card,
html .cat-cart-chip, html .cat-plan-chip{-webkit-backdrop-filter:none !important;backdrop-filter:none !important;animation:none !important;}
/* min-width:0 down the whole chain (card → list → row) so a long option label
   ("Assassin's Creed Black Flag Resynced") can't force the rail wider than its
   248px track — the label ellipsis-truncates instead of spilling over the grid. */
.cat-facet{min-width:0;}
.cat-facet-head{display:flex;align-items:center;gap:9px;min-height:30px;font-family:var(--portal-font-ui);font-size:var(--portal-text-xs);font-weight:var(--portal-weight-semibold);letter-spacing:.14em;text-transform:uppercase;color:var(--portal-muted);min-width:0;padding-bottom:9px;margin-bottom:6px;border-bottom:var(--portal-rule-w-strong) solid var(--portal-border-strong);}
.cat-facet-head .cat-sort-icon{width:16px;height:16px;flex:none;color:var(--portal-muted);}
/* Selected-count badge: a SOLID accent circle with a white digit — the same vocabulary
   as the checked filter checkbox, so it reads as part of the control rather than a pale
   foreign chip. Perfect 20px circle for one digit, an evenly-padded pill for two+ ("10");
   UI face + proportional figures so a lone «1» sits optically centred. */
/* Active-filter signal — NO number at all (a floating digit read as unclear and
   sat misaligned). The single, unambiguous cue is the facet TITLE lighting up
   accent when the section has active filters; the count element is hidden and
   only drives that state. */
.cat-facet-badge{display:none;}
.cat-facet-head:has(.cat-facet-badge:not([hidden])) .cat-facet-title{color:var(--portal-accent-soft-ink);}
.cat-facet-badge[hidden]{display:none;}
/* Sort options in the sidebar: quiet rows, the active order carries a check. */
html .cat-sortopt{display:flex;align-items:center;gap:10px;min-height:34px;padding:0 6px;margin:0 -6px;border-radius:9px;font-size:var(--portal-text-base);font-weight:600;color:var(--portal-text);text-decoration:none;transition:background-color var(--mo-fast) var(--mo-ease),color var(--mo-fast) var(--mo-ease);}
html .cat-sortopt:hover{background:var(--portal-hover);color:var(--portal-accent-soft-ink);text-decoration:none;}
html .cat-sortopt.active{color:var(--portal-accent-soft-ink);font-weight:750;}
html.dark .cat-sortopt.active{color:var(--portal-accent-soft-ink);}
.cat-sortopt.active::after{content:"";flex:none;margin-left:auto;width:14px;height:14px;background:var(--portal-accent);-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 13l5 5 11-12'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 13l5 5 11-12'/%3E%3C/svg%3E") center/contain no-repeat;}
.cat-facet-search{width:100%;min-height:36px;margin:var(--portal-space-3) 0 var(--portal-space-3);padding:0 11px;font-size:13px;}
.cat-facet-list{display:grid;grid-template-columns:minmax(0,1fr);gap:1px;min-width:0;}
.cat-facet-row{display:flex;align-items:center;gap:10px;min-width:0;min-height:36px;padding:0 6px;margin:0 -6px;border-radius:9px;cursor:pointer;font-size:var(--portal-text-base);font-weight:600;color:var(--portal-text);transition:background-color var(--mo-fast) var(--mo-ease);}
.cat-facet-row[hidden]{display:none;}
.cat-facet-row:hover{background:var(--portal-hover);}
.cat-facet-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-facet-count{margin-left:auto;margin-right:8px;flex:none;color:var(--portal-muted);font-family:var(--portal-font-figure);font-size:12px;font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;}
/* The checkbox itself: quiet square, solid accent + white check when on.
   min/max pin the box to 19px — the base sheet's generic `input` rules carry
   min-heights (32-44px) that would stretch it into a pill otherwise. */
html input[type="checkbox"][data-facet]{appearance:none;-webkit-appearance:none;flex:none;width:19px;height:19px;min-width:19px;max-width:19px;min-height:19px;max-height:19px;margin:0;padding:0;border:1.5px solid var(--portal-border);border-radius:6px;background:var(--portal-surface-soft);cursor:pointer;transition:background-color var(--mo-fast) var(--mo-ease),border-color var(--mo-fast) var(--mo-ease);}
html input[type="checkbox"][data-facet]:hover{border-color:color-mix(in srgb,var(--portal-accent) 55%,var(--portal-border));}
html input[type="checkbox"][data-facet]:focus-visible{outline:none;box-shadow:var(--portal-focus-ring);}
/* An ACCENT-coloured focus halo around a box that is already filled with the
   accent does not read as focus — it reads as a second, outer selection, i.e.
   the checkbox looks checked twice. That is the "double tick" the owner
   reported as a bug (2026-08-04), and it lands on every filter click, because
   Safari keeps :focus-visible on a checkbox after a pointer click.
   The indicator stays (removing it would strand keyboard users) but goes
   NEUTRAL when the box is filled, so it reads as "focused" against the green
   rather than as another green ring stacked on a green square. */
html input[type="checkbox"][data-facet]:checked:focus-visible{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--portal-text) 16%,transparent);
}
html input[type="checkbox"][data-facet]:checked{background:var(--portal-accent) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 13l4 4 10-10'/%3E%3C/svg%3E") center/12px 12px no-repeat;border-color:var(--portal-accent);}
/* "Show all (N)" fold: a distinct, centred control set off from the list by a
   hairline divider — symmetric on every width (was a left-jammed bare link). */
/* "Show all (N)" fold — a real, defined button, not bare accent text that blends
   into the list. A soft accent fill with an accent-line edge reads clearly as
   clickable in both themes; it deepens on press. */
html:not(.dark) .cat-facet-more,
html.dark .cat-facet-more{display:block;width:100%;margin:var(--portal-space-4) 0 var(--portal-space-1);padding:10px 0;border:var(--portal-rule-w) solid var(--portal-accent-line);border-radius:var(--portal-radius-sm);background:var(--portal-accent-soft);box-shadow:none;min-height:0;cursor:pointer;text-align:center;color:var(--portal-accent-soft-ink);font-size:12px;font-weight:700;letter-spacing:.02em;transition:background-color var(--mo-fast) var(--mo-ease),color var(--mo-fast) var(--mo-ease);}
html:not(.dark) .cat-facet-more:hover,
html.dark .cat-facet-more:hover{background:color-mix(in srgb,var(--portal-accent) 20%,transparent);color:var(--portal-accent-soft-ink);text-decoration:none;transform:none;box-shadow:none;filter:none;}
.cat-facet-more:active{transform:var(--portal-mo-press);}
.cat-facet-more:focus-visible{outline:none;box-shadow:var(--portal-focus-ring);}
/* Live result counter above the grid — appears only while filters narrow it. */
.cat-result-line[hidden]{display:none;}
.cat-result-line{display:block;text-align:left;margin:0 2px 12px;font-size:var(--portal-text-sm);font-weight:650;color:var(--portal-muted);font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;}
/* Facet header chevron — only shown on mobile, where the facets become
   tap-to-open accordions (hidden on desktop, where they are always expanded). */
.cat-facet-chev{display:none;flex:none;margin-left:auto;width:18px;height:18px;color:var(--portal-muted);transition:transform var(--mo-base) var(--mo-ease);}
@media (max-width: 940px){
  .cat-page--faceted{grid-template-columns:minmax(0,1fr);}
  /* One column: the sidebar sits ABOVE the grid, so the desktop line-up offset
     would just punch a hole between them. */
  .cat-main{padding-top:0;}
  /* Mobile: the rail is a static row of tap-to-open bars — drop the desktop
     pinned-scroll (no own scrollbar, full height). */
  .cat-side{position:static;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));align-items:start;gap:10px;max-height:none;overflow:visible;padding:0;}
  /* Filters collapse into compact tap-to-open bars so they stop eating the
     screen; the header (title + selected badge + chevron) stays ~46px tall.
     `html .cat-side` outranks the base `html .cat-facet{padding:4px 14px 12px}`,
     whose 4px-top / 12px-bottom otherwise pushes the collapsed title above centre. */
  html .cat-side .cat-facet{padding:0 14px;}
  .cat-facet-head{cursor:pointer;-webkit-user-select:none;user-select:none;min-height:46px;}
  .cat-facet-chev{display:block;}
  .cat-facet .cat-facet-list,.cat-facet .cat-facet-search{display:none;}
  html .cat-side .cat-facet.is-open{padding:0 14px 12px;}
  .cat-facet.is-open .cat-facet-list{display:grid;}
  .cat-facet.is-open .cat-facet-search{display:block;}
  .cat-facet.is-open .cat-facet-chev{transform:rotate(180deg);color:var(--portal-accent-soft-ink);}
}
/* Denomination table (NS Cabinet layout): qty + buy inline per row. Money is
   set in the plain table type (Switzer 13/400, same cells as Transactions —
   only tabular digits added), the buy button wears the standard quiet-control
   spec (the sidebar-collapse vocabulary). */
.cat-buy-wrap{margin-top:2px;}
/* The facet/search filters set [hidden] on rows; the mobile grid display below beats
   the UA [hidden]{display:none} (any author display wins over UA), so filters looked
   dead on phones — restore hiding explicitly. */
.cat-buy-table tr[hidden]{display:none !important;}
/* The product name speaks in the same voice as the region/brand tile titles
   («Австралия», «Австрия»): the UI face at 640 with the tiles' tight tracking. */
.cat-buy-table .cat-prod-name{font-weight:640;letter-spacing:-.008em;}
/* Units a step smaller than their figures («5 ГБ», «30 дн.») — full-size
   uppercase «ГБ» reads BIGGER than the digit beside it and breaks the rhythm. */
.cat-prod-detail .cat-unit{font-size:.82em;}
.cat-buy-table td{vertical-align:middle;}
/* Public product number under the name (returned by request) — quiet, tabular. */
.cat-prod-id{display:block;margin-top:2px;font-size:var(--portal-text-xs);font-weight:600;color:var(--portal-muted);font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;white-space:nowrap;}
.cat-buy-table .cat-price-cell{white-space:nowrap;}
.cat-price-val{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;white-space:nowrap;}
.cat-stock{color:var(--portal-good,#3f8a5b);font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;}
.cat-stock--na{color:var(--portal-muted);}
.cat-stock--out{color:var(--portal-muted);}
.cat-stock-chip{display:none;}   /* sold-out state word for the PHONE pill; desktop keeps its stock column */
.cat-qty{width:82px;min-height:38px;padding:0 10px;background:var(--portal-surface-soft);border:1px solid var(--portal-border);border-radius:9px;color:var(--portal-text);font-variant-numeric:tabular-nums;}
.cat-qty:focus{outline:none;border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);}
.cat-action-cell{text-align:right;}
/* html-prefixed so the quiet-control spec outranks the base sheet's solid
   `html.dark button { background: var(--portal-accent) }` rule. */
html .cat-buy-btn, html.dark .cat-buy-btn{display:inline-flex;align-items:center;justify-content:center;min-height:38px;padding:0 16px;border-radius:11px;font-size:13px;font-weight:600;color:var(--portal-muted);border:1px solid var(--portal-border);background-color:var(--portal-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;box-shadow:var(--portal-shadow-sm);cursor:pointer;text-decoration:none;transition:color var(--mo-fast,140ms) var(--mo-ease),border-color var(--mo-fast,140ms) var(--mo-ease);}
html .cat-buy-btn:hover, html.dark .cat-buy-btn:hover{color:var(--portal-accent-soft-ink);border-color:var(--portal-accent);background-color:var(--portal-glass);}
/* ── MOBILE (≤760px): the leaf denomination table is NOT special-cased any
   more — it renders as the desktop table and scrolls sideways, exactly like
   Transactions & Orders. The shared rule lives near the end of this file
   («Data tables keep the desktop layout on phones»). */
/* Leaf masthead on phones: the two filter chips drop below the logo + title and
   split the width evenly, reading as one deliberate filter bar instead of two
   small buttons floating under a truncated heading. */
@media (max-width: 760px){
  .cat-brand-head{gap:12px;}
  .cat-brand-head .cat-toolbar-row{flex:1 1 100%;flex-wrap:nowrap;justify-content:stretch;gap:10px;margin-top:2px;}
  .cat-brand-head .cat-toolbar-row .cat-sort{flex:1 1 0;min-width:0;}
  .cat-brand-head .cat-toolbar-row .cat-sort .portal-select-button{width:100%;}
  /* Menus never spill off-screen: the LEFT chip (stock) opens flush-left, the
     RIGHT chip (sort) flush-right, both capped to the viewport width so a long
     option label ("Цена: сначала дешёвые") grows inward, not past the edge. */
  .cat-brand-head .cat-sort .portal-select-menu{min-width:100%;max-width:calc(100vw - 28px);}
  .cat-brand-head .cat-sort--plain .portal-select-menu{left:0;right:auto;}
  .cat-brand-head .cat-sort:not(.cat-sort--plain) .portal-select-menu{left:auto;right:0;}
}
/* Cart chip (only rendered when the cart holds items): glass control with an
   accent count pill, bottom-aligned with the toolbar fields. */
html .cat-cart-chip{display:inline-flex;align-items:center;gap:8px;min-height:40px;padding:0 15px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);font-size:13px;font-weight:700;color:var(--portal-text);text-decoration:none;background-color:var(--portal-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;transition:color var(--mo-fast) var(--mo-ease),border-color var(--mo-fast) var(--mo-ease);}
html .cat-cart-chip svg{width:17px;height:17px;flex:none;color:var(--portal-muted);transition:color var(--mo-fast) var(--mo-ease);}
html .cat-cart-chip:hover{color:var(--portal-accent-soft-ink);border-color:color-mix(in srgb,var(--portal-accent) 45%,var(--portal-border));text-decoration:none;}
html .cat-cart-chip:hover svg{color:var(--portal-accent-soft-ink);}
html .cat-cart-chip .cat-cart-chip-n{min-width:20px;height:20px;padding:0 6px;border-radius:999px;background:var(--portal-accent);color:var(--portal-accent-ink);font-size:11px;font-weight:800;display:inline-flex;align-items:center;justify-content:center;font-variant-numeric:tabular-nums;}
html .cat-cart-chip--side{width:100%;min-height:46px;border-radius:var(--portal-radius,16px);justify-content:flex-start;box-shadow:var(--portal-shadow);}
html .cat-cart-chip--side .cat-cart-chip-n{margin-left:auto;}

/* Search/filter emptied the grid: a quiet dashed invitation to retry. */
.cat-empty-note{margin:6px 0 0;padding:34px 20px;text-align:center;color:var(--portal-muted);font-size:14px;font-weight:600;border:1px dashed var(--portal-border);border-radius:var(--portal-radius,16px);}
/* The catalog back link is `.detail-back` like every other back link — see the
   block at «THE BACK CONTROL». `.cat-back` here is only portal.js's marker, so
   it deliberately carries NO chrome: the bordered chip and the 15px chevron
   that used to live here are what made the catalog's back look like a
   different control from the one on the order page. */
/* The search field is a glass control — same material as the balance chip. */
.cat-search{flex:1;min-width:200px;min-height:40px;display:flex;align-items:center;gap:9px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);background:var(--portal-surface-soft);padding:0 12px;transition:border-color var(--mo-fast,140ms) var(--mo-ease),box-shadow var(--mo-fast,140ms) var(--mo-ease),background-color var(--mo-fast,140ms) var(--mo-ease);}
.cat-search:focus-within{border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);background:var(--portal-surface);}
.cat-search svg{width:17px;height:17px;color:var(--portal-muted);flex:none;}
.cat-search input{flex:1;min-width:0;border:0;background:transparent;outline:none;font-size:14px;color:var(--portal-text);}
.cat-toolbar-controls{display:flex;align-items:center;gap:10px;}
.cat-cart-link{position:relative;display:inline-flex;align-items:center;justify-content:center;width:44px;height:44px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);background:var(--portal-surface);color:var(--portal-text);}
.cat-cart-link svg{width:20px;height:20px;}
.cat-cart-link:hover{background:var(--portal-hover);color:var(--portal-accent-soft-ink);border-color:color-mix(in srgb,var(--portal-accent) 40%,var(--portal-border));}
.cat-cart-badge{position:absolute;top:-5px;right:-5px;min-width:18px;height:18px;padding:0 5px;border-radius:999px;background:var(--portal-accent);color:var(--portal-accent-ink);font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;}
.cat-items{display:grid;grid-template-columns:repeat(auto-fill,minmax(225px,1fr));gap:16px;}
/* Pinned direct-top-up card above the Steam grid.

   IT IS THE DASHBOARD QUICK-ACTION CARD, STRETCHED. Same material, same
   title-leading composition, same corner watermark and corner arrow — only the
   width changes (owner, 2026-08-05: "оформлена как кнопка быстрого действия на
   главном экране, в похожем стиле: просто растянутая").

   It used to re-compose itself as a horizontal banner — icon chip left, copy
   centre, arrow right — on the argument that the dashboard composition breaks
   at full width. Two things were wrong with that, and both were visible.

   1. THE HOVER BUG. The banner reused `> span` — the element the dashboard uses
      for its faint corner watermark — as an opaque icon chip. It reset the
      resting opacity to 1 but never touched the inherited
      `:hover > span { opacity: .11 }`, which exists to BRIGHTEN a .06 watermark.
      So pointing at the card dropped the Steam logo to 11% and it all but
      vanished. Composing as the real card removes that by construction: the
      span is a watermark again, and .06 → .11 does what it was written to do.
   2. IT READ AS A FOREIGN COMPONENT — a green-washed banner with its own
      geometry sitting above a grid of neutral tiles, and on a phone the ≤860px
      dashboard rules and the banner's counter-rules fought each other into a
      cramped three-row block with the arrow suppressed.

   So the variant is now three declarations, not sixty: full width, its own
   bottom margin, and a watermark scaled to the wider surface. Everything else —
   background, padding, type, arrow, hover, the ≤860px behaviour — is inherited
   from `.quick-action-card`, which is the whole point. */
html .cat-pinned-action{
  width:100%;
  margin:0 0 16px;
  /* The card is ~2.5x the width of a dashboard grid cell, so the corner glyph
     grows with it; --qa-mark-reach (which reserves the copy's lane) is derived
     from these two, so the body text tracks it without a second number. */
  --qa-mark-w:128px;
  --qa-mark-bleed:26px;
}
html .cat-pinned-action > span:not(.lock-overlay){bottom:-30px;}
.cat-pinned-action strong{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.cat-pinned-badge{
  font-style:normal;font-size:var(--portal-text-xs);font-weight:var(--portal-weight-semibold);
  color:var(--portal-accent-soft-ink);border:var(--portal-rule-w) solid var(--portal-border);
  border-radius:999px;padding:2px 8px;background:var(--portal-surface);
}
/* Catalog tiles — one calm, unified storefront system. Every brand logo sits on
   the SAME neutral plate (no per-brand colour wash, no glass/liquid-glow), so the
   grid reads as a single system rather than a mix of materials; the colour comes
   from the logos, the chrome stays quiet. Hover = gentle lift + accent hairline. */
.cat-tile{display:flex;flex-direction:column;border:1px solid var(--portal-border);border-radius:14px;overflow:hidden;text-decoration:none;color:var(--portal-text);background:var(--portal-surface);box-shadow:var(--portal-shadow-sm);transition:border-color var(--mo-fast,140ms) var(--mo-ease),box-shadow var(--mo-base,210ms) var(--mo-ease),transform var(--mo-base,210ms) var(--mo-ease);}
/* The search filter sets [hidden]; our display:flex above would beat the UA
   [hidden]{display:none} rule (author origin wins), so restore it explicitly. */
.cat-tile[hidden]{display:none !important;}
/* Desktop mouse-hover warms the border (gated so a sticky touch-hover can't leave
   the accent stuck on a phone). On touch the accent comes only from .is-pressing —
   a genuine press that cancels on any finger movement (see the region note above). */
@media (hover:hover){.cat-tile:hover{border-color:color-mix(in srgb,var(--portal-accent) 42%,var(--portal-border));}}
.cat-tile.is-pressing{border-color:var(--portal-accent);}
/* Uniform logo plate: one neutral surface for every brand + a hairline divider. */
.cat-tile-img{position:relative;display:flex;align-items:center;justify-content:center;aspect-ratio:16/9;padding:18px;background:var(--portal-surface-soft);background-image:linear-gradient(180deg,rgba(255,255,255,.5),rgba(255,255,255,0) 62%);border-bottom:1px solid var(--portal-border-soft);overflow:hidden;}
/* CHILD combinator, not descendant. These rules size the brand LOGO, but the
   region flag badge is also a descendant of .cat-tile-img (.cat-tile-img >
   .cat-tile-flag > img). `.cat-tile-flag img` below has identical (0,1,1)
   specificity, so it only won for the properties it declares — it never declares
   max-width/max-height/object-fit, so those leaked in from here and resolved the
   flag to 17.3x13.3 instead of its declared 24x18. Minus the 1px border-box
   border that left ~15x11 CSS px of a 640x480 flag: on AU the white fimbriation
   came out at 0.47 device px and Epsilon Crucis at 1.4. That is the whole reason
   the flags looked badly drawn. */
.cat-tile-img > img{max-width:72%;max-height:74%;width:auto;height:auto;object-fit:contain;transition:transform var(--mo-base) var(--mo-ease);}
/* EA / Blizzard / Pokémon Unite marks carry generous internal padding, so they
   read small on the plate next to the others — give them a bit more room. */
.cat-tile-img > img[src*="/ea.png"],
.cat-tile-img > img[src*="/blizzard-mark"],
.cat-tile-img > img[src*="/pokemon-mark"]{max-width:90%;max-height:92%;}
/* Mobile-game brands wear their square App Store icon — render it as a rounded
   app badge (fuller than the wordmark logos) so it reads as a game tile. */
.cat-tile-img > img[src*="/game_"]{max-width:64%;max-height:64%;border-radius:22%;box-shadow:0 2px 9px rgba(15,30,25,.22);}
html.dark .cat-tile-img > img[src*="/game_"]{box-shadow:0 3px 11px rgba(0,0,0,.4);}
.cat-brand-mark img[src*="/game_"]{max-width:86%;max-height:86%;border-radius:24%;}
/* Same leak: hovering a tile was resampling the flag to a fractional size and
   animating it, so the badge shimmered and re-blurred on every hover. */
@media (hover:hover){.cat-tile:hover .cat-tile-img > img{transform:scale(1.06);}}
.cat-tile-img--mono{color:var(--portal-text);font-weight:750;font-size:22px;}
/* Wordmark fallback (brand with no logo): same neutral plate, brand name in ink. */
.cat-tile-img--brand{padding:14px 16px;background:var(--portal-surface-soft);background-image:linear-gradient(180deg,rgba(255,255,255,.5),rgba(255,255,255,0) 62%);}
.cat-tile-wordmark{color:var(--portal-text);font-family:var(--portal-heading-font);font-weight:750;font-size:17px;line-height:1.14;letter-spacing:-.01em;text-align:center;text-shadow:none;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;}
/* Logo-less brands: a coloured app-icon monogram (the name still shows in the
   body below, so the plate carries the mark instead of repeating the text). */
.cat-tile-mono{display:grid;place-items:center;width:66px;height:66px;border-radius:19px;background:var(--mono,var(--portal-accent));color:#fff;font-family:var(--portal-heading-font);font-weight:800;font-size:27px;letter-spacing:.01em;box-shadow:0 6px 16px rgba(20, 24, 22,.20);}
html.dark .cat-tile-mono{box-shadow:0 6px 16px rgba(0,0,0,.35);}
.cat-tile-body{display:flex;flex-direction:column;gap:8px;padding:12px 14px 13px;flex:1;min-width:0;}
/* Brand name — the tile's clear anchor, in the UI voice (not the loud 800 heading). */
.cat-tile-title{display:flex;align-items:center;gap:8px;min-width:0;font-family:var(--portal-font-ui);font-size:15px;font-weight:640;letter-spacing:-.008em;}
.cat-tile-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;}
/* Region tag — a quiet neutral chip (was a loud accent pill). */
.cat-tile-tag{flex:none;max-width:110px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:10px;font-weight:650;letter-spacing:.03em;text-transform:uppercase;color:var(--portal-muted);background:var(--portal-surface-soft);border:1px solid var(--portal-border-soft);border-radius:6px;padding:2px 6px;}
/* Type descriptor ("Гифт-карты", "Пополнение"…): quiet neutral tags in sentence
   case — professional and calm, not shouty accent-coloured uppercase pills. */
/* One fixed-height row: markers never wrap (they'd grow the tile), the last one
   ellipsizes if space runs short, and the "+N" counter stays pinned and whole.
   Compact sizing lets two Russian labels + the counter fit a 225px tile. */
.cat-tile-sub{display:flex;flex-wrap:nowrap;align-items:center;gap:4px;min-height:21px;min-width:0;overflow:hidden;}
.cat-tile-mark{flex:0 1 auto;font-size:10px;font-weight:560;letter-spacing:-.01em;text-transform:none;color:var(--portal-muted);background:var(--portal-surface-soft);border:1px solid var(--portal-border-soft);border-radius:6px;padding:2px 6px;white-space:nowrap;min-width:0;overflow:hidden;text-overflow:ellipsis;}
html.dark .cat-tile-mark{color:var(--portal-muted);background:var(--portal-surface-soft);}
/* "+N" counter for the types that didn't fit — soft accent pill, never shrinks. */
/* TAXONOMY PILLS — what a brand actually sells. Five of the eight product types
   take a hue, three stay neutral: eight colours across forty tiles is a toy, five
   is a legend. The pill keeps its size and shape; only ink and fill change. */
.cat-tile-mark[data-type="cards"]{color:var(--portal-hue-sapphire);background:var(--portal-hue-sapphire-soft);border-color:transparent;}
.cat-tile-mark[data-type="topup"]{color:var(--portal-hue-verdigris);background:var(--portal-hue-verdigris-soft);border-color:transparent;}
.cat-tile-mark[data-type="keys"]{color:var(--portal-hue-iris);background:var(--portal-hue-iris-soft);border-color:transparent;}
.cat-tile-mark[data-type="subs"]{color:var(--portal-progress);background:var(--portal-progress-soft);border-color:transparent;}
.cat-tile-mark[data-type="esim"]{color:var(--portal-reversed);background:var(--portal-reversed-soft);border-color:transparent;}
html.dark .cat-tile-mark[data-type="cards"]{color:var(--portal-hue-sapphire);background:var(--portal-hue-sapphire-soft);}
html.dark .cat-tile-mark[data-type="topup"]{color:var(--portal-hue-verdigris);background:var(--portal-hue-verdigris-soft);}
html.dark .cat-tile-mark[data-type="keys"]{color:var(--portal-hue-iris);background:var(--portal-hue-iris-soft);}
html.dark .cat-tile-mark[data-type="subs"]{color:var(--portal-progress);background:var(--portal-progress-soft);}
html.dark .cat-tile-mark[data-type="esim"]{color:var(--portal-reversed);background:var(--portal-reversed-soft);}
.cat-tile-mark--more{flex:none;color:var(--portal-accent-soft-ink);background:var(--portal-accent-soft);border-color:transparent;font-weight:680;padding:2px 5px;cursor:default;}
html.dark .cat-tile-mark--more{color:var(--portal-accent-soft-ink);background:var(--portal-accent-soft);}
/* eSIM plan filter: segmented chips above the products table (Data-only / +Voice). */
.cat-plan-filter{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 14px;}
/* Same pill as the inbox "Непрочитанные" toggle: glass pill, accent when on. */
.cat-plan-chip{position:relative;display:inline-flex;align-items:center;min-height:36px;padding:0 16px;border-radius:999px;border:1px solid var(--portal-border);background-color:var(--portal-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;color:var(--portal-muted);font-size:13px;font-weight:600;cursor:pointer;user-select:none;transition:border-color .14s var(--mo-ease),background-color .14s var(--mo-ease),color .14s var(--mo-ease);}
.cat-plan-chip input{position:absolute;opacity:0;width:0;height:0;margin:0;pointer-events:none;}
.cat-plan-chip:hover{border-color:color-mix(in srgb,var(--portal-accent) 45%,var(--portal-border));color:var(--portal-text);}
/* Active: keep the dark glass, light up with an accent ring + accent text (the
   "pressed" look from the reference) — not a solid fill. */
.cat-plan-chip.is-active,.cat-plan-chip:has(input:checked){border-color:var(--portal-accent);color:var(--portal-accent-soft-ink);box-shadow:var(--portal-focus-ring);}
.cat-tile-price{font-size:var(--portal-text-base);font-weight:750;color:var(--portal-text);font-variant-numeric:tabular-nums;margin-top:auto;padding-top:2px;}
/* Brand artwork ships in dark ink on transparency — keep the plate light in dark mode. */
html.dark .cat-tile-img{background:linear-gradient(165deg,#eceef5,#dfe3ee);}
html.dark .cat-tile-wordmark{color:#1a2130;}
html.dark .cat-product-img:not(.cat-product-img--mono){background:linear-gradient(165deg,#eceef5,#dfe3ee);}
/* Region badge pinned to the artwork plate: flag for ISO regions, globe for
   world-ish ones (GLOB/CIS/…).

   THE BADGE SITS ON AN INK CHIP, and this is the third attempt at it, so the
   reasoning is worth keeping. Two earlier versions tried to carry the badge on
   a hairline alone — first an inset ring (never rendered: an inset shadow on a
   replaced element paints under the artwork), then an outer one. Both were the
   same bet: that a 1px edge is enough to separate a flag from what is behind
   it. It is not, and the reason is the artwork, not the edge. Roughly half the
   catalog's regions fly a white-dominant flag — JP, FI, PL, QA, AR, UY, IL,
   CY — and the plate they land on is a near-white brand plate in BOTH themes.
   Their INTERIOR matches the surface, so a hairline turns the badge into an
   outlined ghost: Japan reads as a red dot floating on the plate, Finland as a
   blue cross with no flag under it. Verified at 1x and 3.4x, 2026-08-05.

   The chip fixes that by construction rather than by degree — the flag now sits
   on ink that is dark in both themes, so nothing it contains can match its
   surround. Its own hairline (--portal-flag-chip-ring, light) does the mirror
   job for the other extreme: DE's and EE's black bands would otherwise merge
   into the chip. White flag, black flag, either way there is an edge.

   It reads as a badge on purpose. The earlier "the badge IS the flag, no
   plate" direction is what produced the screenshots the owner rejected. */
.cat-tile-flag{
  position:absolute;top:10px;right:10px;
  display:grid;place-items:center;
  padding:2.5px;
  background:var(--portal-flag-chip);
  border-radius:6px;
  box-shadow:0 1px 2px rgba(0,0,0,.22);
}
/* 24px, not 28: at 28 the badge started competing with the brand logo for the
   plate. 24 is the size the markup always claimed — it just never rendered it
   (the selector leak drew it at 17). */
.cat-tile-flag img[src*="/flags/"]{width:24px;box-shadow:0 0 0 1px var(--portal-flag-chip-ring);}
/* The globe stand-in for world-ish regions sits INSIDE the same ink chip, on
   its own light mat — it is monochrome line art, so it needs a surface to be
   legible. Matching the flags beside it means matching their box: the same
   24x18 footprint and the same light hairline, so a globe tile and a flag tile
   carry badges of identical size and weight. */
.cat-tile-flag svg{display:block;width:14px;height:14px;color:var(--portal-muted);padding:2px 5px;box-sizing:content-box;background:var(--portal-flag-mat);border-radius:3px;box-shadow:0 0 0 1px var(--portal-flag-chip-ring);}
/* Ref number inside detail H1s: the figure face with forced lining tabular
   digits so numerals match the letters' height (mixed-face digits looked
   smaller than letters). Selectable for copying. */
/* white-space:nowrap keeps the reference a single token — the parent title
   carries overflow-wrap:anywhere (so a pathological title can't overflow), but
   that also let "#DX-QAGW-47MB" break at its own hyphens ("#DX-QAGW-" / "47MB").
   The ref now wraps whole onto the next line instead of splitting mid-number. */
/* The #DX-… ref in the brand face (Switzer heading font) — owner reverted the
   mono experiment 2026-07-16 and kept Switzer; tnum/lnum hold numerals at cap
   height. (The ref itself is rendered lowercase in client_ref_number.) */
.detail-title .detail-ref{font-family:var(--portal-heading-font);font-weight:760;font-variant-numeric:tabular-nums lining-nums;letter-spacing:.01em;white-space:nowrap;-webkit-user-select:text;user-select:text;}
/* ---- order delivery card: one block per delivery format ------------------- */
.order-delivery .delivery-body{display:grid;gap:18px;padding:22px 24px;}
.delivery-block{display:grid;gap:8px;min-width:0;}
.delivery-code{display:flex;align-items:center;gap:10px;min-width:0;}
.delivery-code code{
  flex:1 1 auto;min-width:0;overflow-wrap:anywhere;
  font-family:var(--portal-font-code);font-size:var(--portal-text-base);
  color:var(--portal-text);background:var(--portal-accent-soft);
  border:var(--portal-rule-w) solid var(--portal-border);
  border-radius:var(--portal-radius-xs);padding:9px 12px;
  -webkit-user-select:text;user-select:text;
}
.delivery-copy{flex:0 0 auto;}
.delivery-copy-all{display:flex;justify-content:flex-end;}
.delivery-link{overflow-wrap:anywhere;}
.delivery-esim-links{display:flex;flex-wrap:wrap;gap:10px;}
.delivery-file-row{display:flex;justify-content:flex-start;}
.delivery-download{text-decoration:none;}
.delivery-pending{margin:0;color:var(--portal-muted);font-size:var(--portal-text-base);}
@media (max-width: 640px){
  .order-delivery .delivery-body{padding:18px 20px;}
}
/* The whole title stays on ONE line on phones («убери перенос номера»): the
   word+ref pair shrinks with the viewport instead of the ref dropping to its
   own giant second line. 5.8vw fits the longest form ("Transaction #DX-XXXX-
   XXXX") at 320-430px; taller screens keep the clamp's 24px. */
@media (max-width: 760px){
  .detail-page .detail-title{
    white-space:nowrap;
    overflow-wrap:normal;
    font-size:min(24px, 5.8vw);
  }
}
/* Text is selectable/copyable by DEFAULT (detail values, refs, API docs — the
   owner copies these). Only interactive CHROME refuses selection, so a tap or
   swipe never highlights a tile/label/button; portal.js scopes its selectstart
   guard to the same zones. */
html body{-webkit-tap-highlight-color:transparent;}
.sidebar, .topbar, button, .nav-icon, .lang-row, .theme-switch,
.cat-tile, .cat-region, .cat-plan-chip, .cat-facet-head, .cat-sort, .cat-buybar,
.quick-action-card, .home-feed-row, .ns-pagination, .dropdown-menu
{-webkit-user-select:none;user-select:none;-webkit-touch-callout:none;}
html body input, html body textarea, html body select, html body [contenteditable="true"]{-webkit-user-select:text;user-select:text;-webkit-touch-callout:default;}
/* Sticky buy bar — appears once any row has a quantity. A compact opaque
   pill hugging the bottom-right corner: only as wide as its content, equal
   breathing room around the text and the button. */
html .cat-buybar{position:sticky;bottom:16px;right:16px;z-index:40;margin:18px 0 0 auto;width:max-content;max-width:100%;display:flex;align-items:center;gap:22px;padding:10px 10px 10px 20px;border:1px solid var(--portal-border);border-radius:var(--portal-radius,16px);background-color:var(--portal-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;box-shadow:0 18px 44px rgba(20, 24, 22,.22);}
html.dark .cat-buybar{box-shadow:0 18px 44px rgba(0,0,0,.5);}
.cat-buybar[hidden]{display:none!important;}
.cat-buybar-info{margin:0;display:grid;gap:2px;min-width:0;}
.cat-buybar-info small{font-size:var(--portal-text-xs);font-weight:750;letter-spacing:.07em;text-transform:uppercase;color:var(--portal-muted);font-variant-numeric:tabular-nums;}
.cat-buybar-total{display:flex;align-items:baseline;gap:7px;color:var(--portal-text);font-family:var(--portal-font-figure);font-size:17px;font-weight:750;font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1,"lnum" 1;white-space:nowrap;}
.cat-buybar-total-word{font-family:var(--portal-font-ui);font-size:var(--portal-text-sm);font-weight:650;color:var(--portal-muted);}
html .cat-buyall-btn, html.dark .cat-buyall-btn{border:0;border-radius:11px;padding:11px 22px;background:var(--portal-accent);color:var(--portal-accent-ink);font-family:var(--portal-heading-font);font-size:var(--portal-text-base);font-weight:700;box-shadow:0 8px 20px color-mix(in srgb, var(--portal-accent) 35%, transparent);cursor:pointer;white-space:nowrap;}
html .cat-buyall-btn:hover,html .cat-buyall-btn:focus-visible,html.dark .cat-buyall-btn:hover,html.dark .cat-buyall-btn:focus-visible{outline:none;filter:brightness(1.08);}
html .cat-buyall-btn:disabled,html.dark .cat-buyall-btn:disabled{opacity:.6;cursor:default;}
@media (max-width: 860px){.cat-buybar{bottom:calc(78px + env(safe-area-inset-bottom));}}

/* Account + package picker (Telegram Stars-style categories). */
.cat-pkg-head{display:flex;align-items:center;gap:16px;margin:0 0 18px;}
.cat-pkg-logo{width:56px;height:56px;flex:none;display:grid;place-items:center;border-radius:14px;background:#fff;box-shadow:0 0 0 1px rgba(20,33,28,.10);overflow:hidden;}
.cat-pkg-logo img{width:74%;height:74%;object-fit:contain;}
.cat-pkg-head h2{font-size:clamp(26px,2vw,32px);font-weight:760;letter-spacing:-.01em;margin:0;line-height:1.05;}
.cat-pkg-card{border:1px solid var(--portal-border);border-radius:var(--portal-radius);background-color:var(--portal-card-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;box-shadow:var(--portal-shadow);padding:20px 22px;margin-bottom:16px;}
.cat-pkg-eyebrow{display:block;margin:0 0 12px;color:var(--portal-muted);font-size:11px;font-weight:850;letter-spacing:.1em;text-transform:uppercase;}
.cat-pkg-input{width:100%;min-height:48px;padding:0 16px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm);background:var(--portal-surface-soft);color:var(--portal-text);font-size:16px;outline:none;transition:border-color var(--mo-fast,140ms) var(--mo-ease),box-shadow var(--mo-fast,140ms) var(--mo-ease),background-color var(--mo-fast,140ms) var(--mo-ease);}
.cat-pkg-input:focus{border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);background:var(--portal-surface);}
.cat-pkg-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px;}
html .cat-pkg,html.dark .cat-pkg{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:56px;padding:0 16px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm);background:var(--portal-surface-soft);color:var(--portal-text);cursor:pointer;text-align:left;transition:border-color var(--mo-fast,140ms) var(--mo-ease),background-color var(--mo-fast,140ms) var(--mo-ease);}
html .cat-pkg:hover,html.dark .cat-pkg:hover{border-color:color-mix(in srgb,var(--portal-accent) 45%,var(--portal-border));background:var(--portal-surface);}
html .cat-pkg.is-active,html.dark .cat-pkg.is-active{border-color:var(--portal-accent);background:color-mix(in srgb,var(--portal-accent) 12%,var(--portal-surface));box-shadow:var(--portal-focus-ring);}
html .cat-pkg:disabled,html.dark .cat-pkg:disabled{opacity:.5;cursor:not-allowed;}
.cat-pkg-name{font-size:14px;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.cat-pkg .cat-pkg-price{font-size:14px;font-weight:700;white-space:nowrap;color:var(--portal-text);}
.cat-pkg-buybar{position:sticky;bottom:14px;z-index:40;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:12px 12px 12px 20px;border:1px solid var(--portal-border);border-radius:16px;background:var(--portal-surface);box-shadow:0 14px 34px rgba(20, 24, 22,.18);margin-top:8px;}
.cat-pkg-summary{font-size:14px;color:var(--portal-muted);font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
html .cat-pkg-buy,html.dark .cat-pkg-buy{flex:none;border:0;border-radius:11px;padding:11px 24px;background:var(--portal-accent);color:var(--portal-accent-ink);font-family:var(--portal-heading-font);font-size:var(--portal-text-base);font-weight:700;cursor:pointer;box-shadow:0 8px 20px color-mix(in srgb,var(--portal-accent) 35%,transparent);}
html .cat-pkg-buy:hover:not(:disabled),html.dark .cat-pkg-buy:hover:not(:disabled){filter:brightness(1.08);}
html .cat-pkg-buy:disabled,html.dark .cat-pkg-buy:disabled{opacity:.5;cursor:not-allowed;box-shadow:none;}
@media (max-width:860px){.cat-pkg-buybar{bottom:calc(78px + env(safe-area-inset-bottom));}}
@media (max-width:600px){.cat-pkg-grid{grid-template-columns:1fr;}}

/* Stacked mobile tables: the base sheet paints each row-card #fff with no
   dark variant, leaving white cards on the dark chrome. Repaint them. */
@media (max-width: 760px){
  html.dark .ns-table-wrap tr, html.dark .table-wrap tr{background:var(--portal-surface);border-color:var(--portal-border);}
}
@media (prefers-reduced-motion: reduce){
  html .cat-tile, html .cat-search, html .cat-buy-btn, html .steam-card{animation:none !important;}
}
/* Steam top-up (NS Cabinet layout): one form card + a local-equivalent side
   card, both on the standard card material; labels use the uppercase filter
   vocabulary from Orders/Transactions. */
.steam-grid{display:grid;grid-template-columns:minmax(0,1fr) 300px;gap:16px;align-items:start;}
.steam-card{display:grid;gap:0;padding:24px 26px;border:1px solid var(--portal-border);border-radius:var(--portal-radius);box-shadow:var(--portal-shadow);background-color:var(--portal-card-glass);background-image:var(--portal-sheen);background-repeat:no-repeat;}
.steam-form{display:grid;gap:18px;}
.steam-field{display:grid;gap:8px;min-width:0;}
/* Labels + inputs follow the Orders/Transactions filter spec exactly. */
.steam-field > span{color:var(--portal-muted);font-size:11px;font-weight:850;letter-spacing:.08em;text-transform:uppercase;}
.steam-input{min-height:40px;padding:0 12px;background:var(--portal-surface-soft);border:1px solid var(--portal-border);border-radius:8px;color:var(--portal-text);font-family:var(--portal-font-ui,inherit);font-size:14px;font-weight:400;}
.steam-input:focus{outline:none;border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);}
.steam-amount-row{display:grid;grid-template-columns:150px minmax(0,1fr);gap:12px;align-items:start;}
.steam-amount-row .portal-select-button{width:100%;}
/* In dark mode the surface-on-surface menu needs a stronger lift to read as a
   layer above the card content. */
html.dark .portal-select-menu,
html.dark .dropdown-menu{box-shadow:0 18px 44px rgba(0,0,0,.55),var(--portal-shadow-lg);}
/* Backdrop-filtered cards form stacking contexts, so a LATER sibling card
   paints over a dropdown opened inside an EARLIER one (the menu's z-index is
   trapped inside its card). Lift whichever card hosts the open dropdown. */
.steam-card, .ns-filter-grid{position:relative;}
.steam-card:has([data-dropdown].open),
.ns-filter-grid:has([data-dropdown].open){z-index:40;}
.steam-limits{margin:2px 0 0;color:var(--portal-muted);font-size:var(--portal-text-sm);font-weight:500;}
.steam-form-foot{display:flex;align-items:center;justify-content:space-between;gap:14px;flex-wrap:wrap;border-top:1px solid var(--portal-border-soft);padding-top:18px;}
.steam-form-foot > span{color:var(--portal-muted);font-size:13px;}
.steam-rates{padding:0;overflow:hidden;}
.steam-rates-title{margin:0;padding:15px 18px;border-bottom:1px solid var(--portal-border-soft);color:var(--portal-muted);font-size:11px;font-weight:850;letter-spacing:.08em;text-transform:uppercase;}
.steam-rate-row{display:flex;align-items:center;justify-content:space-between;padding:13px 18px;border-bottom:1px solid var(--portal-border-soft);font-size:14px;color:var(--portal-text);}
.steam-rate-row:last-child{border-bottom:0;}
/* Values match the Transactions table cells exactly: the plain UI font
   (Switzer) at 13px/400 with tabular digits — not the display money style. */
.steam-rate-value{font-size:13px;font-weight:400;color:var(--portal-text);font-variant-numeric:tabular-nums;}
.steam-rate-value.metric-empty{color:var(--portal-muted);}
@media (max-width: 860px){.steam-grid{grid-template-columns:1fr;}}
/* Balance / deposit blocks: uppercase labels instead of the numbered rail. */

.cat-empty{font-size:14px;color:var(--portal-muted);padding:24px;text-align:center;}

.cat-product{display:block;}
.cat-product-card{padding:24px;}
.cat-product-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);gap:24px;}
.cat-product-img{height:190px;border-radius:14px;overflow:hidden;background:var(--portal-surface-soft);}
.cat-product-img img{width:100%;height:100%;object-fit:cover;}
.cat-product-img--mono{background:radial-gradient(120% 120% at 18% 12%,rgba(255,255,255,.22),rgba(255,255,255,0) 55%),linear-gradient(150deg,var(--portal-accent),var(--portal-accent-dark));display:flex;align-items:center;justify-content:center;padding:20px;}
.cat-product-img--mono .cat-tile-wordmark{font-size:26px;-webkit-line-clamp:3;}
.cat-product-title{font-size:22px;font-weight:800;margin:0 0 16px;}
.cat-product-eyebrow{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--portal-muted);margin:14px 0 8px;}
.cat-step{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border-radius:50%;background:var(--portal-accent-soft);color:var(--portal-accent-soft-ink);font-weight:800;font-size:11px;}
.cat-product-desc{font-size:13px;color:var(--portal-muted);line-height:1.55;margin:0;}
.cat-product-hint{font-size:var(--portal-text-sm);color:var(--portal-muted);margin:6px 0 0;}

.cat-select{position:relative;}
.cat-select-btn{width:100%;display:flex;align-items:center;gap:11px;padding:13px 15px;border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);background:var(--portal-surface-soft);cursor:pointer;text-align:left;transition:border-color var(--mo-fast,140ms) var(--mo-ease),background var(--mo-fast,140ms) var(--mo-ease);}
.cat-select-btn:hover{background:var(--portal-hover);border-color:color-mix(in srgb,var(--portal-accent) 45%,var(--portal-border));}
.cat-select.open .cat-select-btn{border-color:var(--portal-accent);box-shadow:var(--portal-focus-ring);background:var(--portal-surface);}
.cat-select.is-disabled{opacity:.6;}
.cat-select.is-disabled .cat-select-btn{cursor:not-allowed;}
/* 26x19 was not 4:3, so object-fit:cover was silently cropping the top and
   bottom off every flag in the region dropdown. The wrapper is now the same 4:3
   box as the artwork, and the ring comes from the primitive. */
.cat-select-flag,.cat-opt-flag{width:24px;height:18px;flex:none;display:inline-flex;align-items:center;justify-content:center;}
.cat-flag--globe{width:22px;height:22px;color:var(--portal-muted);}
/* The globe stand-in for world-ish regions (GLOB/CIS/…) is SQUARE — never stretch
   it into the 4:3 flag slot. It just has to fit the slot's height. */
.cat-select-flag .cat-flag--globe,.cat-opt-flag .cat-flag--globe{width:18px;height:18px;}
.cat-select-text{font-weight:700;font-size:14px;flex:1;min-width:0;color:var(--portal-text);}
.cat-select-rate{font-weight:800;font-size:13px;color:var(--portal-accent-soft-ink);font-variant-numeric:tabular-nums;}
.cat-select .cat-chevron{width:18px;height:18px;color:var(--portal-muted);flex:none;transition:transform var(--mo-base,210ms) var(--mo-ease);}
.cat-select.open .cat-chevron{transform:rotate(180deg);color:var(--portal-accent-soft-ink);}
.cat-menu{position:absolute;top:calc(100% + 8px);left:0;right:0;z-index:40;background:var(--portal-surface);border:1px solid var(--portal-border);border-radius:var(--portal-radius-sm,11px);box-shadow:var(--portal-shadow-lg);padding:6px;max-height:320px;overflow-y:auto;opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-8px) scale(.99);transform-origin:top center;transition:opacity var(--mo-base,210ms) var(--mo-ease),transform var(--mo-slow,340ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1)),visibility var(--mo-base,210ms) step-end;}
.cat-select.open .cat-menu{opacity:1;visibility:visible;pointer-events:auto;transform:none;transition:opacity var(--mo-base,210ms) var(--mo-ease),transform var(--mo-slow,340ms) var(--mo-spring,cubic-bezier(.34,1.4,.64,1)),visibility 0s;}
.cat-opt{width:100%;display:flex;align-items:center;gap:11px;padding:10px 11px;border:0;border-radius:9px;background:transparent;cursor:pointer;text-align:left;transition:background var(--mo-fast,140ms) var(--mo-ease);}
.cat-opt+.cat-opt{margin-top:2px;}
.cat-opt:hover{background:var(--portal-hover);}
.cat-opt.is-active{background:var(--portal-accent-soft);}
.cat-opt-text{flex:1;min-width:0;}
.cat-opt-name{font-weight:700;font-size:14px;color:var(--portal-text);}
.cat-opt-rate{font-weight:800;font-size:13px;color:var(--portal-accent-soft-ink);font-variant-numeric:tabular-nums;}

.cat-price{margin:18px 0 4px;}
.cat-price b{font-family:var(--portal-font-number);font-variant-numeric:tabular-nums;font-weight:800;font-size:34px;letter-spacing:-.02em;color:var(--portal-financial-text,var(--portal-text));}
.cat-product-cta{display:flex;gap:10px;margin-top:16px;align-items:stretch;}
.cat-product-cta form:first-child{flex:1;}
.cat-product-cta .dep1-continue{width:100%;justify-content:center;}
.cat-product-cta .dep1-continue:disabled{opacity:.5;cursor:not-allowed;}
.cat-cart-btn{flex:0 0 auto;width:54px;padding:0 !important;}
.cat-cart-btn svg{width:20px;height:20px;}

.cat-cart{border:1px solid var(--portal-border);border-radius:14px;overflow:hidden;background:var(--portal-surface);margin-bottom:16px;}
.cat-cart-row{display:flex;align-items:center;gap:14px;padding:14px;border-bottom:1px solid var(--portal-border-soft);}
.cat-cart-row:last-child{border-bottom:0;}
.cat-cart-img{width:48px;height:48px;border-radius:11px;overflow:hidden;flex:none;background:var(--portal-surface-soft);}
.cat-cart-img img{width:100%;height:100%;object-fit:cover;}
.cat-cart-meta{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px;}
.cat-cart-meta strong{font-size:14px;font-weight:700;}
.cat-cart-meta small{font-size:12px;color:var(--portal-muted);}
.cat-cart-price{font-weight:800;font-size:15px;font-variant-numeric:tabular-nums;color:var(--portal-text);}
.cat-cart-remove-form{flex:none;}
.cat-cart-remove{display:grid;place-items:center;width:32px;height:32px;border-radius:9px;border:1px solid var(--portal-border-soft);background:var(--portal-surface);color:var(--portal-muted);line-height:0;cursor:pointer;transition:background var(--mo-fast,140ms) var(--mo-ease),color var(--mo-fast,140ms) var(--mo-ease),border-color var(--mo-fast,140ms) var(--mo-ease);}
.cat-cart-remove-icon{width:15px;height:15px;display:block;}
.cat-cart-remove:hover{background:color-mix(in srgb,var(--portal-danger) 12%,var(--portal-surface));color:var(--portal-danger);border-color:transparent;}
.cat-cart-summary{border:1px solid var(--portal-border);border-radius:14px;padding:18px;background:var(--portal-surface-soft);}
.cat-cart-line{display:flex;align-items:center;justify-content:space-between;font-size:14px;margin-bottom:10px;}
.cat-cart-line span{color:var(--portal-muted);}
.cat-cart-line strong{font-weight:800;font-size:18px;font-variant-numeric:tabular-nums;}
.cat-cart-summary .dep1-continue{width:100%;justify-content:center;margin-top:6px;}
.cat-cart-back{display:inline-block;margin-top:14px;font-size:13px;color:var(--portal-muted);text-decoration:none;}
.cat-cart-back:hover{color:var(--portal-accent-soft-ink);}

@media (max-width: 760px){
  .cat-product-grid{grid-template-columns:1fr;}
  .cat-items{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:11px;}
}
@media (max-width: 560px){
  .cat-toolbar{gap:9px;}
  .cat-items{grid-template-columns:repeat(2,1fr);}
  .cat-product-card{padding:18px;}
  .cat-product-cta{flex-direction:column;}
  .cat-cart-btn{width:100%;}
}

/* ===========================================================================
   §27 — API documentation (developer page) code blocks
   The page reuses the legacy .ns-api-docs-* / .ns-doc-card system (already
   token-based + theme-aware). Only the copy-enabled code snippet, the doc list,
   and the endpoint header row are new here. Tokens only; the copy control uses
   html-prefixed pairs so the legacy solid-button fill can't drown it in either
   theme (design-system "known dark-theme gotcha").
   =========================================================================== */
.ns-doc-snippet{
  background:var(--portal-surface-soft);
  border:1px solid var(--portal-border);
  border-radius:var(--portal-radius-sm);
  overflow:hidden;
}
.ns-doc-snippet-head{
  align-items:center;
  background:var(--portal-surface);
  border-bottom:1px solid var(--portal-border-soft);
  display:flex;
  gap:var(--portal-space-2);
  justify-content:space-between;
  padding:var(--portal-space-2) var(--portal-space-3);
}
.ns-doc-snippet-head>span{
  color:var(--portal-muted);
  font-family:var(--portal-font-mono);
  font-size:var(--portal-text-xs);
  font-weight:var(--portal-weight-semibold);
  letter-spacing:.04em;
  text-transform:uppercase;
}
.ns-doc-snippet pre{
  background:var(--portal-surface-soft);
  border:0;
  border-radius:0;
  color:var(--portal-text);
  margin:0;
  overflow-x:auto;
  padding:var(--portal-space-3);
}
.ns-doc-snippet code{
  font-family:var(--portal-font-mono);
  font-size:var(--portal-text-sm);
  line-height:1.6;
  white-space:pre;
}
/* Quiet (outline) copy button — html-prefixed pair beats the legacy solid
   `button` fill in both themes; tokens flip per theme. The same pair also undoes
   legacy portal.css `button{width:100%}` (≤760px), which otherwise stretches the
   copy control across the snippet header on a phone. */
html:not(.dark) .ns-doc-copy,
html.dark .ns-doc-copy{
  background:transparent;
  border:1px solid var(--portal-border);
  border-radius:var(--portal-radius-xs);
  box-shadow:none;
  color:var(--portal-muted);
  cursor:pointer;
  flex:0 0 auto;
  font-family:var(--portal-font-ui);
  font-size:var(--portal-text-xs);
  font-weight:var(--portal-weight-semibold);
  min-height:0;
  padding:5px 11px;
  width:auto;
  transition:color var(--portal-mo-fast) var(--portal-ease),
             border-color var(--portal-mo-fast) var(--portal-ease),
             background var(--portal-mo-fast) var(--portal-ease);
}
html:not(.dark) .ns-doc-copy:hover,
html.dark .ns-doc-copy:hover{
  background:transparent;
  border-color:var(--portal-accent);
  color:var(--portal-accent-soft-ink);
}
.ns-doc-copy:active{transform:var(--portal-mo-press);}
.ns-doc-copy:focus-visible{outline:none;box-shadow:var(--portal-focus-ring);}
/* Doc lists and endpoint header rows */
.ns-doc-list{
  color:var(--portal-muted);
  display:grid;
  gap:var(--portal-space-2);
  line-height:1.5;
  margin:0;
  padding-left:20px;
}
.ns-doc-list code{
  color:var(--portal-text);
  font-family:var(--portal-font-mono);
  font-size:var(--portal-text-sm);
}
.ns-doc-endpoint{
  align-items:center;
  display:flex;
  flex-wrap:wrap;
  gap:var(--portal-space-2);
}
.ns-doc-endpoint code{
  color:var(--portal-text);
  font-family:var(--portal-font-mono);
  font-size:var(--portal-text-md);
  font-weight:var(--portal-weight-semibold);
  overflow-wrap:anywhere;
}
.ns-doc-endpoint-block{
  border-top:1px solid var(--portal-border-soft);
  display:grid;
  gap:var(--portal-space-3);
  padding-top:var(--portal-space-4);
}
.ns-doc-endpoint-block:first-of-type{border-top:0;padding-top:0;}
.ns-doc-table-wrap{overflow-x:auto;}
/* Section sub-headings inside a doc card (e.g. "Call order", "Rate limits"). */
.ns-doc-card h3{
  color:var(--portal-text);
  font-family:var(--portal-heading-font);
  font-size:var(--portal-text-lg);
  font-weight:var(--portal-weight-bold);
  margin:0;
}
/* Doc card = the portal's card, on the portal's radius (the legacy sheet's own
   14px + drop shadow read as a foreign component next to .metric-card). */
html .ns-doc-card{
  border-radius:var(--portal-radius);
  box-shadow:var(--portal-shadow-sm);
  padding:var(--portal-space-5);
}
/* Reference tables read like the portal's .ns-data-table: the whole table is a
   bordered panel, the header row sits on surface-soft. (The legacy .ns-doc-table
   was a bare hairline grid — off-system.) */
.ns-doc-table-wrap{
  border:1px solid var(--portal-border);
  border-radius:var(--portal-radius-sm);
}
html .ns-doc-table th{
  background:var(--portal-surface-soft);
  border-bottom:1px solid var(--portal-border);
}
html .ns-doc-table tr:last-child td{border-bottom:0;}
/* Method label — NOT a chip. The filled green/blue pill is the default Swagger
   look (owner, 2026-07-15: «очень иишные»); do NOT bring it back. Here the verb
   is a quiet mono label of fixed width, so the paths align in a column and the
   emphasis sits on the path itself. A hairline rule separates the two. */
html .api-method,
html.dark .api-method,
html .api-method.get,
html.dark .api-method.get,
html .api-method.post,
html.dark .api-method.post{
  background:none;
  border:0;
  border-radius:0;
  box-shadow:none;
  color:var(--portal-muted);
  display:inline-block;
  font-family:var(--portal-font-mono);
  font-size:var(--portal-text-xs);
  font-weight:var(--portal-weight-semibold);
  letter-spacing:.1em;
  min-height:0;
  min-width:38px;
  padding:0;
  text-align:left;
}
/* The rule + the path: one continuous line, the path carries the weight. */
html .ns-doc-endpoint code{
  border-left:1px solid var(--portal-border);
  padding-left:var(--portal-space-3);
}
@media (prefers-reduced-motion: reduce){
  .ns-doc-copy{transition:none;}
}
@media (max-width: 760px){
  /* Legacy portal.css collapses EVERY table into stacked white cards at this
     width (thead hidden, tr{background:#fff}) — that loses the header context
     the API-docs tables need and paints hardcoded white rows under the dark
     theme. The docs tables stay real tables and scroll inside .ns-doc-table-wrap. */
  .ns-doc-table{display:table;min-width:480px;}
  .ns-doc-table thead{display:table-header-group;}
  .ns-doc-table tbody{display:table-row-group;padding:0;gap:0;}
  .ns-doc-table tr{display:table-row;background:transparent;border:0;border-radius:0;box-shadow:none;overflow:visible;}
  .ns-doc-table th,.ns-doc-table td{display:table-cell;}
  .ns-doc-card h3{font-size:var(--portal-text-md);}
}

@media (hover: none) {
    html .home-financials .home-feed-link:hover {
        background: transparent;
    }
}

/* ── Data tables keep the DESKTOP layout on phones and scroll sideways ─────────
   Legacy portal.css collapses EVERY <table> into stacked label/value cards at
   ≤760px and again at ≤640px (thead hidden, each <tr> becomes a bordered card,
   `td::before` prints the data-label). The owner wants these read exactly like
   the PC — the real horizontal table, swipe to see the rest — for Transactions,
   Orders, the "recent services" list and the catalog denomination leaf. So we
   re-assert the native table box on every .ns-data-table and let it overflow-
   scroll inside its .ns-table-wrap (which already carries overflow-x:auto). The
   API-docs tables above already do this for themselves; this is the same idiom,
   generalised. data-label attributes stay in the markup — they simply go inert
   once the ::before labels are switched off. Class-scoped + last in the cascade
   so it outranks the element-selector card blocks and the dark-row repaint. */
@media (max-width: 760px){
  /* overscroll-behavior-x:none kills the iOS elastic over-scroll (rubber-band):
     dragging the table past its edge no longer bounces open a strip of empty
     card background, and the sideways gesture never chains to the page. */
  .ns-table-wrap{ -webkit-overflow-scrolling:touch; overscroll-behavior-x:none; }
  /* Columns sized to their CONTENT, not a fixed 860px stretched to fill.
     A fixed width made table-layout:auto squeeze the money columns until
     "0.1293"/"Завершён" wrapped one glyph per line, and left big empty gaps
     in the wide columns. width:auto + min-width:100% + nowrap cells → every
     column is exactly as wide as its longest line, the table overflows and
     scrolls sideways, and a short table still fills the card edge-to-edge. */
  .ns-table-wrap .ns-data-table{ display:table; width:auto; min-width:100%; }
  .ns-table-wrap .ns-data-table thead{ display:table-header-group; }
  .ns-table-wrap .ns-data-table tbody{ display:table-row-group; padding:0; gap:0; }
  /* EVERY row — header AND body — must be a table-row. If only tbody rows are
     restored, the thead sizes its columns on its own and the two desync: the
     header labels drift off their columns and the body's first column balloons
     to absorb the slack. */
  .ns-table-wrap .ns-data-table tr{ display:table-row; }
  /* Reset the LEGACY stacked-card row styling (portal.css: every `tr` gets
     background:surface + border + border-radius:10px + overflow:hidden for the
     phone card layout) back to a flat table row. This MUST cover the HEADER row
     too, not just tbody: a filled, rounded, square-cornered header block pokes
     past the wrap's rounded top corner on iOS Safari and reads as a "double
     edge" seam. Flat + transparent → only the th hairline draws, no block. */
  html .ns-table-wrap .ns-data-table tbody tr,
  html.dark .ns-table-wrap .ns-data-table tbody tr,
  html .ns-table-wrap .ns-data-table thead tr,
  html.dark .ns-table-wrap .ns-data-table thead tr{
    background:transparent; border:0; border-radius:0;
    box-shadow:none; overflow:visible;
  }
  /* nowrap: nothing wraps (dates, amounts, статус stay one line); middle:
     single-line rows sit centred instead of pinned to the top with a tall
     void beneath a taller neighbour cell. */
  .ns-table-wrap .ns-data-table th,
  .ns-table-wrap .ns-data-table td{
    display:table-cell; vertical-align:middle; white-space:nowrap;
  }
  /* Inner nodes (the id-column <code>, price/name spans …) carry their OWN
     white-space:normal — without this they wrap one glyph per line inside a
     tight column and blow the row height up to ~250px. */
  .ns-table-wrap .ns-data-table th *,
  .ns-table-wrap .ns-data-table td *{ white-space:nowrap; }
  .ns-table-wrap .ns-data-table td::before,
  .ns-table-wrap .ns-data-table th::before{ content:none; display:none; }
}

/* NOTE: the Finances/Orders operation lists stay the generic sideways-scrolling
   tables on a phone (the block above) — the owner asked to keep them "как
   раньше", so the stacked-card experiment was reverted. Do not re-add it.
   The balance-bar phone rules live with the rest of the finances header CSS. */

/* ── Filter dropdowns (status select + date calendar) ──────────────────────────
   OPAQUE floating cards — the owner explicitly rejected liquid glass here
   ("никакого жидкого стекла": any see-through over the dense filter form reads
   as broken layering). Solid surface + soft sheen + strong shadow, lifted above
   the fixed bottom dock; JS flips the calendar above the field when there's no
   room below. Do not reintroduce translucency/backdrop-blur on these. */
.portal-select-menu,
.portal-date-menu{
  background-color:var(--portal-surface);
  background-image:var(--portal-sheen);
  background-repeat:no-repeat;
  border:1px solid var(--portal-border);
  box-shadow:var(--portal-shadow-lg);
  z-index:210;   /* clear the fixed bottom dock */
}
/* Day cells / month-nav are <button>s, so the legacy base sheet's GLOBAL button
   rules land on them: `button{min-height:44px;padding:0 14px}` plus, under
   760px, `button{width:100%}`. The 14px side padding pushed every cell's
   min-content width to ~46px → 7 columns (+ gaps) ≈ 348px, wider than the
   popup's inner box (min(92vw,320px) − padding), so the grid burst out to the
   right and "Вс" got clipped while the padding-less .cal-dow row stayed narrow
   ("слева пусто, справа обрезано"). Reset the inherited button metrics here and
   pin both grids to minmax(0,1fr) tracks so the columns can never be widened by
   their content again. */
.portal-date-menu .cal-day,
.portal-date-menu .cal-nav{
  min-height:0;
  min-width:0;
  padding:0;
  width:auto;
}
.portal-date-menu .cal-dow,
.portal-date-menu .cal-grid{
  grid-template-columns:repeat(7, minmax(0, 1fr));
}
/* Month nav: quiet ghost chevrons (inline SVG, stroke:currentColor) — the old
   bordered squares with «‹ ›» text entities read as stray boxes on the card. */
.portal-date-menu .cal-nav{
  width:32px;
  height:32px;
  flex:none;
  border:0;
  border-radius:var(--portal-radius-xs);
  background:transparent;
  color:var(--portal-muted);
}
.portal-date-menu .cal-nav:hover{
  background:var(--portal-hover);
  color:var(--portal-text);
}
.portal-date-menu .cal-nav:active{ background:var(--portal-press); }
.portal-date-menu .cal-nav-icon{
  width:18px;
  height:18px;
  pointer-events:none;   /* clicks always resolve to the button, never the path */
}
/* Day numbers in the site's figure face — tabular lining numerals read clean
   and premium instead of the heavy inherited body digits. */
.portal-date-menu .cal-day{
  font-family:var(--portal-font-figure);
  font-variant-numeric:tabular-nums lining-nums;
  font-weight:540;
}
/* Today = accent-coloured numeral. The previous 1px inset ring rendered as a
   stray rounded square floating around the date ("обводка выходит куда-то").
   Selected still gets the solid accent fill from the base sheet. */
.portal-date-menu .cal-day.today{ box-shadow:none; color:var(--portal-accent-soft-ink); font-weight:680; }
/* Desktop: flip above the field if it would overflow below (JS toggles .flip-up). */
.portal-date.flip-up .portal-date-menu{ top:auto; bottom:calc(100% + 8px); }

@media (max-width: 760px){
  /* On phones the calendar is the same compact anchored "окошко" as on desktop —
     a popover by the field, NOT a full-screen modal. It may float over the
     filter form; the card is fully OPAQUE (owner canon — no glass/blur), so the
     overlap reads as a floating card, not a glitch. Roomier tap targets +
     slightly larger type for touch.
     The card hangs off the field's RIGHT edge (right:0), so its width must fit
     the space to the LEFT of that edge — 92vw was wider than that on a 320px
     phone and pushed the popup's left edge (and its rounded corner) off-screen. */
  .portal-date .portal-date-menu{ width:min(320px, calc(100vw - 48px)); padding:14px; }
  .portal-date-menu .cal-day,
  .portal-date-menu .cal-empty{ height:40px; }
  .portal-date-menu .cal-day{ font-size:15px; }
  .portal-date-menu .cal-title{ font-size:16px; }
  .portal-date-menu .cal-dow span{ font-size:12px; }
  /* ≥40px tap target for the month chevrons on touch. */
  .portal-date-menu .cal-nav{ width:40px; height:40px; border-radius:var(--portal-radius-sm); }
  .portal-date-menu .cal-nav-icon{ width:20px; height:20px; }
}

/* ── Ledger numbers in the site's figure face (Inter, tabular) ─────────────────
   The finances/orders pages set dates and totals in the body face (Switzer)
   while money elsewhere on the site uses Inter — an inconsistent, "не в стиле
   сайта" look. Put the numeric/date columns in Inter tabular; the text columns
   (описание/товар/статус) stay Switzer. Columns are targeted by POSITION per
   page — the data-label attr is localized (ДАТА/DATE/…) so it can't be matched
   by value.
   Finances: DATE·DESCRIPTION·STATUS·AMOUNT → 1,4.
   Orders:   DATE·PRODUCT·QTY·TOTAL·STATUS·ORDER ID → 1,3,4,6. */
.finances-page .ns-data-table tbody td:nth-child(1),
.finances-page .ns-data-table tbody td:nth-child(4),
.orders-page .ns-data-table tbody td:nth-child(1),
.orders-page .ns-data-table tbody td:nth-child(3),
.orders-page .ns-data-table tbody td:nth-child(4){
  font-family:var(--portal-font-figure);
  font-variant-numeric:tabular-nums lining-nums;
}
/* #DX-… reference numbers in the brand face (Switzer heading font) — owner
   reverted the mono experiment 2026-07-16. Applies to the ORDER ID column and the
   restored finances TRANSACTION ID column; the detail-title ref carries the same
   face via .detail-ref. */
.orders-page .ns-data-table tbody td:nth-child(6),
.orders-page .ns-data-table tbody td:nth-child(6) code,
.finances-page .ns-data-table tbody td:nth-child(5),
.finances-page .ns-data-table tbody td:nth-child(5) code{
  font-family:var(--portal-heading-font);
  font-weight:600;
  font-variant-numeric:tabular-nums lining-nums;
}
.finances-page .fin-op-noref{ color:var(--portal-muted); }

/* ============================================================================
   Finances — the unified money page (operations list + top-up sub-view).
   All values from --portal-* tokens; both themes; reduced-motion respected;
   the calm, flat look that replaced the liquid-glow balance plaque.
   ============================================================================ */
/* Balance BAR — the way a modern account states its money (Wise/Mercury): the
   spendable figure leads, the live 30-day flow stats sit beside it behind
   hairlines, and the one top-up action closes the row. Flat on the page — no
   card, no chart — so the header keeps the same calm register as Orders.
   The figure is TEXT-coloured, not green: green here read as "success" and stole
   the meaning it earns in the ledger, where «+$50.00» is an actual credit. */
.finances-page .fin-head {
    display: grid;
    gap: var(--portal-space-4);
    margin: 0;
}
/* The balance reads like the top line of a STATEMENT: a quiet eyebrow, ONE clean
   figure, a full-width hairline, then the live 30-day flow as a compact meta line.
   A soft accent margin-rule on the left gives the block its identity WITHOUT a
   card — flat + hairlines, structure by line and space, no box. Capped to a tidy
   statement column so on a wide desktop the eyebrow/action and the rule don't
   stretch across the whole page. */
.finances-page .fin-balance {
    position: relative;
    max-width: 34rem;
    padding-left: var(--portal-space-4);
    border-left: 2px solid var(--portal-accent-line);
}
/* Eyebrow row: the figure is named once, quietly; the top-up sits opposite the
   eyebrow so it never competes with the number below it. */
.finances-page .fin-balance-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--portal-space-3);
    margin-bottom: var(--portal-space-2);
}
.finances-page .fin-balance-label {
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--portal-muted);
    white-space: nowrap;
}
/* The figure: a small muted lead currency mark + ONE amount span at a single
   weight and colour, tabular so digits stay grid-stable. Reads clean at $1.6548
   and $1,149.68 alike — no big-int / tiny-cents split. */
.finances-page .fin-balance-figure {
    display: flex;
    align-items: baseline;
    gap: .08em;
    color: var(--portal-financial-text);
    font-family: var(--portal-font-figure);
    font-variant-numeric: tabular-nums lining-nums;
    line-height: 1;
}
.finances-page .fin-figure-cur {
    align-self: flex-start;
    margin-top: .16em;
    font-size: clamp(20px, 6vw, 26px);
    font-weight: var(--portal-weight-medium);
    color: var(--portal-muted);
}
.finances-page .fin-figure-amt {
    font-size: clamp(38px, 12vw, 52px);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: -.02em;
}
/* The one line under the figure — the "statement rule" that groups the block. */
.finances-page .fin-balance-rule {
    height: 1px;
    background: var(--portal-border);
    margin: var(--portal-space-4) 0 var(--portal-space-3);
}
/* 30-day flow as a compact inline meta line: value + quiet word, middot-separated.
   Money in earns the ledger green ONLY when something actually came in; money out
   stays the normal text colour (canon: списания обычным цветом); a pending top-up
   reads muted — it is not money yet. */
.finances-page .fin-flow {
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px var(--portal-space-2);
    font-size: var(--portal-text-base);
    line-height: 1.5;
    color: var(--portal-muted);
}
.finances-page .fin-flow-val {
    font-family: var(--portal-font-figure);
    font-variant-numeric: tabular-nums lining-nums;
    font-weight: var(--portal-weight-semibold);
    color: var(--portal-text);
}
.finances-page .fin-flow-val.is-in { color: var(--portal-good); }
.finances-page .fin-flow-val.is-pending { color: var(--portal-muted); }
.finances-page .fin-flow-val.is-review { color: var(--portal-danger); }
.finances-page .fin-flow-k { color: var(--portal-muted); }
.finances-page .fin-flow-dot { color: var(--portal-border-strong); padding: 0 2px; }
/* The deposit sub-page keeps a QUIET matched header (label + figure one step
   down, same face); set on its own so the page-hero rules don't reach it. */
.finances-deposit .fin-balance-label,
.finances-deposit .fin-balance-figure {
    font-family: var(--portal-font-figure);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: -.01em;
    color: var(--portal-financial-text);
}
.finances-deposit .fin-balance-figure { font-variant-numeric: tabular-nums lining-nums; }
/* A caption under the headline, flush with it — no inset, since there is no card
   edge left to inset from. */
.finances-page .fin-balance-note {
    display: flex;
    align-items: flex-start;
    gap: var(--portal-space-2);
    margin: 0;
    padding: 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.45;
}
/* The text shrinks and wraps inside the row; without this it keeps its
   min-content width and drops below the glyph, leaving the icon stranded on a
   line of its own once the column gets narrow. */
.finances-page .fin-balance-note > span {
    flex: 1 1 0;
    min-width: 0;
}
.finances-page .fin-balance-note-extra::before {
    content: "·";
    margin-right: var(--portal-space-2);
}
.finances-page .fin-banner-icon {
    flex: none;
    width: 16px;
    height: 16px;
    color: var(--portal-muted);
}
/* Operations filters: the four-field bordered grid (type / transaction id / date
   range) laid out in one row exactly like the orders/transactions filter — the
   day + TXID filters are back. */
.finances-page .fin-filter {
    margin: 0;
    grid-template-columns: 160px minmax(0, 1fr) 170px 170px;
}
@media (max-width: 720px) {
    .finances-page .fin-filter { grid-template-columns: 1fr 1fr; }
    .finances-page .fin-filter > label:first-of-type { grid-column: 1 / -1; }
    .finances-page .fin-filter label:nth-child(3) { grid-column: 1 / -1; }
}
/* Top-up = a QUIET accent chip, not the loud glass pill it used to be: a soft
   accent-tinted fill, a hairline accent-line rim, accent text. It reads as the
   one money action without shouting — "indigo as a single small control" — and
   reuses the .cat-facet-more vocabulary so the cabinet has ONE soft-accent chip,
   not two. It is an <a>, so the legacy `html.dark button` accent flood never
   reaches it; the html-prefixed pair pins the soft fill defensively in both
   themes anyway. */
html.dark .fin-topup-btn,
html:not(.dark) .fin-topup-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--portal-space-2);
    min-height: 38px;
    padding: 0 var(--portal-space-4);
    border-radius: var(--portal-radius-sm);
    border: 1px solid var(--portal-accent-line);
    color: var(--portal-accent-soft-ink);
    background: var(--portal-accent-soft);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--portal-mo-fast) var(--portal-ease),
                border-color var(--portal-mo-fast) var(--portal-ease),
                transform var(--portal-mo-fast) var(--portal-ease);
}
.fin-topup-icon { width: 15px; height: 15px; flex: none; }
html .fin-topup-btn:hover,
html.dark .fin-topup-btn:hover {
    background: color-mix(in srgb, var(--portal-accent) 16%, transparent);
    border-color: var(--portal-accent);
}
html .fin-topup-btn:focus-visible {
    outline: none;
    box-shadow: var(--portal-focus-ring);
}
html .fin-topup-btn:active { transform: var(--portal-mo-press); }
@media (prefers-reduced-motion: reduce) {
    html .fin-topup-btn:active { transform: none; }
}
/* Description cell: label + a muted chevron on the linked (order) rows. */
.finances-page .fin-op-desc {
    display: inline-flex;
    align-items: center;
    gap: var(--portal-space-1);
    color: var(--portal-text);
}
/* CATEGORY DOT — a 6px lead on the description, coloured by what kind of movement
   the row is. This is the whole point of the functional palette on the densest
   table in the product: purchase / top-up / refund / commission stop being eight
   identical grey words and become scannable at a glance. The label stays in the
   body ink — the hue is on the dot, not the text (Law 7). */
.finances-page .fin-op-desc::before {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    margin-right: var(--portal-space-1);
    border-radius: var(--portal-radius-pill);
    background: var(--portal-idle);
}
.finances-page .fin-op-desc[data-cat="purchase"]::before { background: var(--portal-hue-iris); }
.finances-page .fin-op-desc[data-cat="top_up"]::before,
.finances-page .fin-op-desc[data-cat="deposit"]::before { background: var(--portal-hue-verdigris); }
.finances-page .fin-op-desc[data-cat="sale"]::before { background: var(--portal-good); }
.finances-page .fin-op-desc[data-cat="refund"]::before { background: var(--portal-reversed); }
.finances-page .fin-op-desc[data-cat="withdraw"]::before { background: var(--portal-hue-sapphire); }
.finances-page .fin-op-desc[data-cat="commission"]::before { background: var(--portal-warn); }
.finances-page .fin-op-chevron {
    width: 15px;
    height: 15px;
    flex: none;
    color: var(--portal-muted);
    opacity: .6;
    transition: transform var(--portal-mo-fast) var(--portal-ease);
}
.finances-page .ns-row-link:hover .fin-op-chevron { transform: translateX(2px); }
/* AMOUNT column reads right-aligned like every money table. */
.finances-page .ns-data-table tbody td:nth-child(4),
.finances-page .ns-data-table thead th:nth-child(4) { text-align: right; }

/* Finances «Пополнить» sub-view header: back link + title + quiet balance. */
.finances-deposit .fin-dep-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: end;
    gap: var(--portal-space-3) var(--portal-space-4);
}
.finances-deposit .fin-dep-head .detail-back { grid-column: 1 / -1; }
.finances-deposit .fin-dep-title {
    margin: 0;
    color: var(--portal-text);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-2xl);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: -.015em;
}
/* The available balance is a QUIET readout, not a rival headline mashed onto the
   title line: a letterspaced micro-label over a tabular figure, aligned right. */
.finances-deposit .fin-dep-balance {
    margin: 0;
    display: grid;
    align-items: flex-end;
    gap: var(--portal-space-1);
    min-width: 132px;
    padding: var(--portal-space-2) var(--portal-space-3);
    border: 1px solid var(--portal-border-soft);
    border-radius: var(--portal-radius-xs);
    background: var(--portal-surface);
    text-align: right;
    box-shadow: var(--portal-shadow-sm);
}
.finances-deposit .fin-dep-balance .fin-balance-sep { display: none; }
.finances-deposit .fin-dep-balance .fin-balance-label {
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--portal-muted);
}
.finances-deposit .fin-dep-balance .fin-balance-figure {
    font-family: var(--portal-font-number);
    font-size: var(--portal-text-lg);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    color: var(--portal-text);
    font-variant-numeric: tabular-nums lining-nums;
}
@media (max-width: 600px) {
    .finances-deposit .fin-dep-head {
        grid-template-columns: minmax(0, 1fr);
        align-items: start;
    }
    .finances-deposit .fin-dep-balance {
        width: auto;
        min-width: 0;
        display: flex;
        align-items: baseline;
        justify-items: stretch;
        gap: var(--portal-space-2);
        padding: 0;
        border: 0;
        background: transparent;
        box-shadow: none;
        text-align: left;
    }
    .finances-deposit .fin-dep-balance .fin-balance-sep { display: inline; }
}

/* ---------------------------------------------------------------------------
   Deposit modal — the «Пополнить» flow in a dialog, opened from the finances page
   (the ?view=deposit sub-page stays the no-JS / deep-link fallback). The DIALOG
   is the card: the sheet inside carries no border, background or shadow of its
   own, so the flow sits in one frame instead of three nested ones. Sized to fit
   the viewport without scrolling — that was the whole complaint.
   --------------------------------------------------------------------------- */
/* Every modal blurs the page behind it, so opening the deposit sheet (or any
   dialog) pulls focus onto the card and the busy list underneath melts away.
   Set in the skin over the legacy flat scrim; the darker tint keeps text on the
   card readable through the blur. */
html .portal-modal-backdrop {
    background: rgba(15, 17, 21, .5);
    -webkit-backdrop-filter: blur(7px) saturate(105%);
    backdrop-filter: blur(7px) saturate(105%);
}
html.dark .portal-modal-backdrop {
    background: rgba(0, 0, 0, .58);
}
/* The dialog is the frame, dressed in the same liquid-glass material as the
   dashboard plaques: translucent fill + the drifting neutral sheen + a layered
   shadow with an inset top highlight. Roomier than before, and both themes are
   stated explicitly so the legacy base panel fill can't show through. */
html:not(.dark) .finances-deposit-modal-panel,
html.dark .finances-deposit-modal-panel {
    width: min(480px, calc(100vw - 32px));
    max-width: 480px;
    gap: var(--portal-space-4);
    padding: var(--portal-space-6) var(--portal-space-6) var(--portal-space-7);
    background-color: color-mix(in srgb, var(--portal-glass) 92%, transparent);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    -webkit-backdrop-filter: blur(22px) saturate(140%);
    backdrop-filter: blur(22px) saturate(140%);
    /* !important to match every other liquid-glass surface — the legacy base
       sheet resets `animation` on the panel, so the drift needs to win. The
       global reduced-motion blanket still overrides the duration. */
    border: 1px solid var(--portal-border);
    box-shadow: var(--portal-shadow-lg), inset 0 1px 0 rgba(255, 255, 255, .06);
}
html:not(.dark) .finances-deposit-modal-panel {
    border-color: color-mix(in srgb, var(--portal-border) 88%, transparent);
}
html.dark .finances-deposit-modal-panel {
    background-color: color-mix(in srgb, var(--portal-glass) 88%, transparent);
    border-color: rgba(255, 255, 255, .06);
}
.finances-deposit-modal-panel .portal-modal-head { padding-right: var(--portal-space-7); }
.finances-deposit-modal-panel .portal-modal-head h2 { font-size: var(--portal-text-xl); }
.finances-deposit-modal-panel .dep1 { margin: 0; }
.finances-deposit-modal-panel .finances-deposit { display: block; }
.finances-deposit-modal-panel .depuid-shell { gap: 0; }
/* The dialog is the frame — strip the sheet's own. */
html .finances-deposit-modal-panel .depuid-panel {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}
/* ============================================================================
   THE close (X) control — one component for the whole portal. Every modal,
   panel and sheet emits it via _portal_close_button() as `.portal-close`, so
   they are pixel-identical. A quiet glass circle with a geometrically-centred
   SVG cross (grid place-items — no font-glyph baseline drift, which is what made
   the old `×`/&times; buttons sit crooked); the hover is an accent-tinted fill
   with a soft lift. Context classes below add ONLY position — never the look.
   Stated as html-prefixed pairs so the legacy base's `html.dark …` close rules
   cannot win on specificity in either theme.
   ============================================================================ */
html:not(.dark) .portal-close,
html.dark .portal-close {
    display: grid;
    place-items: center;
    flex: none;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--portal-border);
    border-radius: 999px;
    background-color: var(--portal-glass);
    -webkit-backdrop-filter: blur(14px) saturate(150%);
    backdrop-filter: blur(14px) saturate(150%);
    color: var(--portal-muted);
    line-height: 0;
    box-shadow: var(--portal-shadow-sm), inset 0 1px 0 rgba(255, 255, 255, .05);
    cursor: pointer;
    transition:
        background-color var(--portal-mo-fast) var(--portal-ease),
        border-color var(--portal-mo-fast) var(--portal-ease),
        box-shadow var(--portal-mo-fast) var(--portal-ease),
        color var(--portal-mo-fast) var(--portal-ease),
        transform var(--portal-mo-fast) var(--portal-ease);
}
/* The cross itself: crisp, square, dead-centre in the circle. */
.portal-close-icon { width: 15px; height: 15px; display: block; margin: 0; }
html:not(.dark) .portal-close:hover,
html.dark .portal-close:hover {
    color: var(--portal-accent-soft-ink);
    background-color: color-mix(in srgb, var(--portal-accent) 12%, var(--portal-glass));
    border-color: var(--portal-accent);
    box-shadow: var(--portal-shadow), inset 0 1px 0 rgba(255, 255, 255, .06);
    transform: translateY(-1px);
}
html .portal-close:active { transform: var(--portal-mo-press); }
html .portal-close:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }
@media (prefers-reduced-motion: reduce) {
    html .portal-close:hover { transform: none; }
}
/* Context — modal close is pinned top-right; the look comes from .portal-close. */
html .portal-modal-close {
    position: absolute;
    right: var(--portal-space-4);
    top: var(--portal-space-4);
    z-index: 2;
}

/* The ONE status pill (shared with orders): tone modifiers only. Six tones, so a
   state that is IN FLIGHT, one that was REVERSED and one where nothing happened
   stop looking identical — that flattening was most of why a dense table read as
   one tone. The unmodified pill is "settled" and stays in the body ink.
   Every tone also renders a glyph: green money and red failure are only ΔE 7.5
   apart under deuteranopia, so colour is a scanning aid on top of the word. */
.ns-status.is-ok { color: var(--portal-good); }
.ns-status.is-progress { color: var(--portal-progress); }
.ns-status.is-warn { color: var(--portal-warn); }
.ns-status.is-danger { color: var(--portal-danger); }
.ns-status.is-reversed { color: var(--portal-reversed); }
.ns-status.is-idle,
.ns-status.is-muted { color: var(--portal-idle); }
.ns-status::before {
    content: "";
    display: inline-block;
    width: 1em;
    margin-right: .1em;
    font-weight: var(--portal-weight-bold);
    text-align: center;
}
.ns-status.is-ok::before { content: "✓"; }
.ns-status.is-progress::before { content: "◴"; }
.ns-status.is-warn::before { content: "!"; }
.ns-status.is-danger::before { content: "✕"; }
.ns-status.is-reversed::before { content: "↩"; }
.ns-status.is-idle::before,
.ns-status.is-muted::before { content: "·"; }
/* Signed amounts on the operations table: top-ups/refunds are green with «+»,
   but a spend (debit) reads in the normal money colour with «−» — NOT red, per
   the reference (the legacy base sheet tints .txn-amount.negative danger). */
.finances-page .txn-amount.negative { color: var(--portal-financial-text); }
.finances-page .txn-amount.danger { color: var(--portal-danger); }

/* ============================================================================
   Marketplaces — connect flow, lot management, sales statistics.

   Tokens only, both themes. The buttons here reuse .ghost-button /
   .ghost-danger rather than the bespoke .mp-refresh-btn / .mp-connect-submit
   shapes the page used to invent.
   ============================================================================ */

.mp-metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--portal-space-3);
    margin-bottom: var(--portal-space-5);
}

.mp-overview-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--portal-space-3);
    flex-wrap: wrap;
}

/* Primary CTA — the entry point that used to be buried in a collapsed
   <details> on a sub-page. */
.mp-connect-cta {
    display: inline-flex;
    align-items: center;
    gap: var(--portal-space-1);
    padding: var(--portal-space-2) var(--portal-space-4);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
    text-decoration: none;
    box-shadow: var(--portal-shadow-sm);
    transition: background var(--portal-mo-fast) var(--portal-ease),
                box-shadow var(--portal-mo-fast) var(--portal-ease),
                transform var(--portal-mo-fast) var(--portal-ease);
}
html .mp-connect-cta:hover,
html.dark .mp-connect-cta:hover { background: var(--portal-accent-dark); }
html .mp-connect-cta:active,
html.dark .mp-connect-cta:active { transform: var(--portal-mo-press); }
.mp-connect-cta:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }

/* Onboarding empty state — a centred column, NOT a box holding another box. The
   hero itself is bare; the single form card below is the only surface. */
.mp-empty-hero {
    padding: var(--portal-space-8) var(--portal-space-6) var(--portal-space-7);
    background: none;
    border: 0;
    box-shadow: none;
    text-align: center;
}
.mp-empty-hero-icon {
    display: inline-grid;
    place-items: center;
    width: 56px;
    height: 56px;
    margin: 0 auto var(--portal-space-4);
    border-radius: var(--portal-radius-md);
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
.mp-empty-hero-glyph { width: 26px; height: 26px; }
.mp-empty-hero > h2 {
    margin: 0 0 var(--portal-space-2);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-2xl);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: -.015em;
    color: var(--portal-text);
    text-wrap: balance;
}
.mp-empty-hero > p {
    margin: 0 auto var(--portal-space-6);
    max-width: 42ch;
    color: var(--portal-muted);
    font-size: var(--portal-text-md);
    text-wrap: pretty;
}
.mp-empty-hero .mp-connect-card { text-align: left; margin-inline: auto; max-width: 30rem; }
/* The hero already names the task ("Connect your first … account"); the card's
   own title would just repeat it. */
.mp-empty-hero .mp-connect-title { display: none; }

.mp-connect-card {
    margin-top: var(--portal-space-5);
    padding: var(--portal-space-6);
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius-md);
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
}
.mp-connect-title {
    margin: 0 0 var(--portal-space-3);
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-xl);
    font-weight: var(--portal-weight-semibold);
    color: var(--portal-text);
}
.mp-connect-help { margin-bottom: var(--portal-space-4); }
.mp-connect-help > summary {
    cursor: pointer;
    color: var(--portal-accent-soft-ink);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-medium);
}
.mp-connect-help > summary:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }
.mp-connect-steps {
    margin: var(--portal-space-3) 0 0;
    padding-left: var(--portal-space-5);
    color: var(--portal-text);
    font-size: var(--portal-text-base);
    line-height: 1.7;
}
.mp-connect-note {
    margin: var(--portal-space-3) 0 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    line-height: 1.6;
}
.mp-connect-form { display: grid; gap: var(--portal-space-3); }
.mp-connect-form label {
    display: grid;
    gap: var(--portal-space-1);
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
}
.mp-connect-form input {
    padding: var(--portal-space-2) var(--portal-space-3);
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface);
    color: var(--portal-text);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-lg);
}
.mp-connect-form input:focus-visible {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
html .mp-connect-submit,
html.dark .mp-connect-submit {
    justify-self: start;
    padding: var(--portal-space-2) var(--portal-space-5);
    border: none;
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
    cursor: pointer;
}
html .mp-connect-submit:hover,
html.dark .mp-connect-submit:hover { background: var(--portal-accent-dark); }
.mp-connect-submit:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }

/* Lot management */
.mp-lots-card {
    margin-bottom: var(--portal-space-5);
    padding: var(--portal-space-5);
    border: 1px solid var(--portal-border-soft);
    border-radius: var(--portal-radius);
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
}
.mp-lots-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--portal-space-3);
    flex-wrap: wrap;
    margin-bottom: var(--portal-space-3);
}
.mp-lots-head > h3 {
    margin: 0;
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-xl);
    font-weight: var(--portal-weight-semibold);
    color: var(--portal-text);
}
.mp-lots-counters { color: var(--portal-muted); font-size: var(--portal-text-sm); }
.mp-lots-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--portal-space-2);
    margin-bottom: var(--portal-space-3);
}
.mp-lots-action { margin: 0; }
/* Honest status of the last queued command — never a fake success. */
.mp-lots-status {
    display: flex;
    align-items: center;
    gap: var(--portal-space-2);
    margin: 0 0 var(--portal-space-3);
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
}

.mp-stats-card .mp-stats-empty {
    margin: 0;
    color: var(--portal-muted);
    font-size: var(--portal-text-base);
}

@media (max-width: 640px) {
    .mp-metric-grid { grid-template-columns: 1fr; }
    .mp-lots-actions > .mp-lots-action { flex: 1 1 100%; }
    .mp-lots-actions > .mp-lots-action > button { width: 100%; }
    .mp-empty-hero { padding: var(--portal-space-6) var(--portal-space-4); }
}

/* ============================================================================
   AI assistant — floating launcher + chat panel.

   Lives outside .app-shell so it survives PJAX. Tokens only, both themes, and
   the entrance animation is dropped entirely under reduced motion.
   ============================================================================ */

.assistant-root {
    position: fixed;
    right: var(--portal-space-6);
    bottom: var(--portal-space-6);
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--portal-space-3);
}

html .assistant-launcher,
html.dark .assistant-launcher {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 52px;
    height: 52px;
    padding: 0;
    border: none;
    border-radius: var(--portal-radius-pill);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    box-shadow: var(--portal-shadow-lg);
    cursor: pointer;
    transition: background var(--portal-mo-fast) var(--portal-ease),
                transform var(--portal-mo-fast) var(--portal-ease);
}
html .assistant-launcher:hover,
html.dark .assistant-launcher:hover { background: var(--portal-accent-dark); }
html .assistant-launcher:active,
html.dark .assistant-launcher:active { transform: var(--portal-mo-press); }
.assistant-launcher:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }
.assistant-launcher > svg { width: 24px; height: 24px; }

.assistant-panel {
    display: flex;
    flex-direction: column;
    width: min(380px, calc(100vw - var(--portal-space-6) * 2));
    max-height: min(560px, calc(100vh - var(--portal-space-8) * 2));
    border: 1px solid var(--portal-border-soft);
    border-radius: var(--portal-radius);
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-lg);
    overflow: hidden;
    animation: wprovider-rise var(--portal-mo-base) var(--portal-ease-out) both;
}
.assistant-panel[hidden] { display: none; }

.assistant-head {
    display: flex;
    align-items: center;
    gap: var(--portal-space-2);
    padding: var(--portal-space-3) var(--portal-space-4);
    border-bottom: 1px solid var(--portal-border-soft);
    background: var(--portal-surface-soft);
}
.assistant-head > strong {
    font-family: var(--portal-heading-font);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
    color: var(--portal-text);
}
.assistant-usage {
    margin-left: auto;
    color: var(--portal-muted);
    font-size: var(--portal-text-xs);
}
/* The assistant panel's close is the shared `.portal-close` glass circle (see
   the close-control block above); it needs no visual override, only its place
   at the end of the header row — the usage span's margin-left:auto pins it. */

.assistant-log {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--portal-space-2);
    padding: var(--portal-space-4);
    overflow-y: auto;
}
.assistant-bubble {
    margin: 0;
    padding: var(--portal-space-2) var(--portal-space-3);
    max-width: 88%;
    border-radius: var(--portal-radius-sm);
    font-size: var(--portal-text-base);
    line-height: 1.55;
    /* Model output is inserted as text, never as markup — newlines still need
       to render, hence pre-wrap rather than any HTML parsing. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}
.assistant-bubble.is-assistant {
    align-self: flex-start;
    background: var(--portal-surface-soft);
    color: var(--portal-text);
}
.assistant-bubble.is-user {
    align-self: flex-end;
    background: var(--portal-accent-soft);
    color: var(--portal-text);
}
.assistant-bubble.is-pending { color: var(--portal-muted); font-style: italic; }
.assistant-bubble.is-error { background: transparent; color: var(--portal-danger); }
.assistant-bubble a { color: var(--portal-accent-soft-ink); }

.assistant-confirm {
    display: flex;
    gap: var(--portal-space-2);
    align-self: flex-start;
    flex-wrap: wrap;
}
html .assistant-confirm-yes,
html.dark .assistant-confirm-yes {
    padding: var(--portal-space-1) var(--portal-space-4);
    border: none;
    border-radius: var(--portal-radius-xs);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    cursor: pointer;
}
html .assistant-confirm-yes:hover,
html.dark .assistant-confirm-yes:hover { background: var(--portal-accent-dark); }
.assistant-confirm-yes:focus-visible,
.assistant-confirm-no:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }

.assistant-form {
    display: flex;
    align-items: flex-end;
    gap: var(--portal-space-2);
    padding: var(--portal-space-3);
    border-top: 1px solid var(--portal-border-soft);
}
.assistant-field { flex: 1 1 auto; display: block; }
/* The label names the control for assistive tech; the placeholder carries the
   visible hint. */
.assistant-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
.assistant-form textarea {
    width: 100%;
    max-height: 120px;
    padding: var(--portal-space-2) var(--portal-space-3);
    border: 1px solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface);
    color: var(--portal-text);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    resize: none;
}
.assistant-form textarea:focus-visible {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
html .assistant-send,
html.dark .assistant-send {
    padding: var(--portal-space-2) var(--portal-space-4);
    border: none;
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    cursor: pointer;
}
html .assistant-send:hover,
html.dark .assistant-send:hover { background: var(--portal-accent-dark); }
html .assistant-send[disabled],
html.dark .assistant-send[disabled] { opacity: .6; cursor: default; }
.assistant-send:focus-visible { outline: none; box-shadow: var(--portal-focus-ring); }

/* On a narrow viewport the panel becomes a sheet, and the launcher lifts clear
   of the mobile tab bar. */
@media (max-width: 640px) {
    .assistant-root {
        right: var(--portal-space-3);
        bottom: calc(var(--portal-space-8) + var(--portal-space-3));
    }
    .assistant-panel {
        position: fixed;
        inset: 0;
        width: 100vw;
        max-height: 100vh;
        border: none;
        border-radius: 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .assistant-panel { animation: none; }
    html .assistant-launcher,
    html.dark .assistant-launcher { transition: none; }
    html .assistant-launcher:active,
    html.dark .assistant-launcher:active { transform: none; }
}

/* ---------------------------------------------------------------------------
   27. Dark theme v6 — "deep ink, lit edge".

   Everything below is html.dark-scoped ON PURPOSE. The approved light theme
   keeps its existing radial sheen geometry untouched; a global rewrite of the
   220% background-size would put a dark smudge in the middle of every light
   card, because light's --portal-sheen is a DARK radial viewed through an
   offset 220% area.
   --------------------------------------------------------------------------- */

/* ONE DIRECTION OF LIGHT, EVERY CARD. Until now .metric-card/.quick-action-card
   inherited `background-size: 220% 220%` at position 0 0, the hero/wallet tiles
   used 220% at 85% -15%, and the tier-1 sheets got no size at all — three
   lighting directions on one screen, two of them from BELOW. */
html.dark .metric-card,
html.dark .quick-action-card,
html.dark .home-feed-card,
html.dark .ns-filter-grid,
html.dark .ns-table-wrap,
html.dark .security-card,
html.dark .sessions-card,
html.dark .ns-profile-card,
html.dark .mp-pulse-card,
html.dark .mp-overview-status-card,
html.dark .mp-activity-card,
html.dark .mp-event-card,
html.dark .mp-account-health-card,
html.dark .mp-channel-summary-card,
html.dark .mp-automation-card,
html.dark .mp-sales-trend-card,
html.dark .mp-product-performance-card,
html.dark .mp-customer-performance-card,
html.dark .mp-inbox-workload-card,
html.dark .mp-order-workload-card,
html.dark .cat-region,
html.dark .cat-pkg-card,
html.dark .steam-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-position: 0 0;
    animation: none;
}

/* THE RIM. Tier 2/3 plates take the full directional bevel. This also finally
   reaches the three biggest cards on Home: the hero and the two wallet tiles
   override box-shadow with --portal-shadow-lg and therefore have had NO inset
   light at all. */
html.dark .metric-card,
html.dark .quick-action-card,
html.dark .home-feed-card,
html.dark .cat-tile,
html.dark .mp-overview-status-card,
html.dark .mp-account-health-card {
    box-shadow: var(--portal-rim);
}
/* Tier-1 sheets can be 600px tall, and a full bevel on a long panel reads as a
   box. Lit top edge plus a light throw only. */
html.dark .ns-filter-grid,
html.dark .ns-table-wrap,
html.dark .security-card,
html.dark .sessions-card,
html.dark .ns-profile-card,
html.dark .mp-activity-card,
html.dark .mp-event-card,
html.dark .mp-channel-summary-card,
html.dark .mp-automation-card,
html.dark .mp-sales-trend-card,
html.dark .mp-product-performance-card,
html.dark .mp-customer-performance-card,
html.dark .mp-inbox-workload-card,
html.dark .mp-order-workload-card {
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .085),
        0 1px 2px rgba(4, 10, 18, .40),
        0 12px 28px -16px rgba(4, 10, 18, .55);
}
/* Rows sat at an opaque --portal-surface inside the wrap; keep them a hair below
   it so the sheet reads as ONE panel rather than a stack of tiles. */
html.dark .ns-table-wrap tr,
html.dark .table-wrap tr {
    background: transparent;
    border-color: var(--portal-border-soft);
}
html.dark .ns-data-table th {
    background: rgba(255, 255, 255, .035);
    border-bottom: var(--portal-rule-w) solid var(--portal-border);
}

/* The balance card is a plain plaque like every other one: the owner does not
   want it singled out by colour. (A green plate lived here briefly — see
   docs/design-system.md. Do not reinstate it without asking.) */
/* The two wallet tiles stay ink glass: if the whole summary row went emerald it
   would read as a green stripe. */
html.dark .ns-home-summary .metric-card.wallet-card {
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-position: 0 0;
    box-shadow: var(--portal-rim);
}

/* ACTIVE NAV — 232x40 on desktop, permanently on screen. */
html.dark .sidebar-link.active {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
    box-shadow: none;
}
html.dark .sidebar-link.active .nav-icon { color: var(--portal-accent-soft-ink); }

/* PRIMARY BUTTON — keeps §18e's directional bevel, gains the top-lit gradient,
   and loses a backdrop-filter that was doing nothing behind a 90% opaque fill. */
html.dark .login-form button,
html.dark .security-step-next,
html.dark .portal-modal button[type="submit"] {
    background-color: var(--portal-accent-dark);
    background-image: var(--portal-accent-gradient);
    color: var(--portal-accent-ink);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, .30),
        inset 0 -1px 0 rgba(4, 40, 24, .32),
        0 1px 2px rgba(4, 10, 18, .50),
        0 8px 20px -8px rgba(53, 206, 133, .34);
}

/* FOCUS — the solid 1px ring carries the 3:1, so the soft outer layer is free to
   read as light rather than as a second border. Scoped tightly: a selector list
   that swallows bare <a> puts a 4px glow on inline links and fragments it across
   line boxes. */
html.dark :is(input, select, textarea, button,
              .portal-select-button, .portal-date-button):focus-visible {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow:
        0 0 0 1px var(--portal-accent-ring),
        0 0 0 4px rgba(53, 206, 133, .20);
}

/* FINANCES — the money screen was a bare canvas with a 2px tick on it. The FIGURE
   stays one solid colour: gradient-clipped text renders a balance in two
   colours and kills selection visibility. */
html.dark .finances-page .fin-balance {
    border-left: 2px solid transparent;
    border-image: linear-gradient(180deg,
        var(--portal-accent) 0%, rgba(53, 206, 133, .10) 100%) 1;
}
html.dark .finances-page .fin-balance-rule {
    background: linear-gradient(90deg,
        rgba(53, 206, 133, .34) 0%, var(--portal-border) 46%, transparent 100%);
}

/* MOBILE TAB BAR — the other surface with genuinely moving content behind it,
   and the one place blur() does real optical work. */
@media (max-width: 860px) {
    html.dark .sidebar {
        background-color: var(--portal-glass-thin) !important;
        background-image: none !important;
        border: 1px solid rgba(255, 255, 255, .10);
        -webkit-backdrop-filter: blur(22px) saturate(175%);
        backdrop-filter: blur(22px) saturate(175%);
        box-shadow:
            inset 0 1px 0 rgba(255, 255, 255, .14),
            0 2px 6px rgba(4, 10, 18, .45),
            0 18px 44px -18px rgba(4, 10, 18, .75);
    }
    html.dark .sidebar-link.active .nav-icon {
        background: var(--portal-accent-soft);
        box-shadow: none;
    }
}

/* ---------------------------------------------------------------------------
   28. Kill the frozen sheen band ("волна").

   The sheen was painted at `background-size: 220% 220%` positioned at
   `85% -15%` — deliberately oversized, because a perpetual animation used to
   slide it across the panel, which read as ambient light in motion. Deleting
   that animation (it repainted a gradient in every animated element on every
   frame) left the oversized gradient FROZEN off-centre, so instead of ambient
   light it became a hard diagonal band across the surface.

   Two different fixes, because these are two different kinds of object:
   - CONTROLS get no sheen at all. A 38px chip is too small to carry a lighting
     gradient; on a button it only ever reads as a smudge.
   - PANELS keep the light but at honest geometry (100% / 0 0), so it is a soft
     top-light instead of a band.

   All of it is html.dark-scoped: the light theme's sheen is a DARK radial and
   re-centring it would put a smudge in the middle of every light card.
   --------------------------------------------------------------------------- */
html.dark .balance-chip,
html.dark .language-switch.lang-row,
html.dark .theme-switch.lang-row,
html.dark .sidebar-collapse,
html.dark .dep1-continue,
html.dark .dep2-saveic,
html.dark .dep1-chip,
html.dark .dep1-method,
html.dark .cat-sort .portal-select-button,
html.dark .cat-buy-btn,
html.dark .cat-cart-chip,
html.dark .cat-plan-chip,
html.dark .cat-buybar,
html.dark .portal-select-button,
html.dark .portal-date-button,
html.dark .ghost-button,
html.dark .ghost-danger,
html.dark .messenger-unread-toggle,
html.dark .ns-pagination button,
html.dark .mp-tabbar,
html.dark .mp-tab {
    background-image: none;
}
/* Panels keep the top light. The geometry reset that used to live here is gone:
   the oversized values were stripped at source (they were vestigial — left over
   from an animation that no longer exists), so nothing has to out-specify
   anything. Kept only for the two panels whose own rules set a size. */
html.dark .bal-hero,
html.dark .bal-route,
html.dark .dep1-card,
html.dark .depuid-card,
html.dark .depuid-hero,
html.dark .cat-region,
html.dark .cat-pkg-card,
html.dark .steam-card,
html.dark .mp-pulse-card,
html.dark .portal-modal-panel,
html.dark .ns-home-summary .metric-card,
html.dark .metric-card,
html.dark .quick-action-card,
html.dark .home-feed-card {
    background-size: 100% 100%;
    background-position: 0 0;
}

/* ---------------------------------------------------------------------------
   29. Sidebar — the marker is the ICON SLOT, not the row.

   The active section used to be a 232x40 accent plate: the single largest,
   loudest, permanently-on-screen block of colour in the product, on a surface
   whose own rule ("colour is earned by meaning, ceiling ~3% of pixels") says a
   sidebar is exactly what must NOT be filled. Owner's call, and it also settles
   an inconsistency: the mobile tab bar has always marked the current section by
   lighting the icon slot alone. Desktop now speaks the same vocabulary.

   Every state lives on the slot: hover neutral, active accent + ring, press the
   press tint plus the dip. The row itself only ever changes its TEXT colour, so
   nothing full-width lights up at any point — including the pointer bloom,
   which is a full-row highlight by definition and is switched off here.

   Placed last on purpose: six earlier blocks set the row plate (1183, 1191,
   1198, 3021, 3029, 8237) and none of them uses !important, so the cascade
   settles this cleanly without a specificity war. Scoped to >=861px because the
   mobile bar already implements this idea in its own geometry (48px circle) and
   must keep it. */
@media (min-width: 861px) {
    /* 1 — the row carries no PERSISTENT fill. Only the resting and the current
       states are cleared; hover and press are TRANSIENT and keep the full-row
       highlight (see 3b). That split is the whole idea: what is permanently on
       screen stays quiet, what answers your pointer right now stays obvious. */
    html:not(.dark) .sidebar-link,
    html.dark .sidebar-link,
    html:not(.dark) .sidebar-link.active,
    html.dark .sidebar-link.active,
    html:not(.dark) .sidebar-link.active:focus-visible,
    html.dark .sidebar-link.active:focus-visible {
        background: transparent;
        background-color: transparent;
        box-shadow: none;
    }

    /* 2 — no slot. The glyph keeps its natural 18px box and the row keeps the
       base grid; the icon never gets a plate, a ring or a radius in ANY state, so
       there is no square anywhere in this panel. Only the transitions stay, for
       the colour change and the press dip. */
    html .sidebar-link .nav-icon {
        background: none;
        box-shadow: none;
        border-radius: 0;
        padding: 0;
        color: var(--portal-muted);
        transition: color var(--mo-fast) var(--mo-ease),
                    transform var(--mo-press-out) var(--mo-ease-press-out);
    }

    /* 3a — CURRENT section: COLOUR, and nothing else. No plate, no outline. Any
       filled shape behind the glyph is a second object competing with the label
       for the same job, and on the one surface that is permanently on screen the
       quietest marker that still works is the right one. The glyph and its label
       both go accent; everything around them stays exactly as it rests. */
    html .sidebar-link.active .nav-icon,
    html .sidebar-link.active { color: var(--portal-accent-soft-ink); }

    /* 3b — HOVER and PRESS stay on the ROW, the way they always were. They are
       transient and pointer-driven: the job there is to show what you are about
       to hit, and the full plate does that better than a 30px slot. The slot
       only warms its glyph so the two states never compete. */
    html:not(.dark) .sidebar-link:hover,
    html.dark .sidebar-link:hover {
        background: var(--portal-hover);
        color: var(--portal-text);
    }
    html .sidebar-link:hover .nav-icon { color: var(--portal-accent-soft-ink); }
    /* Hovering the CURRENT section gets the same neutral plate as any other row.
       An older rule (§1200) filled it with --portal-accent-soft at (0,3,1), which
       outranks the pair above — so it is restated here at matching specificity,
       later in the file. Without this, hovering the active row washes the whole
       plate accent and the icon slot stops reading as the marker. */
    html:not(.dark) .sidebar-link.active:hover,
    html.dark .sidebar-link.active:hover {
        background: var(--portal-hover);
        color: var(--portal-accent-soft-ink);
    }

    html .sidebar-link:not(.active):active,
    html .sidebar-link:not(.active).is-pressing {
        background: var(--portal-press-row);
    }
    html .sidebar-link.active:active,
    html .sidebar-link.active.is-pressing {
        background: var(--portal-accent-press);
    }
    html .sidebar-link:active .nav-icon,
    html .sidebar-link.is-pressing .nav-icon {
        transform: scale(.86);
        transition-duration: var(--mo-fast), var(--mo-press-in);
        transition-timing-function: var(--mo-ease), var(--mo-ease-press-in);
    }

    /* 5 — locked sections stay legible: the padlock row is flex, not the grid. */
    html .sidebar-link.locked .nav-icon { background: none; box-shadow: none; }
}

/* ---------------------------------------------------------------------------
   30. Finances + top-up — one component language with Home.

   The published money screens had drifted into a separate visual product: a
   bare balance statement with an accent rail, plus a deposit sheet made from
   segmented half-buttons and nested glass. The markup now opts into the shared
   metric-card vocabulary; the rules below keep finance-specific layout hooks
   while using the same neutral material, type ladder and button hierarchy.
   --------------------------------------------------------------------------- */

/* Finance hero: one neutral instrument card, including its information footer.
   In dark mode border-soft lands on the sheen's own value and disappears; mix
   the two neutral line weights so the dividers remain quiet but readable. */
.finances-page .fin-head.metric-card {
    --portal-finance-divider: var(--portal-border-soft);
}
html.dark .finances-page .fin-head.metric-card {
    --portal-finance-divider: color-mix(
        in srgb,
        var(--portal-border) 75%,
        var(--portal-border-strong) 25%
    );
}
html:not(.dark) .finances-page .fin-head.metric-card,
html.dark .finances-page .fin-head.metric-card {
    align-content: start;
    min-height: 0;
    padding: var(--portal-space-6);
    gap: var(--portal-space-4);
    overflow: hidden;
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius);
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    background-position: 0 0;
    box-shadow: var(--portal-rim);
}
html:not(.dark) .finances-page .fin-balance,
html.dark .finances-page .fin-balance {
    max-width: none;
    padding: 0;
    border: 0;
    border-image: none;
}
.finances-page .fin-balance-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--portal-space-3) var(--portal-space-5);
    margin: 0;
}
.finances-page .fin-balance-summary {
    display: grid;
    flex: 1 1 auto;
    gap: var(--portal-space-2);
}
.finances-page .fin-balance-figure {
    gap: 0;
    line-height: 1.05;
}
.finances-page .fin-head.metric-card .fin-figure-cur,
.finances-page .fin-head.metric-card .fin-figure-amt {
    align-self: baseline;
    margin: 0;
    color: var(--portal-financial-text);
    font-size: var(--portal-text-3xl);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
}
html:not(.dark) .finances-page .fin-balance-rule,
html.dark .finances-page .fin-balance-rule {
    height: var(--portal-rule-w);
    margin: var(--portal-space-4) 0 var(--portal-space-3);
    background: var(--portal-finance-divider);
}
.finances-page .fin-flow {
    gap: var(--portal-space-1) var(--portal-space-2);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
}
.finances-page .fin-head.metric-card .fin-flow > span,
.finances-page .fin-head.metric-card .fin-balance-note > span,
.finances-page .fin-head.metric-card .fin-topup-btn > span {
    color: inherit;
    font-size: inherit;
    font-weight: inherit;
    letter-spacing: 0;
    text-transform: none;
}
.finances-page .fin-head.metric-card .fin-flow-val {
    color: var(--portal-text);
    font-weight: var(--portal-weight-semibold);
}
.finances-page .fin-head.metric-card .fin-flow-val.is-in {
    color: var(--portal-good);
}
.finances-page .fin-head.metric-card .fin-flow-val.is-pending {
    color: var(--portal-muted);
}
.finances-page .fin-head.metric-card .fin-flow-val.is-review {
    color: var(--portal-danger);
}
.finances-page .fin-head.metric-card .fin-flow-k,
.finances-page .fin-head.metric-card .fin-balance-note > span {
    color: var(--portal-muted);
}
.finances-page .fin-flow-dot {
    padding: 0;
}
.finances-page .fin-balance-note {
    padding-top: var(--portal-space-4);
    border-top: var(--portal-rule-w) solid var(--portal-finance-divider);
    font-family: var(--portal-font-ui);
}
.finances-page .fin-banner-icon {
    width: var(--portal-space-4);
    height: var(--portal-space-4);
}

/* Primary means primary: the same solid brand action as the portal's other
   submit buttons, compact enough to sit beside the metric label. */
html:not(.dark) .fin-topup-btn,
html.dark .fin-topup-btn {
    flex: none;
    min-height: calc(var(--portal-space-8) - var(--portal-space-2));
    padding: 0 var(--portal-space-4);
    border: var(--portal-rule-w) solid var(--portal-accent);
    border-radius: var(--portal-radius-sm);
    background-color: var(--portal-accent);
    background-image: var(--portal-accent-gradient);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    box-shadow: var(--portal-shadow-sm);
}
html:not(.dark) .fin-topup-btn:hover,
html.dark .fin-topup-btn:hover {
    border-color: var(--portal-accent-dark);
    background: var(--portal-accent-dark);
    color: var(--portal-accent-ink);
    box-shadow: var(--portal-shadow);
}
.fin-topup-icon {
    width: var(--portal-space-4);
    height: var(--portal-space-4);
}

/* Standalone top-up heading: page title + a quiet inline readout, not another
   floating card competing with the payment sheet. */
.finances-deposit .fin-dep-head {
    align-items: center;
}
.finances-deposit .fin-dep-balance {
    display: flex;
    align-items: baseline;
    gap: var(--portal-space-2);
    min-width: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    text-align: right;
}
.finances-deposit .fin-dep-balance .fin-balance-sep {
    display: inline;
}
.finances-deposit .fin-dep-balance .fin-balance-label {
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-medium);
    letter-spacing: 0;
    text-transform: none;
}
.finances-deposit .fin-dep-balance .fin-balance-figure {
    color: var(--portal-financial-text);
    font-family: var(--portal-font-figure);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
}

/* The standalone sheet is a shared metric card. In a modal the dialog remains
   the only frame, so the inner article is deliberately stripped back. */
html .dep1--uid:not(.dep1--embedded) .depuid-shell:has(.dep-methods-switch) {
    max-width: 100%;
}
html:not(.dark) .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card,
html.dark .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card {
    gap: var(--portal-space-5);
    padding: var(--portal-space-6);
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius);
    background-color: var(--portal-card-glass);
    background-image: var(--portal-sheen);
    background-repeat: no-repeat;
    background-size: 100% 100%;
    box-shadow: var(--portal-rim);
}
html:not(.dark) .dep1--embedded .depuid-panel.metric-card,
html.dark .dep1--embedded .depuid-panel.metric-card {
    overflow: visible;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}

/* Wide flows separate method navigation from the selected payment detail. The
   exchange-style list remains vertical and all existing radio/data hooks stay
   untouched. */
html .dep-pay:has(> .dep-methods-switch) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    column-gap: var(--portal-space-6);
    row-gap: var(--portal-space-5);
}
html .dep-pay:has(> .dep-methods-switch) > .dep-methods-switch {
    align-self: start;
    padding-right: var(--portal-space-6);
    border-right: var(--portal-rule-w) solid var(--portal-border-soft);
}
html .dep-selector-block {
    gap: var(--portal-space-2);
}
html .dep-selector-title,
html .dep-methods-list-head {
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-medium);
    letter-spacing: 0;
}
html .dep-asset-switch {
    gap: var(--portal-space-1);
    padding: var(--portal-space-1);
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
}
html .dep-asset-pill-body {
    min-height: var(--portal-space-8);
    padding: var(--portal-space-2);
    border-radius: var(--portal-radius-xs);
}
html .dep-asset-pill input:checked + .dep-asset-pill-body {
    background: var(--portal-surface);
    box-shadow: var(--portal-shadow-sm);
}
html .dep-asset-ic {
    width: var(--portal-space-6);
    height: var(--portal-space-6);
}
html .dep-asset-title {
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
}
html .dep-asset-sub {
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-normal);
}
html .dep-network-group {
    overflow: hidden;
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
}
html .dep-methods-list-head {
    padding: var(--portal-space-3) var(--portal-space-3) var(--portal-space-2);
    border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
}
html .dep-method-pill-body {
    min-height: calc(var(--portal-space-8) + var(--portal-space-3));
    padding: var(--portal-space-2) var(--portal-space-3) var(--portal-space-2) var(--portal-space-4);
    border-radius: 0;
    background: transparent;
}
html .dep-method-pill input:checked + .dep-method-pill-body {
    background: var(--portal-surface);
    box-shadow: none;
}
html .dep-method-title {
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
}
html .dep-method-sub {
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-normal);
}
html .dep-method-meta-main {
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-semibold);
}
html .dep-method-meta-sub {
    font-size: var(--portal-text-xs);
    font-weight: var(--portal-weight-normal);
}
html .dep-method-pane {
    gap: var(--portal-space-5);
}
html .depuid-method-row {
    padding-bottom: var(--portal-space-3);
    border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
}
html .depuid-method-id strong {
    font-size: var(--portal-text-lg);
    font-weight: var(--portal-weight-semibold);
}
html .depuid-method-id span {
    font-size: var(--portal-text-sm);
}

/* Deposit fields reuse the portal input shell. Save is the primary action;
   Copy is a compact ghost icon inside a row whose entire surface remains the
   keyboard/touch target. */
html .dep-field {
    gap: var(--portal-space-2);
}
html .dep-field-label {
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    text-transform: none;
}
html:not(.dark) .dep-field-row,
html.dark .dep-field-row {
    min-height: var(--portal-space-8);
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
    box-shadow: none;
}
html:not(.dark) .dep-field--warn .dep-field-row,
html.dark .dep-field--warn .dep-field-row {
    border-color: var(--portal-warn-line);
}
@media (hover: hover) {
    html .dep-field-row--copy:hover {
        border-color: var(--portal-accent);
        box-shadow: none;
    }
}
html:not(.dark) .dep-field-value,
html.dark .dep-field-value {
    padding: var(--portal-space-2) var(--portal-space-4);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
}
html .dep-field-input {
    padding: 0 var(--portal-space-4);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
}
html .dep-field-hint {
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-sm);
}
html:not(.dark) .portal-modal button.dep-field-action,
html.dark .portal-modal button.dep-field-action,
html:not(.dark) .dep-field-action,
html.dark .dep-field-action {
    overflow: visible;
    align-self: stretch;
    margin: var(--portal-space-1);
    padding: 0 var(--portal-space-3);
    border: 0;
    border-radius: var(--portal-radius-xs);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-semibold);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: var(--portal-shadow-sm);
    filter: none;
}
html:not(.dark) .portal-modal button.dep-field-action:hover,
html.dark .portal-modal button.dep-field-action:hover,
html:not(.dark) .dep-field-action:hover,
html.dark .dep-field-action:hover {
    background: var(--portal-accent-dark);
    color: var(--portal-accent-ink);
    filter: none;
}
html:not(.dark) .depuid-copy-action,
html.dark .depuid-copy-action {
    align-self: center;
    width: var(--portal-space-7);
    height: var(--portal-space-7);
    margin: var(--portal-space-1);
    padding: 0;
    border: var(--portal-rule-w) solid var(--portal-border-soft);
    border-radius: var(--portal-radius-xs);
    background: var(--portal-surface);
    color: var(--portal-muted);
    font-family: var(--portal-font-ui);
    box-shadow: var(--portal-shadow-sm);
}
html .depuid-copy-label {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
html .depuid-copy-status {
    position: absolute;
    width: var(--portal-rule-w);
    height: var(--portal-rule-w);
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}
html .depuid-copy-action .dep2-copyic {
    width: var(--portal-space-5);
    height: var(--portal-space-5);
}
@media (hover: hover) {
    html .dep-field-row--copy:hover .depuid-copy-action {
        background: var(--portal-accent-soft);
        color: var(--portal-accent-soft-ink);
    }
}
html .dep-field-row--copy:focus-visible .depuid-copy-action {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
html .dep-field-row--copy.is-copied {
    border-color: var(--portal-good-line);
}
html .dep-field-row--copy.is-copied .depuid-copy-action {
    border-color: var(--portal-good-line);
    background: var(--portal-good-soft);
    color: var(--portal-good);
}
/* The legacy address variant is more specific than the shared copy control.
   Repeat the shared material here so UID and wallet addresses stay identical. */
html:not(.dark) .dep-field--addr .depuid-copy-action,
html.dark .dep-field--addr .depuid-copy-action {
    border: var(--portal-rule-w) solid var(--portal-border-soft);
    background: var(--portal-surface);
    color: var(--portal-muted);
}
html .dep-field--addr .depuid-copy-action .dep2-copyic {
    width: var(--portal-space-5);
    height: var(--portal-space-5);
}
@media (hover: hover) {
    html .dep-field--addr .dep-field-row--copy:hover .depuid-copy-action {
        background: var(--portal-accent-soft);
        color: var(--portal-accent-soft-ink);
    }
}
html .dep-field--addr .dep-field-row--copy:focus-visible .depuid-copy-action {
    background: var(--portal-accent-soft);
    color: var(--portal-accent-soft-ink);
}
html .dep-field--addr .dep-field-row--copy.is-copied .depuid-copy-action {
    border-color: var(--portal-good-line);
    background: var(--portal-good-soft);
    color: var(--portal-good);
}

/* Rest-state material is declared late in this section, so keyboard focus is
   deliberately reasserted after it. Network rows use an inset ring because
   their shared tray clips outside shadows. */
html .fin-topup-btn:focus-visible,
html .dep-field-row--copy:focus-visible,
html .dep-field-row--form:focus-within {
    outline: none;
    border-color: var(--portal-accent);
    box-shadow: var(--portal-focus-ring);
}
html .dep-asset-pill input:focus-visible + .dep-asset-pill-body {
    box-shadow: var(--portal-focus-ring);
}
html .dep-method-pill input:focus-visible + .dep-method-pill-body {
    box-shadow: var(--portal-focus-ring-inset);
}
html:not(.dark) .portal-modal button.dep-field-action:focus-visible,
html.dark .portal-modal button.dep-field-action:focus-visible,
html:not(.dark) .dep-field-action:focus-visible,
html.dark .dep-field-action:focus-visible {
    outline: none;
    box-shadow: var(--portal-focus-ring);
}

/* Payment facts and proof form use the same data and field type roles as the
   operations filters; no bespoke weights or glass submit button. */
html .dep-sheet-rows > div {
    gap: var(--portal-space-4);
    padding: var(--portal-space-3) 0;
    border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
}
html .dep-sheet-rows > div:first-child {
    border-top: var(--portal-rule-w) solid var(--portal-border-soft);
}
html .dep-sheet-rows dt,
html .dep-sheet-rows dd {
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
}
html .dep-claim {
    gap: var(--portal-space-3);
}
html .dep-claim-title {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    text-transform: none;
}
html .dep-claim-form {
    grid-template-columns: minmax(0, 1fr) minmax(0, 2fr) auto;
    gap: var(--portal-space-2);
}
html .dep-claim-field {
    gap: var(--portal-space-2);
}
html .dep-claim-field > span {
    color: var(--portal-muted);
    font-size: var(--portal-text-sm);
    font-weight: var(--portal-weight-semibold);
    letter-spacing: 0;
    text-transform: none;
}
html .dep-claim-input {
    min-height: var(--portal-space-8);
    padding: 0 var(--portal-space-4);
    border: var(--portal-rule-w) solid var(--portal-border);
    border-radius: var(--portal-radius-sm);
    background: var(--portal-surface-soft);
    font-size: var(--portal-text-md);
    font-weight: var(--portal-weight-semibold);
}
html:not(.dark) .dep-claim-submit,
html.dark .dep-claim-submit,
html:not(.dark) .portal-modal button.dep-claim-submit,
html.dark .portal-modal button.dep-claim-submit {
    overflow: visible;
    min-height: var(--portal-space-8);
    padding: 0 var(--portal-space-4);
    border: 0;
    border-radius: var(--portal-radius-sm);
    background: var(--portal-accent);
    color: var(--portal-accent-ink);
    font-family: var(--portal-font-ui);
    font-size: var(--portal-text-base);
    font-weight: var(--portal-weight-semibold);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: var(--portal-shadow-sm);
    filter: none;
}
html:not(.dark) .dep-claim-submit:hover,
html.dark .dep-claim-submit:hover,
html:not(.dark) .portal-modal button.dep-claim-submit:hover,
html.dark .portal-modal button.dep-claim-submit:hover {
    background: var(--portal-accent-dark);
    color: var(--portal-accent-ink);
    filter: none;
}
html:not(.dark) .portal-modal button.dep-claim-submit:focus-visible,
html.dark .portal-modal button.dep-claim-submit:focus-visible,
html:not(.dark) .dep-claim-submit:focus-visible,
html.dark .dep-claim-submit:focus-visible {
    outline: none;
    box-shadow: var(--portal-focus-ring);
}

/* The dialog is one opaque portal surface. Multi-method flows use the shared
   wide measure; one-method Bybit onboarding keeps the compact modal width. */
html:not(.dark) .finances-deposit-modal-panel,
html.dark .finances-deposit-modal-panel {
    background-color: var(--portal-surface);
    background-image: none;
    border-color: var(--portal-border);
    box-shadow: var(--portal-shadow-lg);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}
html:not(.dark) .finances-deposit-modal-panel:has(.dep-methods-switch),
html.dark .finances-deposit-modal-panel:has(.dep-methods-switch) {
    width: min(var(--portal-dialog-wide), calc(100vw - (var(--portal-space-6) * 2)));
    max-width: var(--portal-dialog-wide);
}

@media (max-width: 980px) {
    html .dep-pay:has(> .dep-methods-switch) {
        grid-template-columns: minmax(0, 1fr);
    }
    html .dep-pay:has(> .dep-methods-switch) > .dep-methods-switch {
        padding-right: 0;
        padding-bottom: var(--portal-space-5);
        border-right: 0;
        border-bottom: var(--portal-rule-w) solid var(--portal-border-soft);
    }
    html:not(.dark) .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card,
    html.dark .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card {
        padding: var(--portal-space-5);
    }
}

@media (max-width: 600px) {
    html:not(.dark) .finances-page .fin-head.metric-card,
    html.dark .finances-page .fin-head.metric-card {
        padding: var(--portal-space-4);
    }
    .finances-page .fin-balance-top {
        column-gap: var(--portal-space-3);
    }
    .finances-page .fin-head.metric-card .fin-figure-cur,
    .finances-page .fin-head.metric-card .fin-figure-amt {
        font-size: var(--portal-text-2xl);
    }
    .finances-deposit .fin-dep-head {
        align-items: flex-start;
        gap: var(--portal-space-2);
    }
    .finances-deposit .fin-dep-balance {
        text-align: left;
    }
    html:not(.dark) .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card,
    html.dark .dep1--uid:not(.dep1--embedded) .depuid-panel.metric-card {
        gap: var(--portal-space-4);
        padding: var(--portal-space-4);
        border-radius: var(--portal-radius-md);
    }
    html .dep-asset-pill-body {
        grid-template-columns: minmax(0, 1fr);
        justify-items: center;
        gap: var(--portal-space-1);
        text-align: center;
    }
    html .dep-asset-copy {
        justify-items: center;
    }
    html .dep-field-action {
        padding: 0 var(--portal-space-2);
    }
    html .dep-claim-form {
        grid-template-columns: minmax(0, 1fr);
    }
}
