/*! COPIE ÎNGHEȚATĂ bono-ds — nu edita aici; modificările DS se fac în bono-ro/edge-33
   copiat:  2026-10-17T20:38Z
   sursă:   bono-ro/edge-33 · v8.5-dev · 2026-10-15 (pachetul bgn-skills a4eabecbc203)
   canonic: https://edge-33.vercel.app/bono-ds.css */
/*! bono-ds v8.5-dev · sursă: bono-ro/edge-33 · modificat: 2026-10-15 */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fraunces:ital,opsz,wght@0,9..144,500;0,9..144,700;1,9..144,500;1,9..144,700&family=Source+Serif+4:ital,wght@0,400;0,600;1,400&family=JetBrains+Mono:wght@400;500&display=swap');

/* ═══════════════════════════════════════════════════════════════
   BONO · Design System — v6.0 cleanup
   Changelog față de v5:
   - Tipografie: scala v3 (semantică) și v4 (numerică) unificate
     pe un singur selector. Ambele clase funcționează.
   - Alias-uri eliminate din :root (--c-bg, --c-bg-card, --c-bg-deep,
     --c-surface, --c-night, --c-paper). Componentele folosesc acum
     direct numele canonice: --c-bej-0, --c-bej-1, --c-white, --c-dark.
   - Token nou --c-dark-mid (#4A4448) pentru gradient avatar.
   - Hard-code fixes: card-hairline border, vox-fab radius,
     t20 strong color.
   v6.2:
   - --c-dark RENAMED to --c-danger: background token, destructive button
     fill only (.btn.danger). --c-ink stays the text token. Same hex,
     never interchangeable. --c-dark survives only as a deprecated alias
     for the frozen doc-originale pages.
   - New tokens: --c-danger-hover (danger fill hover), --c-dark-grad
     (dark decorative surfaces — magenta ramp from the Tailwind
     pink-700→800 delta; placeholder, under design).
   - .v3-topnav deprecated (kept for legacy/frozen pages only).
   ═══════════════════════════════════════════════════════════════ */

:root {
  /* ─── Versiune DS — ține-o sincronă cu banner-ul de pe linia 1.
     Inspectabilă la runtime în orice pagină care încarcă acest fișier:
     getComputedStyle(document.documentElement).getPropertyValue('--ds-version') ─── */
  --ds-version: "v8.5-dev · 2026-10-15";

  /* ─── Ultimul release breaking — vezi VERSIONING.md.
     Majorul se schimbă DOAR când o versiune sparge ceva, deci valoarea de aici e
     mereu un x.0: e destul să compari majorul tău cu al ei.
     O copie ștampilată cu un număr MAI MIC decât valoarea asta are nevoie de
     MIGRARE, nu de un simplu re-copy: notele release-ului spun ce call-site-uri
     se mută în același pas cu fișierul. Copiile de dinaintea numerotării (orice
     ștampilă v1–v7, dinainte de primul release GitHub) intră toate în cazul ăsta.
     Egal sau mai mare ⇒ re-copy simplu, verificat cu check-stamp-drift.py. ─── */
  --ds-breaking-since: "v8.0";

  /* ─── Familii tipografice ─── */
  /* Promovate din bono-skills-website (theme.css). Până aici, familia era scrisă
     de mână în 78 de declarații; o schimbare de brand însemna 78 de editări și
     copia din aplicație nu putea rămâne o ștampilă, fiindcă tocmai refactorul
     ăsta o transformase într-un fork. Valorile sunt verbatim cele din aplicație,
     ca cele două fișiere să spună același lucru. */
  --font-sans:    'Inter Variable', 'Inter', system-ui, -apple-system, sans-serif;
  --font-serif:   'Source Serif 4', Georgia, serif;   /* body editorial — T20 */
  --font-mono:    'JetBrains Mono', ui-monospace, monospace;
  --font-display: 'Fraunces', serif;                  /* italic de display, via <em> */

  /* ─── Brand ─── */
  --c-pink:        #EE4379;
  --c-pink-deep:   #C9255B;
  --c-pink-tint:   #FDE7F0;
  --c-pink-ultra:  rgba(238, 67, 121, 0.06);

  /* ─── Accent — interactive states (checked, active, focus, selected) ─── */
  /* Schimbă doar aici dacă brandul evoluează sau vrei o temă nouă */
  --c-accent:       var(--c-pink);
  --c-accent-deep:  var(--c-pink-deep);
  --c-accent-tint:  var(--c-pink-tint);
  --c-accent-ultra: var(--c-pink-ultra);

  /* ─── Surfaces — LOCKED (4 surfaces) ─── */
  --c-white:       #FFFFFF;       /* lifted: KPI, table card, inputs */
  --c-bej-0:       #FBFAF7;       /* page bg — unic peste tot */
  --c-bej-05:      #F7F5F2;       /* HOVER de rând/listă — treapta dintre bej-0 și bej-1. NU e a cincea
                                     suprafață: suprafețele rămân patru, asta e o stare, ca --c-hover-veil.
                                     Extras 2026-09-09 din bono-skills-website: rândul de tabel de acolo
                                     hover-ează pe `hover:bg-muted/50` (shared/ui/table.tsx), iar --muted e
                                     --c-bej-1 — deci hover-ul randat era bej-1 la 50% peste corpul alb al
                                     tabelului: #F7F5F2. Valoarea exista în producție de la primul stamp și
                                     nu avea nume în niciunul dintre repo-uri.
                                     SOLID, nu alpha: suprafețele sunt solide aici (alpha e limbajul liniilor),
                                     iar o copie ștampilată se poate compara doar pe un literal. Peste bej-0
                                     același amestec de 50% dă #F5F3EE — două trepte sub asta, imperceptibil,
                                     deci o singură valoare solidă servește ambele gazde.
                                     Text: --c-ink 17.72:1. --c-fog dă 4.19:1, sub AA — meta dintr-un rând
                                     hover-at rămâne pe --c-ink (pe --c-bej-1 fog e oricum 3.84:1). */
  --c-bej-1:       #EFEBE4;       /* tonal: sidebar, action cards, thead */
  --c-hover-veil:  rgba(221, 214, 201, 0.55); /* SINGLE hover fill for the neutral buttons
                                     (.btn.secondary, .btn.outline). Translucent on purpose: it is
                                     layered over whatever the button's own rest fill is, so the
                                     hover step is relative to its host instead of naming a surface.
                                     Over bej-1 (secondary's fill) → ~#E5DFD5. Over white → ~#ECE9E1,
                                     a hair off bej-1, which is what .outline hover used to hardcode.
                                     Fixes .outline disappearing on a bej-1 host. Buttons only —
                                     not a surface token, never use it as a background on a panel. */
  --c-selection:   #DDD6C9;       /* BANDA DE SELECȚIE DE TEXT (::selection). Stare, nu suprafață — ca
                                     --c-hover-veil și --c-bej-05. Neutră DINADINS: selecția e a
                                     utilizatorului, nu a brandului. Fără o declarație aici, o gazdă
                                     shadcn cade pe `selection:bg-primary` (clasa e pe <Input> în
                                     shadcn/ui), deci un câmp de căutare focusat cu cmd+A ieșea cu banda
                                     roz și text alb — brandul țipând pe un gest de editare.
                                     SOLID, nu alpha: ::selection nu compune predictibil peste fundalul
                                     gazdei în toate motoarele, iar un literal e singurul lucru pe care
                                     check-stamp-drift.py îl poate compara. Perechea se declară întreagă
                                     (fundal ȘI text), deci citirea nu depinde de gazdă: pe alb ΔL* 14.15,
                                     pe bej-0 12.42, pe bej-1 7.33, iar pe o suprafață închisă
                                     (.card.dark) banda rămâne deschisă cu text ink.
                                     Text: --c-ink dă 13.35:1. --c-fog dă 3.16:1 și NU trece, de aceea
                                     regula forțează --c-ink — un meta gri selectat nu-și păstrează griul.
                                     Același hex ca --c-rule-solid, rol diferit și niciodată interschimbabil
                                     — exact relația --c-danger / --c-ink. */
  --c-danger:      #110D10;       /* BACKGROUND token — destructive button fill ONLY (.btn.danger).
                                     Never for text: black text is --c-ink (same hex, separate role). */
  --c-danger-hover:#2E2926;       /* hover state for the black destructive fill. Lifted from #1F1B17
                                     (2026-10-12): press returns to --c-danger, and at #1F1B17 the two
                                     blacks collapsed on a real screen — hover and press looked the same.
                                     L* 17 vs 4.6 makes the press step visible; white on it is 14.4:1. */
  --c-dark-grad:   linear-gradient(135deg, #2F081A 0%, #09041D 100%);
                                  /* dark DECORATIVE surfaces: .sec-dark, .card.dark, .kpi.dark,
                                     .tabs.dark, .avatar.dark, .page-banner.dark, .vox-fab.
                                     Hand-tuned ramp: deep magenta → near-black violet.
                                     Placeholder — under design, value may change. */
  --c-dark:        var(--c-danger); /* DEPRECATED alias — kept only so frozen doc-originale pages
                                       still render. Do not use in new work. */
  --c-dark-mid:    #4A4448;       /* dark raised — folosit în avatar gradient */

  /* ─── Text ─── */
  --c-ink:         #110D10;       /* TEXT token — primary text on light surfaces.
                                     Never as background: black fills are --c-dark / --c-dark-grad. */
  --c-fog:         #7A7570;       /* labels, meta secundar */
  --c-mist:        #ABA59E;       /* placeholder, muted */

  /* ─── Lines ─── */
  --c-rule-card:   rgba(17, 13, 16, 0.12);   /* border CARD/KPI — separare ușoară de container */
  --c-input-border: rgba(17, 13, 16, 0.30);  /* border INPUT/SELECT + controale mici — v6.1: contrast pe suprafețe bej */
  --c-rule:        rgba(17, 13, 16, 0.10);   /* hairline secundar */
  --c-rule-hover:  rgba(17, 13, 16, 0.20);   /* hairline pe hover — o treaptă peste --c-rule (accordion) */
  --c-input-border-hover: rgba(17, 13, 16, 0.58); /* border INPUT/SELECT pe hover — două trepte mai închis decât --c-input-border (0.30) */
  --c-input-border-tonal: rgba(17, 13, 16, 0.46); /* border INPUT/SELECT tonal — pe .tonal fundalul (--c-bej-0) nu mai
                                     separă câmpul de cardul alb (1.04:1), deci conturul duce singur limita
                                     controlului. La 0.30 măsoară 2.00:1, sub pragul de 3:1 pentru componente
                                     non-text (WCAG 1.4.11); 0.46 e treapta minimă care trece: 3.16:1 peste
                                     propriul fill bej-0 și 3.30:1 peste cardul alb. Calibrat exact pe contextul
                                     documentat (gazdă albă sau bej-0) — pe o gazdă bej-1 dă 2.78:1 și NU trece,
                                     de aceea .tonal nu se pune pe .card.tonal (acolo inputul e alb, contextul A). */
  --c-rule-soft:   rgba(17, 13, 16, 0.06);   /* divider intern */
  --c-rule-solid:  #DDD6C9;                  /* fallback solid (peste imagini/gradient) */
  --c-rule-soft-solid: #EAE3D5;

  /* ─── Status ─── */
  --c-success:     #1F7A3A;   /* fill/stroke: bordură, iconiță, punct, fill de badge — status POZITIV (verde).
                                 NU ca text sub 24px: pică AA pe --c-bej-1 (4.53 e prag de non-text, nu de text).
                                 Text = --c-success-text. Alb pe el = 5.38 (.badge.filled — singurul alb-pe-fill). */
  --c-success-bg:  #EBF7EF;   /* tint suprafețe de status pozitiv */
  --c-success-text:#076E26;   /* text de status pozitiv — SINGURUL token de text al familiei.
                                 6.43 alb / 6.16 bej-0 / 5.41 bej-1 / 5.84 tint */
  --c-warn:        #B8580A;   /* fill/stroke: bordură, iconiță, fill de badge — status de ATENȚIE (ambră).
                                 NU ca text: 4.23 pe propriul tint, 3.98 pe --c-bej-1. Text = --c-warn-text. */
  --c-warn-bg:     #FBF1E2;   /* tint suprafețe de status de atenție */
  --c-warn-text:   #A95109;   /* text de status de atenție — SINGURUL token de text al familiei.
                                 5.43 alb / 5.20 bej-0 / 4.57 bej-1 / 4.86 tint */
  --c-error:       #DC2626;   /* fill/stroke NUMAI: borduri, iconițe, fill de badge — mesagerie de EROARE (roșu).
                                 NU ca text: pică AA pe propriul tint (4.38:1) și pe --c-bej-1 (4.06:1),
                                 la ORICE mărime sub 24px — bold nu schimbă pragul. Text = --c-error-text. */
  --c-error-bg:    #FDF1F1;   /* tint suprafețe de eroare: bg input error, banner */
  --c-error-text:  #CE2222;   /* text de eroare — SINGURUL token de text al familiei. Trece AA pe toate
                                 cele patru suprafețe DS: 5.42 alb / 5.19 bej-0 / 4.56 bej-1 / 4.91 tint */
  --c-ai:          #A8336E;
  --c-ai-bg:       #F4E4EE;
  --c-ai-grad:     linear-gradient(135deg, #C9255B 0%, #7B2A6E 100%);
                              /* Primul stop a fost --c-pink (#EE4379) până pe 2026-09-22. Toate cele
                                 trei suprafețe care poartă gradientul ăsta scriu ALB pe el — .badge.live,
                                 .badge.filled.ai, .vox-fab.ai — iar albul pe #EE4379 dă 3.67:1, sub AA
                                 la orice mărime pe care o folosesc (11-15px). Nicio alpha nu salva
                                 nimic: 2.02 / 2.48 / 3.03 / 3.67 pe 55/70/85/100. Pe --c-pink-deep
                                 (#C9255B) albul dă 5.39:1 și trec toate trei deodată. Capătul violet
                                 e neschimbat (8.76:1). Brandul rămâne --c-pink; ăsta e gradientul AI,
                                 care e o SUPRAFAȚĂ DE TEXT — de asta pornește de pe treapta adâncă.
                                 Istoricul și nota de migrare: tokens.semantics.json → value_history. */
  --c-read:        #5C6BC0;   /* periwinkle indigo — status "citit / văzut" */
  --c-read-bg:     #ECEFFE;   /* tint — fundal status citit */
  --c-read-text:   #5461AF;   /* text "citit" <18px pe tint — AA (4.95:1 pe --c-read-bg); --c-read pică la 4.25:1 */

  /* ─── Radii ─── */
  --r-xs:          6px;
  --r-sm:          10px;
  --r-md:          14px;   /* carduri inline, hairline */
  --r-lg:          20px;   /* butoane lg, vox-fab, modal */
  --r-xl:          24px;   /* carduri content, KPI hero */
  --r-full:        9999px; /* butoane, badges, tabs, nav — capetele complet rotunde */
  /* DEPRECAT — alias pentru --r-full. Numele vechi rămâne valid pentru consumatorii
     care îl folosesc deja; cod nou scrie --r-full (convenția shadcn: rounded-full). */
  --r-pill:        var(--r-full);

  /* ─── Spacing ─── */
  --sp-4:          4px;
  --sp-8:          8px;
  --sp-12:         12px;
  --sp-16:         16px;
  --sp-24:         24px;
  --sp-28:         28px;
  --sp-40:         40px;
  --sp-56:         56px;
  --sp-92:         92px;

  /* ─── Shadows ─── */
  --sh-xs:      0 1px 2px rgba(17,13,16,0.04);   /* floor: lift abia perceptibil (sidebar active) */
  --sh-sm:      0 1px 3px rgba(17,13,16,0.06), 0 1px 2px rgba(17,13,16,0.04);
  --sh-md:      0 4px 14px rgba(17,13,16,0.10);
  --sh-pink:    0 6px 16px rgba(238,67,121,0.30);
  --sh-popover: 0 12px 32px rgba(17,13,16,0.12);
  /* Toast — două straturi: contactul de 1px „așază" cutia pe suprafață,
     ambientul mai jos și mai intens îi dă ridicarea. --sh-popover era un
     singur strat difuz care pe topnav-ul alb se citea ca o ceață. */
  --sh-toast:   0 1px 2px rgba(17,13,16,0.04), 0 6px 24px rgba(17,13,16,0.18);

  /* ─── Grid · dot pattern ─── */
  --grid-color:  rgba(17, 13, 16, 0.15);
  --grid-size:   32px;
  --grid-radius: 1px;
  --grid-edge:   1.5px;

  /* ─── Layout · breakpoint unic + grilă de 12 coloane ───
     DS-ul are UN SINGUR breakpoint: 1024px. Sub el = mobile (layout mobil +
     .flow-chrome). De la el în sus = desktop (.app-shell + .layout-12, sidebar
     opțional). Nu se adaugă breakpoint-uri intermediare — dacă un layout are
     nevoie de unul, problema e layout-ul.
     Custom properties NU funcționează în `@media`, deci valoarea e scrisă
     literal acolo (`min-width: 1024px` / `max-width: 1023.98px`); tokenul
     rămâne sursa declarată a valorii, pentru consumatori (JS, docs, audit). */
  --bp-desktop:     1024px;  /* singurul breakpoint din DS */
  --layout-max:     1440px;  /* lățimea canvasului pe care se desenează: layout-ul e proiectat la
                                1440, deci nu se întinde peste ce a fost văzut la design. Pe full HD
                                zona de conținut de lângă rail (1920 − 240 = 1680) e mai lată decât
                                capul — grila se oprește la 1440. Cu sidebar stă lipită de rail și
                                restul devine aer în dreapta; fără sidebar se centrează. Fundalul
                                curge în continuare la edge. */
  --layout-pad:     32px;    /* gutter exterior desktop */
  --layout-pad-mob: 20px;    /* gutter exterior mobile */
  --grid-cols:      12;      /* coloane pe desktop — cu sau fără sidebar */
  --grid-gutter:    24px;    /* gutter între coloane */
  --sidebar-w:      240px;   /* sidebar full — rail fix, ÎN AFARA grilei de 12 */
  --sidebar-w-icon: 64px;    /* sidebar icon-only — același rol, rail mai îngust */
  --flow-max:       560px;   /* lățimea coloanei .flow-chrome în banda mobile (tablete incluse) */

  /* ─── Scrim canonic — dim + blur în spatele ORICĂRUI overlay de tip modal ───
     Perechea unică pentru .modal-backdrop, .doc-sheet-backdrop, .flow-sheet-scrim
     (și orice overlay viitor de același tip). Tratament canonic edge-33, per
     reviewul de design: ink @ 25% + blur 6px. Nu redefini local (rgba/blur
     hardcodat) — orice consumator citește exclusiv acești doi tokeni. */
  --c-backdrop:  rgba(17, 13, 16, 0.25);    /* dim pe modal/sheet, ink @ 25% */
  --scrim-blur:  6px;                       /* blur pe modal/sheet — pereche cu --c-backdrop */

  /* ─── Document & overlay (Signer) ─── */
  --c-pdf-hl:    rgba(238, 67, 121, 0.10);  /* highlight în corpul PDF, pink @ 10% */

  /* ─── Tipografie · accent optic Fraunces ───
     Fraunces se randează optic mai mic decât Inter la aceeași dimensiune.
     Compensăm cu un increment FIX de +2px, identic pe toată scala — accentul
     e exact cu 2px mai mare decât heading-ul Inter în care apare, la orice
     dimensiune (T60 → T28) și pe orice breakpoint din clamp().
     Aplicat ÎN AMBELE blocuri `.t60 em, .t-display em, ...` ȘI `.t-em` prin
     `calc(1em + var(--t-em-bump))` — `1em` = font-size-ul heading-ului părinte,
     deci +2px absolut peste el. Cele două blocuri trebuie să rămână
     sincronizate, altfel `<em>` simplu și `.t-em` diverg. */
  --t-em-bump:   2px;

  /* ─── Chart — WIP ───
     Paletă categorică pentru dataviz. Se numesc deja în limbajul de cod
     (--chart-N, ca în shadcn și ca în aplicație), fiindcă s-au născut după
     decizia de naming — nu au un nume de design de tradus.

     Validată pe suprafața bej-0: bandă de luminozitate, prag de cromă,
     separare pentru daltonism (protan/deutan/tritan), prag pentru vederea
     normală și contrast ≥3:1 — toate trec.

     Constrângerea de reținut, și întrebarea deschisă a paginii: familia de
     STATUS a Bono (verde, chihlimbar, roșu, magenta, periwinkle) ocupă atât
     de mult din roata de culoare încât cea mai bună separare posibilă între
     o serie și un status e ~ΔE 15. De aceea orice grafic poartă legendă ȘI
     etichete directe — culoarea singură nu poate purta identitatea aici. */
  --chart-1: #EE4379;   /* brand pink */
  --chart-2: #0A8FA0;   /* teal */
  --chart-3: #5348D6;   /* indigo */
  --chart-4: #91922F;   /* olive */
  --chart-5: #BB61CF;   /* orchid */

  /* ═══ STRATUL DE ROLURI — GENERAT, nu edita aici ═══
     Numele de rol (convenția shadcn) pe lângă numele de design. Nu înlocuiesc
     nimic: aliasează. Numele de design rămâne cheia — și tot ce există azi
     continuă să funcționeze — dar cod nou poate scrie --muted în loc de
     --c-bej-1, deci design-ul și producția vorbesc același limbaj.

     Sursa: role_name din tokens.semantics.json.
     Generator + gardă: scripts/build-naming-dictionary.py
     Dicționarul citibil: components/foundation-naming.html */
  /* DS:roles:start */
  /* Suprafețe și text */
  --background:       var(--c-bej-0);
  --muted-subtle:     var(--c-bej-05);
  --muted:            var(--c-bej-1);
  --muted-foreground: var(--c-fog);
  --foreground:       var(--c-ink);
  --placeholder:      var(--c-mist);
  --card:             var(--c-white);
  /* Brand */
  --primary:       var(--c-accent);
  --primary-hover: var(--c-accent-deep);
  --accent:        var(--c-accent-tint);
  --accent-subtle: var(--c-accent-ultra);
  /* Linii și borduri */
  --input:            var(--c-input-border);
  --input-hover:      var(--c-input-border-hover);
  --input-tonal:      var(--c-input-border-tonal);
  --separator:        var(--c-rule);
  --border:           var(--c-rule-card);
  --separator-hover:  var(--c-rule-hover);
  --separator-subtle: var(--c-rule-soft);
  /* Acțiune distructivă */
  --destructive:       var(--c-danger);
  --destructive-hover: var(--c-danger-hover);
  /* Familii de status */
  --ai:                        var(--c-ai);
  --ai-subtle:                 var(--c-ai-bg);
  --ai-gradient:               var(--c-ai-grad);
  --error:                     var(--c-error);
  --error-subtle:              var(--c-error-bg);
  --error-subtle-foreground:   var(--c-error-text);
  --read:                      var(--c-read);
  --read-subtle:               var(--c-read-bg);
  --read-subtle-foreground:    var(--c-read-text);
  --success:                   var(--c-success);
  --success-subtle:            var(--c-success-bg);
  --success-subtle-foreground: var(--c-success-text);
  --warning:                   var(--c-warn);
  --warning-subtle:            var(--c-warn-bg);
  --warning-subtle-foreground: var(--c-warn-text);
  /* Overlay */
  --overlay:      var(--c-backdrop);
  --overlay-blur: var(--scrim-blur);
  /* Altele */
  --layout-max-width: var(--layout-max);
  /* DS:roles:end */
}

/* ═══ Reset ═══ */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  background-color: var(--c-bej-0);
  color: var(--c-ink);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ═══ Selecția de text — neutră, peste tot ═══
   O singură declarație pentru tot documentul: banda --c-selection, textul --c-ink.
   Perechea se dă ÎNTREAGĂ. Dacă declari doar fundalul, textul rămâne al gazdei, iar
   `.t13`/`.field-help` pe --c-fog ar sta la 3.16:1 pe bandă — sub AA fix în momentul
   în care utilizatorul se uită cel mai atent la el. Cu --c-ink forțat: 13.35:1.

   De ce neutru și nu brand: selecția nu e o stare a interfeței, e a utilizatorului —
   spune „asta am apucat eu", nu „asta e important". Restul sistemului folosește roz
   pentru ce ALEGE aplicația (rândul ales dintr-un meniu, .check-item.card selectat);
   cmd+A într-un câmp de căutare nu alege nimic, doar apucă text ca să-l înlocuiască.
   Gazda cade altfel pe `selection:bg-primary` din shadcn — roz plin cu text alb pe
   un input focusat, adică exact citirea unei stări de aplicație.

   `input`/`textarea` sunt enumerate separat: în Chromium regula moștenită de pe
   `::selection` nu ajunge întotdeauna în shadow DOM-ul controlului, iar câmpul e
   tocmai locul unde se vedea defectul. Fără -moz- nu se poate — Firefox nu suportă
   încă ::selection nedublat pe toate versiunile din suport. */
::selection { background: var(--c-selection); color: var(--c-ink); }
::-moz-selection { background: var(--c-selection); color: var(--c-ink); }
input::selection, textarea::selection { background: var(--c-selection); color: var(--c-ink); }
input::-moz-selection, textarea::-moz-selection { background: var(--c-selection); color: var(--c-ink); }
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
em { font-style: italic; }
img, svg { display: block; max-width: 100%; }

/* ═══ Focus & Accessibility ═══ */

/* 1. Reset global — scoate outline-ul urât de browser, dar DOAR la click (nu tastatură) */
*:focus { outline: none; }

/* 2. Outline vizibil la navigare cu tastatura (`:focus-visible` e filtrat de browser automat) */
*:focus-visible {
  outline: 2px solid var(--c-fog);
  outline-offset: 3px;
  border-radius: var(--r-sm); /* urmează forma aproximativă a elementului */
}

/* 3. Override per componentă — fiecare are border-radius și context diferit */

/* Butoane pill/lg — outline urmează raza lor */
.btn:focus-visible { border-radius: var(--r-full); outline-offset: 3px; }
.btn.lg:focus-visible { border-radius: var(--r-full); }

/* Input și Select — înlocuit cu box-shadow (fog, nu accent) */
/* :focus-visible moștenit din *:focus-visible dar outline-ul e anulat de box-shadow */
.input:focus-visible,
.select:focus-visible { outline: none; /* box-shadow fog deja aplicat pe :focus */ }

/* Selectul fără bordură din antetul calendarului — n-are bordură care să poarte
   halo-ul, deci rămâne pe outline, cu offset mic. */
.calendar-select:focus-visible { outline: 2px solid var(--c-fog); outline-offset: 1px; border-radius: var(--r-md); }

/* Switch — element mic, spațiu suplimentar */
.switch:focus-visible { outline-offset: 4px; border-radius: var(--r-full); }

/* Tabs button — context dens */
.tabs button:focus-visible { outline-offset: 2px; border-radius: var(--r-full); }
/* Toggle — spre deosebire de vecinii de mai sus, NU-și declară aici raza: forma
   lui se schimbă cu starea (cerc în repaus, --r-sm/--r-md selectat), iar un
   border-radius scris aici ar bloca morph-ul. Outline-ul urmează forma curentă. */
.toggle:focus-visible { outline-offset: 3px; }

/* Închiderea modalului — .btn.outline.sm.icon-only */
.modal > .btn.icon-only:focus-visible { outline-offset: 2px; border-radius: var(--r-full); }

/* Vox FAB */
.vox-fab:focus-visible { outline-offset: 3px; border-radius: var(--r-lg); }

/* 4. Dark surface variant — outline roz dispare pe fonduri închise */
.sec-dark *:focus-visible,
.card.dark *:focus-visible,
.kpi.dark *:focus-visible,
.vox-fab:focus-visible { outline-color: #fff; }

/* 5. Reduced motion */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ─── Dot grid ─── */
.has-grid { position: relative; isolation: isolate; }
.has-grid::before {
  content: ''; position: absolute; inset: 0;
  pointer-events: none; z-index: 0;
  background-image: radial-gradient(
    circle,
    var(--grid-color) var(--grid-radius),
    transparent var(--grid-edge)
  );
  background-size: var(--grid-size) var(--grid-size);
}
.has-grid > * { position: relative; z-index: 1; }

/* ─── Section utilities ─── */
/* Toate transparente — grid-ul vine de pe body.has-grid */
.sec-page,
.sec-app,
.sec-mkt,
.sec-mkt-grid { background-color: transparent; }
.sec-tonal    { background-color: var(--c-bej-1); }
.sec-dark     { background: var(--c-dark-grad); color: #fff; }

/* Sec-dark text mapping (sec. 6.3) — heading alb, em pink, eyebrow/body muted alb */
.sec-dark .t-h1,
.sec-dark .t-h2,
.sec-dark .t-h3,
.sec-dark .t60,
.sec-dark .t48,
.sec-dark .t40,
.sec-dark .t32,
.sec-dark .t28        { color: #fff; }
.sec-dark .t-h1 em,
.sec-dark .t60 em,
.sec-dark .t48 em,
.sec-dark .t40 em     { color: var(--c-pink); }
.sec-dark .t-eyebrow,
.sec-dark .t12        { color: rgba(255,255,255,0.50); }
.sec-dark .t-lead,
.sec-dark .t-body,
.sec-dark .t24,
.sec-dark .t16,
.sec-dark .t14,
.sec-dark p           { color: rgba(255,255,255,0.65); }

/* ─── Card primitives ─── */
/* White pe bej-0 — cu border hairline */
.card-hairline {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-md);
}
/* Bej-1 pe bej-0 — fără border */
.card-tonal {
  background: var(--c-bej-1);
  border: none;
  border-radius: var(--r-md);
}

/* ═══════════════════════════════════════════════════════════════
   TIPOGRAFIE
   Două clase pe același stil: numerică (t60, t48...) și semantică
   (t-display, t-h1...). Ambele funcționează — folosește ce e mai clar
   în context. Semantic e recomandat pentru cod nou.

   Podeaua scalei (2026-09-16, odată cu primitivele mobile):
     14  baseline — tot ce trebuie citit
     12  proză necrucială — eyebrow, etichete statice (.t12)
     11  DOAR etichete uppercase și numerale tabulare (.t11 / .t-label);
         .badge, .kpi .label, .popover-label, .calendar-dow stau pe
         aceeași treaptă
     sub 11 nu coboară nimic din UI — singura excepție e .pdf-*
         (miniatura unui document, nu interfață)
   ═══════════════════════════════════════════════════════════════ */

/* ── Display / Hero ── */
.t60, .t-display {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(36px, 5.5vw, 60px);
  letter-spacing: -0.04em; line-height: 1.10;
  color: var(--c-ink); margin: 0;
}
.t60.tight, .t-display.tight { line-height: 1.05; }

/* ── H1 ── */
.t48, .t-h1 {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(32px, 4.5vw, 48px);
  letter-spacing: -0.03em; line-height: 1.08;
  color: var(--c-ink); margin: 0;
}

/* ── H2 ── */
.t40, .t-h2 {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(28px, 3.8vw, 40px);
  letter-spacing: -0.03em; line-height: 1.15;
  color: var(--c-ink); margin: 0;
}

/* ── H3 ── */
.t32, .t-h3 {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(24px, 3.2vw, 32px);
  letter-spacing: -0.02em; line-height: 1.19;
  color: var(--c-ink); margin: 0;
}

/* ── H4 ──
   .modal-title E .t28 — titlul modalului stă în regula treptei, ca „28px" să fie
   declarat într-un singur loc. Slotul își adaugă doar spațierea (secțiunea Modal). */
.t28, .modal-title {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(22px, 2.8vw, 28px);
  letter-spacing: -0.02em; line-height: 1.21;
  color: var(--c-ink); margin: 0;
}

/* ── Fraunces em — accent italic în heading-uri ──
   Compensare optică Fraunces vs Inter: +2px fix via --t-em-bump, la orice mărime.
   ATENȚIE: blocul ăsta și .t-em trebuie să rămână sincronizate. */
.t60 em, .t-display em,
.t48 em, .t-h1 em,
.t40 em, .t-h2 em,
.t32 em, .t-h3 em,
.t28 em, .modal-title em {
  font-family: var(--font-display);
  font-style: italic; font-weight: 700;
  font-size: calc(1em + var(--t-em-bump));
  color: var(--c-pink); letter-spacing: inherit;
}

/* ── Lead ── */
.t24, .t-lead {
  font-family: var(--font-sans); font-weight: 400;
  font-size: 24px; line-height: 30px; letter-spacing: -0.01em;
  color: var(--c-ink); margin: 0;
}
.t24 strong, .t-lead strong { color: var(--c-ink); font-weight: 600; }

/* ── Editorial body — Source Serif 4, exclusiv blog ── */
.t20, .t-edit {
  font-family: var(--font-serif);
  font-size: 20px; font-weight: 400; line-height: 32px; letter-spacing: 0;
  color: var(--c-ink); margin: 0;
}
.t20 strong, .t-edit strong { color: var(--c-ink); font-weight: 600; }

/* ── Subhead ── */
.t18 {
  font-family: var(--font-sans); font-weight: 600;
  font-size: 18px; line-height: 24px; letter-spacing: -0.01em;
  color: var(--c-ink); margin: 0;
}

/* ── Body UI ── */
.t16, .t-body {
  font-family: var(--font-sans); font-weight: 400;
  font-size: 16px; line-height: 24px; letter-spacing: 0;
  color: var(--c-ink); margin: 0;
}
.t16 strong, .t-body strong { color: var(--c-ink); font-weight: 600; }

/* ── UI label ──
   .modal-body E .t14 — descrierea modalului, în aceeași regulă (vezi .modal-title la H4). */
.t14, .t-meta, .modal-body {
  font-family: var(--font-sans); font-weight: 500;
  font-size: 14px; line-height: 20px; letter-spacing: 0;
  color: var(--c-fog); margin: 0;
}

/* ── Control label & description ── */
/* Folosite pentru etichetele controalelor omoloage: checkbox, radio, switch. */
.t15, .t-ctrl {
  font-family: var(--font-sans); font-weight: 400;
  font-size: 15px; line-height: 22px; letter-spacing: 0;
  color: var(--c-ink); margin: 0;
}
.t13, .t-ctrl-desc {
  font-family: var(--font-sans); font-weight: 400;
  font-size: 13px; line-height: 18px; letter-spacing: 0;
  color: var(--c-fog); margin: 0;
}

/* ── Eyebrow / caption ── */
.t12, .t-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-sans);
  font-size: 12px; font-weight: 600;
  line-height: 16px; letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--c-fog); margin: 0;
}
.t12 .dot, .t-eyebrow .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--c-pink); }
.t12.brand, .t-eyebrow.brand { color: var(--c-pink-deep); }

/* ── Label 11 — podeaua scalei ──
   11px există DOAR pentru etichete uppercase și numerale tabulare: capul unei
   coloane într-un card de date, eticheta unui KPI, eticheta de grup dintr-un
   meniu, zilele săptămânii în calendar. Niciodată proză — proza coboară până la
   12 (.t12) și se citește de la 14 (.t14). Sub 11 nu există nimic în UI; singura
   excepție e .pdf-* (miniatura unui document, nu interfață).
   .kpi .label, .popover-label și .calendar-dow sunt grupate aici ca să existe un
   singur loc care spune „11px"; ce le diferențiază (tracking, greutate, așezare)
   rămâne în regulile lor, mai jos în fișier. .t-badge și .avatar.sm au urcat de la
   10 la 11 în același pas (2026-09-16). */
.t11, .t-label,
.kpi .label, .popover-label, .calendar-dow,
.command [cmdk-group-heading] {
  font-family: var(--font-sans);
  font-size: 11px; font-weight: 500; line-height: 16px;
  letter-spacing: 0.04em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--c-fog);
}
.t11, .t-label { margin: 0; }

/* ── Mono ── */
.t-mono {
  font-family: var(--font-mono);
  font-size: 12px; color: var(--c-ink);
}

/* ── Cifre financiare ── */
.tn40 {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(28px, 3.5vw, 40px); line-height: 1;
  letter-spacing: -0.04em; font-variant-numeric: tabular-nums;
  color: var(--c-ink);
}
.tn28 {
  font-family: var(--font-sans); font-weight: 700;
  font-size: clamp(22px, 2.5vw, 28px); line-height: 1;
  letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
  color: var(--c-ink);
}
/* Cifra pe suprafață închisă ────────────────────────────────────────────
   .tn40 / .tn28 își declară culoarea EXPLICIT (--c-ink), deci bat
   `color: #fff` moștenit de la suprafață — spre deosebire de .t-body sau
   .t16, care doar moștenesc. Fără rândul ăsta cifra rămâne neagră pe
   negru: măsurat 1.07:1 pe stopul de sus al lui --c-dark-grad (#2F081A)
   și 1.04:1 pe cel de jos (#09041D), sub orice prag WCAG. Cu #fff:
   17.94 / 20.06.
   Lista e cea sancționată de suprafețe închise (docs/foundation.md);
   .tabs.dark și .avatar.dark nu găzduiesc cifre, deci nu apar aici —
   o suprafață închisă NOUĂ se adaugă în același timp în ambele locuri.
   Sufixele nu au nevoie de nimic: --c-mist dă 7.35 / 8.22 pe aceleași
   două stopuri. Măsurat cu scripts/check-contrast.mjs. */
.sec-dark .tn40,        .sec-dark .tn28,
.card.dark .tn40,       .card.dark .tn28,
.kpi.dark .tn40,        .kpi.dark .tn28,
.page-banner.dark .tn40, .page-banner.dark .tn28 { color: #fff; }

.t-suffix-lg { font-family: var(--font-sans); font-size: 13px; font-weight: 400; line-height: 1; color: var(--c-mist); }
.t-suffix-md { font-family: var(--font-sans); font-size: 14px; font-weight: 400; line-height: 1; color: var(--c-mist); }
.t-num-inline { font-family: var(--font-sans); font-size: 14px; font-weight: 500; line-height: 1; font-variant-numeric: tabular-nums; }

/* ── Speciale ── */
.t-badge {
  font-family: var(--font-sans);
  font-size: 11px; font-weight: 600;   /* 10 → 11 pe 2026-09-16: podeaua scalei e 11 */
  line-height: 1; letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* Mărimea e relativă la rândul gazdă, nu fixă: +1px peste Inter-ul din jur,
   compensarea optică a lui Fraunces italic (aceeași idee ca --t-em-bump, mai mică
   fiindcă .t-actor stă în text de corp, nu în heading). Istoric: 13px fix, apoi
   15px fix pe 2026-09-22 — și 15 se potrivea doar în rânduri de 15px: în ticker
   (12px) și în .meta (11px) ieșea cu 3–4px prea mare, în text de 16–17px prea
   mic. line-height: inherit — cu 1, rândul cu actor avea alt ritm decât restul. */
.t-actor {
  font-family: var(--font-display); font-style: italic;
  font-weight: 500; font-size: calc(1em + 1px);
  line-height: inherit; letter-spacing: -0.01em;
  color: var(--c-pink);
}

/* ── T-em — accent Fraunces în heading-uri ──────────────────────
   EXCLUSIV pentru cuvinte cu încărcătură emoțională / temporală /
   acțiune pe heading-uri din scala T60–T28. Niciodată inline cu
   `font-family: 'Fraunces'` — folosește `<em class="t-em">cuvânt</em>`.
   Letter-spacing inherit de la heading.
   Compensare optică Fraunces vs Inter: font-size = calc(1em + 2px)
   prin --t-em-bump — exact +2px peste heading, la orice mărime. Trebuie
   să rămână sincronizat cu blocul `.t60 em, .t-display em, ...` de mai sus. */
.t-em {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 700;
  font-size: calc(1em + var(--t-em-bump));
  color: var(--c-pink);
  letter-spacing: inherit;
}

/* ── Responsive ── breakpoint unic: banda mobile e tot ce e sub 1024px ── */
@media (max-width: 1023.98px) {
  .t24, .t-lead { font-size: 20px; line-height: 26px; }
  .t20, .t-edit { font-size: 18px; line-height: 30px; }
  .t18           { font-size: 16px; line-height: 22px; }
}

/* ═══ Icons — .ic ═══
   UN singur set de glifuri: Lucide (lucide.dev, ISC), grila 24×24. Sursa fiecărui
   glif e assets/icons/<nume>.svg — copiat verbatim, niciodată redesenat de mână.
   Înainte de 2026-09-10 sistemul avea 111 desene diferite, 6 grile (12/14/16/20/
   24/32) și 9 grosimi de linie (1.4→3), iar același „warning" exista în 4
   variante. Regula e simplă: markup-ul poartă glifa Lucide așa cum e, iar CSS-ul
   de AICI decide mărimea și grosimea — un singur loc, deci nu poate deriva.

   Grosimea e în PIXELI DE ECRAN (vector-effect: non-scaling-stroke), nu în
   unități de grilă, și urcă OPTIC cu mărimea, pe trepte:

     mărime   12     14     16     18     20     24
     linie    1.25   1.5    1.5    1.75   1.75   2      (px pe ecran)
     pondere  10%    11%    9%     10%    9%     8%

   Ancora e 16px la 1.5px — playground-ul Lucide la „Size 16 · Stroke 1.5 ·
   Absolute stroke width". La 24 linia ajunge la 2px, exact glifa Lucide așa
   cum e desenată. Ca la literă: un corp mic primește proporțional mai multă
   greutate ca să rămână citibil, unul mare mai puțină ca să nu pară gros.
   Alternativele au picat la audit (2026-09-10): 1.5px absolut la orice mărime
   dădea 12% din icon la 12px și 6% la 24px — mic și îndesat, mare și palid;
   proporțional pur (2 pe 24) dădea 1.0px la 12 — subțire și tulbure pe 1x.

   Scara de mărimi: .ic.xs 12 · .ic.sm 14 · .ic 16 · .ic.md 18 · .ic.lg 20 ·
   .ic.xl 24. Contextele care își știu mărimea (.btn, .toggle) o impun singure
   prin regula lor — în buton iconul poartă DOAR .ic, treapta butonului îi dă
   mărimea (vezi secțiunea Buttons). */
.ic {
  flex-shrink: 0;
  width: 16px; height: 16px;
  stroke-width: 1.5;
}
.ic > * { vector-effect: non-scaling-stroke; }
.ic.xs { width: 12px; height: 12px; stroke-width: 1.25; }
.ic.sm { width: 14px; height: 14px; stroke-width: 1.5; }
.ic.md { width: 18px; height: 18px; stroke-width: 1.75; }
.ic.lg { width: 20px; height: 20px; stroke-width: 1.75; }
.ic.xl { width: 24px; height: 24px; stroke-width: 2; }
/* Iconul din butoane are linia în pixeli de ecran chiar și fără clasa .ic —
   butonul îi dă mărimea ȘI grosimea, pe treaptă (secțiunea Buttons). */
.btn > svg > * { vector-effect: non-scaling-stroke; }

/* ═══ Buttons ═══ */
.btn {
  height: 40px; padding: 0 24px;
  /* Pill, explicit. La 40px înălțime, --r-lg (20px) DĂ deja capete complet
     rotunde — decizia din 2026-08-26 nu schimbă niciun pixel, schimbă ce
     declară regula. --r-lg ar înceta să fie pilulă în ziua în care butonul
     crește; --r-full nu poate. */
  border-radius: var(--r-full);
  font-family: var(--font-sans);
  font-size: 14px; line-height: 20px; font-weight: 600;
  letter-spacing: -0.005em;
  border: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  transition: all 140ms;
  white-space: nowrap;
}
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ─── Stări: rest → hover → active ───
   Butonul e o suprafață pe care o ridici și o apeși.

   Rest e plat: fără umbră, fără offset. Hover o ridică — translateY(-1px) plus
   o umbră dedesubt: glow roz (--sh-pink) pe primary, un halou neutru discret
   pe secondary. Variantele plate (ghost, outline, danger.outline) nu se ridică;
   ele semnalează prin suprafață, nu prin altitudine.

   Active e inversul hoverului, nu un strat peste el: transformul revine la 0,
   umbra dispare (un obiect apăsat nu aruncă umbră) și suprafața se închide o
   treaptă. De asta secvența rest → hover → press → release citește ca o
   singură mișcare continuă, animată simetric de `transition: all 140ms` de mai
   sus. Fiecare :active poartă :not(:disabled), la fel ca hoverurile. */
.btn.primary { background: var(--c-accent); color: #fff; }
@media (hover: hover) {
  .btn.primary:hover:not(:disabled) {
    background: var(--c-accent-deep);
    transform: translateY(-1px);
    box-shadow: var(--sh-pink);
  }
}
.btn.primary:active:not(:disabled) {
  background: var(--c-accent-deep);
  transform: translateY(0);
  box-shadow: none;
  filter: brightness(0.94);
}
/* Secondary urmează modelul fizic al primarului (2026-09-03): până atunci avea
   doar `filter: brightness(0.97)` — se închidea, dar nu se ridica, deci era
   singura suprafață plină din DS fără altitudine la hover. Umbra e --sh-pink cu
   ink la 8% în loc de roz la 30%: aceeași geometrie, halou neutru. Rămâne
   literală în regulă, nu token nou — o singură folosire, iar setul de
   suprafețe/umbre e blocat.

   Fill-ul de hover e --c-hover-veil (2026-09-10), același văl ca la
   .btn.outline, în locul vechiului --c-bej-1-hover opac. Dubla oprire de
   linear-gradient NU e un gradient: e singurul fel în care compozitezi un strat
   translucid peste fill-ul opac al butonului într-un singur `background`. Ambele
   opriri sunt aceeași valoare, deci pictează plat. NU o „simplifica" la
   `background: var(--c-hover-veil)` — ai pierde bej-1-ul de dedesubt și
   secondary ar deveni transparent la hover. */
.btn.secondary { background: var(--c-bej-1); color: var(--c-ink); }
@media (hover: hover) {
  .btn.secondary:hover:not(:disabled) { background: linear-gradient(var(--c-hover-veil), var(--c-hover-veil)), var(--c-bej-1); transform: translateY(-1px); box-shadow: 0 6px 16px rgba(17,13,16,0.08); }
}
.btn.secondary:active:not(:disabled) { transform: translateY(0); box-shadow: none; filter: brightness(0.96); }
.btn.ghost { background: transparent; color: var(--c-fog); }
@media (hover: hover) {
  .btn.ghost:hover:not(:disabled) { color: var(--c-ink); background: var(--c-bej-1); }
}
.btn.ghost:active:not(:disabled) { color: var(--c-ink); background: var(--c-bej-1); filter: brightness(0.96); }
/* .outline nu are fundal — asta ÎNSEAMNĂ numele. E un contur peste suprafața
   pe care stă: pe alb citește alb, pe bej-1 citește bej-1, iar conturul face
   forma. Până pe 2026-09-03 avea `background: var(--c-white)`, adică un
   outline care își aducea propria suprafață — motiv pentru care pe modal (card
   bej-1) se vedea ca o plombă albă, iar butonul „cu contur" era de fapt un
   buton plin cu chenar.

   Hoverul a fost --c-bej-1 (2026-09-03), aliniat la .btn.ghost:hover. Problema:
   un fill opac de bej-1 dispare exact pe gazda bej-1 (cardul de modal, action
   cards) — butonul „cu contur" nu avea hover tocmai acolo unde apare cel mai
   des. Din 2026-09-10 hoverul e --c-hover-veil, vălul translucid împărțit cu
   .btn.secondary: nu numește o suprafață, se așază peste ce e dedesubt și
   închide gazda cu o treaptă, oricare ar fi ea. Peste alb iese ~#ECE9E1 (la un
   fir de bej-1-ul pe care outline îl hardcoda), peste bej-0 ~#EAE6DE, peste
   bej-1 ~#E5DFD5 — deci pe suprafețele obișnuite arată ca înainte, iar pe bej-1
   se vede. Conturul urcă și el o treaptă (--c-rule-hover), ca al doilea semnal,
   pe cazul în care fundalul gazdei e o imagine sau un gradient unde vălul se
   pierde. */
.btn.outline { background: transparent; color: var(--c-ink); box-shadow: inset 0 0 0 1px var(--c-rule); }
@media (hover: hover) {
  .btn.outline:hover:not(:disabled)  { background: var(--c-hover-veil); box-shadow: inset 0 0 0 1px var(--c-rule-hover); }
}
/* La press conturul rămâne pe --c-rule-hover și suprafața se închide o treaptă —
   outline nu are altitudine de anulat, deci apăsarea se citește pe contur și pe
   suprafață, ca la restul variantelor plate. */
.btn.outline:active:not(:disabled) { background: var(--c-hover-veil); box-shadow: inset 0 0 0 1px var(--c-rule-hover); filter: brightness(0.96); }

/* .outline-pink NU trece pe transparent: e singurul buton cu contur care apare
   și pe suprafețe închise (.card.dark.cta, templates/card.html), unde fundalul
   alb e ce face textul ink lizibil. Transparent l-ar face ink pe întuneric.
   Vezi docs/components/button.md, „Fundalul variantelor cu contur". */
.btn.outline-pink { background: var(--c-white); color: var(--c-ink); box-shadow: inset 0 0 0 1px var(--c-pink); }
@media (hover: hover) {
  .btn.outline-pink:hover:not(:disabled) { background: var(--c-pink); color: #fff; }
}
.btn.outline-pink:active:not(:disabled) { background: var(--c-pink); color: #fff; filter: brightness(0.94); }
/* .outline pe .card.dark — ink pe transparent nu se citește pe gradient, deci pe
   cardul închis conturul trece pe alb: text alb, hairline alb @ 14%, iar la hover
   și press un văl alb @ 8% cu hairline-ul urcat la 28%. Pressul păstrează
   brightness(0.96) din regula de bază. Venit din Dublin (cardul „Taxe și impozite
   de plată" și acțiunea-icon a Action card-ului), unde stătea în theme.css
   (2026-10-07). Doar pe .card.dark: restul suprafețelor închise rămân pe
   .outline-pink, singurul contur cu fundal propriu. */
.card.dark .btn.outline { color: #fff; box-shadow: inset 0 0 0 1px rgba(255,255,255,0.14); }
.card.dark .btn.outline:active:not(:disabled) {
  background: rgba(255,255,255,0.08);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.28);
}
@media (hover: hover) {
  .card.dark .btn.outline:hover:not(:disabled) {
    background: rgba(255,255,255,0.08);
    box-shadow: inset 0 0 0 1px rgba(255,255,255,0.28);
  }
}
/* ─── Destructive / delete ───
   Destructive is BLACK (--c-danger), always with an icon. The black button is
   reserved for destructive actions ONLY — actions that remove or take something
   away (delete, revoke, terminate, close). `.danger` = primary deletion (black
   fill); `.danger.outline` = lower-impact destructive (solid ink border + ink
   text — vs the faint --c-rule border on .btn.outline) that FILLS BLACK on
   hover, so the warning arrives before the click, never a softer state.
   NOT for cancellations or reversible actions — those stay ghost/secondary/outline.
   NOT for irreversible-but-constructive actions (submit filing, send e-invoice,
   sign) — those stay .btn.primary + confirmation dialog. */
.btn.danger { background: var(--c-danger); color: #fff; }
@media (hover: hover) {
  .btn.danger:hover:not(:disabled) { background: var(--c-danger-hover); transform: translateY(-1px); }
}
/* Danger apasă înapoi pe negrul plin: hoverul îl deschide (--c-danger-hover),
   pressul îl întoarce la --c-danger. Nu mai are nevoie de brightness. */
.btn.danger:active:not(:disabled) { background: var(--c-danger); transform: translateY(0); }
.btn.danger.outline {
  background: var(--c-white); color: var(--c-ink);
  box-shadow: inset 0 0 0 1px var(--c-ink);
}
/* Hover FILLS the outline with the destructive black (2026-10-12) — the same
   model as .outline-pink, which fills pink. Until now hover was bej-1, and
   .btn.outline:hover (0,4,0) beat the base (0,3,0), swapping the ink ring for
   the --c-rule-hover hairline: lighter fill + fainter ring = the button read as
   DISABLED at the exact moment the user was about to delete something. Now the
   hover warns: it becomes the filled .danger button.

   The black pair is .btn.danger's, so destructive has ONE sequence: hover
   --c-danger-hover (a charcoal black), press back to --c-danger (solid black) —
   press reads one step darker than hover, like every other button. No
   brightness() on press: it cannot darken black, so the step lives in the two
   tokens instead. No lift — it stays a flat variant and
   signals through its surface. box-shadow: none is explicit because otherwise
   .btn.outline:hover/:active (0,4,0) would lay the grey hairline over the
   black; filter: none cancels the brightness inherited from .btn.outline:active
   (invisible on black, just one more state to reason about). */
@media (hover: hover) {
  .btn.danger.outline:hover:not(:disabled) {
    background: var(--c-danger-hover); color: #fff;
    box-shadow: none;
    transform: none;
  }
}
.btn.danger.outline:active:not(:disabled) {
  background: var(--c-danger); color: #fff;
  box-shadow: none;
  transform: none;
  filter: none;
}

/* Utility: buton full-width — pentru CTA în card, formular cu un singur câmp, modal submit */
.btn.full { width: 100%; justify-content: center; }

/* Acțiune text-only, în brand. Nu are chrome: nici înălțime de buton, nici
   fundal, nici radius — e un link care se comportă ca o acțiune.
   Contrast: --c-accent pe alb dă 3.67:1, sub AA la 14px. Decizie luată în
   cunoștință de cauză (2026-08-27): linkul de acțiune poartă brandul. Nu e o
   scăpare — dacă apare într-un audit, ăsta e răspunsul. */
.btn.link {
  height: auto; padding: 0; border-radius: 0;
  background: transparent; color: var(--c-accent);
  text-underline-offset: 4px;
}
@media (hover: hover) {
  .btn.link:hover:not(:disabled) { color: var(--c-accent-deep); text-decoration: underline; }
}
.btn.link:focus-visible { border-radius: var(--r-xs); outline-offset: 2px; }

.btn.lg { height: 44px; padding: 0 32px; font-size: 16px; line-height: 24px; border-radius: var(--r-full); }
.btn.sm { height: 30px; padding: 0 18px; font-size: 13px; line-height: 18px; border-radius: var(--r-full); }
.btn.xs { height: 26px; padding: 0 14px; font-size: 12px; line-height: 16px; border-radius: var(--r-full); }
/* Buton-icon — NU o mărime proprie, ci un modificator care compune cu scara
   existentă: .btn.icon-only = 40, .btn.sm.icon-only = 30, .btn.xs.icon-only = 26,
   .btn.lg.icon-only = 44. Înălțimea vine de la treapta de mărime, aspect-ratio o
   face pătrat, --r-full o face cerc. O scară paralelă de mărimi ar putea deriva
   de prima; asta nu poate. */
.btn.icon-only { padding: 0; aspect-ratio: 1; }

/* Iconul din buton — BUTONUL îi dă mărimea, ca pe font-size (2026-09-10).
   Două scări, o regulă pe treaptă, nimic în markup (iconul poartă doar .ic,
   fără .sm/.md/.lg — scripts/check-icons.py pică pe un modificator în buton):

     treaptă      buton   text     icon lângă text   icon-only   linie (px)
     .xs          26      12px     12                12          1.25
     .sm          30      13px     14                16          1.5
     (implicit)   40      14px     16                18          1.5 / 1.75
     .lg          44      16px     20                20          1.75

   Linia urmează mărimea iconului pe scara optică din secțiunea Icons
   (12→1.25 · 14/16→1.5 · 18/20→1.75 · 24→2), nu treapta butonului.

   Lângă text iconul urmărește litera (≈ înălțimea majusculei plus puțin);
   singur în cerc are nevoie de mai multă prezență, deci stă o treaptă mai sus
   și umple constant ~45-53% din buton. Înainte (2026-09-03) icon-only avea
   doar două valori (14 pe .xs/.sm, 18 pe implicit/.lg) — .xs citea aglomerat
   și .lg gol — iar butonul cu text nu-și dimensiona iconul deloc: rămânea la
   ce scria markup-ul, 16 și în 26 și în 44 de pixeli. Mărimea vine de AICI,
   nu din atributele width/height ale SVG-ului — un singur loc, deci nu poate
   deriva de la o componentă la alta. */
.btn > svg { width: 16px; height: 16px; stroke-width: 1.5; }
.btn.lg > svg { width: 20px; height: 20px; stroke-width: 1.75; }
.btn.sm > svg { width: 14px; height: 14px; stroke-width: 1.5; }
.btn.xs > svg { width: 12px; height: 12px; stroke-width: 1.25; }
.btn.icon-only > svg { width: 18px; height: 18px; stroke-width: 1.75; }
.btn.lg.icon-only > svg { width: 20px; height: 20px; stroke-width: 1.75; }
.btn.sm.icon-only > svg { width: 16px; height: 16px; stroke-width: 1.5; }
.btn.xs.icon-only > svg { width: 12px; height: 12px; stroke-width: 1.25; }

/* ── Ținta de atins pe mobil — 44px, invizibilă (2026-09-16) ──
   Sub 1024px orice control sub 44px primește un strat de lovire de 44×44,
   centrat, în propria cutie (nu împinge părintele): md 40 → +2 pe latură,
   sm 30 → +7, xs 26 → +9. .lg are 44 deja; .link e text, fără cutie. Același
   strat pe .toggle (aceeași scară 26/30/40/44). Specificitatea e mică
   dinadins: .modal > .btn.icon-only și celelalte poziționări absolute ale
   containerelor câștigă. */
@media (max-width: 1023.98px) {
  .btn:not(.link), .toggle { position: relative; }
  .btn:not(.lg):not(.link)::before, .toggle:not(.lg)::before {
    content: ""; position: absolute; inset: -2px; border-radius: inherit;
  }
  .btn.sm::before, .toggle.sm::before { inset: -7px; }
  .btn.xs::before, .toggle.xs::before { inset: -9px; }
  /* În grup itemul e mereu 30 (vezi .toggle-group), deci și stratul e al lui 30. */
  .toggle-group .toggle:is(.xs, .lg)::before {
    content: ""; position: absolute; inset: -2px; border-radius: inherit;
  }
}

/* ═══ Inputs ═══ */
.input {
  height: 40px; padding: 0 16px;
  background: var(--c-white);
  border: 1px solid var(--c-input-border);
  border-radius: var(--r-lg);
  font-family: var(--font-sans);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--c-ink); width: 100%;
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
}
.input::placeholder { color: var(--c-mist); }

/* textarea pe .field NEUTRU (label deasupra, „outboard"). Fără regula asta,
   textarea moștenea height:40px de la .input și se strângea la un rând. Perechea
   ei — textarea cu eticheta în bordură — e la carcasă (.input-group), mai jos. */
textarea.input {
  height: auto;
  min-height: 88px;              /* ~4 rânduri la line-height 20px */
  padding: 10px 16px;
  line-height: 20px;
  resize: vertical;              /* niciodată orizontal — rupe layout-ul */
  overflow-y: auto;
  scrollbar-width: thin;
}

/* Variantă de suprafață — stă ÎNAINTE de blocul de stări, nu după. .input.tonal are
   aceeași specificitate ca .input:focus / .error / .success / :disabled (0,2,0), deci între
   ele decide ordinea din fișier: de aici, fiecare stare câștigă natural și varianta n-are
   nevoie de lanț de :not() ca să se dea la o parte. Fundalul și conturul stau împreună,
   fiindcă sunt o singură decizie — pe bej-0 conturul duce singur limita câmpului. */
.input.tonal { background: var(--c-bej-0); border-color: var(--c-input-border-tonal); }
/* ─── Hover retreats by ORDER, not by enumeration (2026-09-03) ───
   Every hover rule in this file that paints a background or a border follows
   one shape:  <element>:hover:where(:not(<not-interactive>)).

   The :where() is the whole trick. Hover has to carry a guard — you do not
   highlight a control that cannot be used — but a plain :not() ADDS WEIGHT, and
   the extra weight made hover outrank the state rules below it. The old fix was
   to name every state back out: hover, but not selected, not error, not
   success, not focused. That list had to be extended by hand for every new
   state, and forgetting cost nothing at build time — the state simply vanished
   under the cursor. :where() contributes ZERO weight, so hover stops
   out-shouting the states and the ones declared AFTER it win on their own.

   Two consequences to respect when editing:
   - A state that owns a background or border must be declared BELOW its hover
     rule. That is why .select's hover moved above .select:focus.
   - Only a not-interactive guard belongs inside the :where() (:disabled,
     :focus-within, an open menu). A visual state does NOT go in there — it wins
     by being later, and adding it back re-creates the list. */
@media (hover: hover) {
  .input:hover:where(:not(:disabled)) { border-color: var(--c-input-border-hover); }
}
.input:focus, .input.is-focused {
  outline: none;
  /* Refinement v6.0: focus halo neutru (fog) în loc de pink — pattern consistent
     pe toate controalele form (input, select, cur-select). Spec sec. 7.2 pomenește
     pink, dar implementarea folosește fog pentru subtilitate și a evita prea mult
     accent în formulare cu multe câmpuri. */
  border-color: var(--c-fog);
  background: var(--c-white);
  box-shadow: 0 0 0 3px rgba(122,117,112,0.14);
}
.input.lg { height: 44px; padding: 0 20px; font-size: 16px; }
.input.error { border-color: var(--c-error); background: var(--c-error-bg); }
/* Validare pe blur: pe focus-in starea de eroare se ridică — utilizatorul editează pe câmp curat */
.input.error:focus, .input.error.is-focused { border-color: var(--c-fog); background: var(--c-white); }
.input.success { border-color: var(--c-success); }
/* :disabled își declară borderul explicit, ca orice altă stare (hover, focus, .error, .success):
   altfel un câmp dezactivat ar moșteni conturul variantei — .tonal l-ar duce dezactivat la 0.46,
   iar două câmpuri dezactivate din același formular ar avea greutăți diferite. */
.input:disabled { background: var(--c-bej-1); border-color: var(--c-input-border); color: var(--c-mist); cursor: not-allowed; }

/* ═══ Select ═══ */
.select {
  height: 40px; padding: 0 36px 0 16px;
  background: var(--c-white);
  border: 1px solid var(--c-input-border);
  border-radius: var(--r-lg);
  font-family: var(--font-sans);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--c-ink); width: 100%;
  cursor: pointer;
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
  /* ascunde săgeata nativă */
  appearance: none; -webkit-appearance: none;
  /* săgeată custom SVG inline */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6L8 10L12 6' stroke='%237A7570' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
/* Aceeași regulă de poziție ca la .input.tonal: varianta stă înaintea stărilor. */
.select.tonal { background-color: var(--c-bej-0); border-color: var(--c-input-border-tonal); }
/* Moved above .select:focus on 2026-09-03. It used to sit below it and outrank
   it by weight; now it must come first so focus, error, success and disabled
   all win by order — matching .input, where hover was already first. */
@media (hover: hover) {
  .select:hover:where(:not(:disabled)) { border-color: var(--c-input-border-hover); }
}

.select:focus,
.select.is-focused {
  outline: none;
  border-color: var(--c-fog);
  background-color: var(--c-white);
  box-shadow: 0 0 0 3px rgba(122,117,112,0.14);
}
.select.lg { height: 44px; padding: 0 40px 0 20px; font-size: 16px; }

/* ── Mobil: zoom-ul iOS Safari ── Sub 16px Safari mărește pagina la focus.
   .input.lg / .select.lg (16px) sunt OBLIGATORII sub 1024px — input.json,
   select.json → sizes.required_in: .app-mobile, .flow-chrome, .sheet.form;
   plasa de siguranță de aici prinde ce scapă auditului. */
@media (max-width: 1023.98px) {
  .input:not(.lg), .select:not(.lg) { font-size: 16px; }
}
.select.error { border-color: var(--c-error); background-color: var(--c-error-bg); }
.select.error:focus, .select.error.is-focused { border-color: var(--c-fog); background-color: var(--c-white); }
.select.success { border-color: var(--c-success); }
.select:disabled { background-color: var(--c-bej-1); border-color: var(--c-input-border); color: var(--c-mist); cursor: not-allowed; }

/* ═══════════════════════════════════════════════════════════════
   SELECT TRIGGER — a doua formă a aceleiași alegeri

   `.select` e `<select>` nativ: lista o desenează sistemul de operare. Asta e
   o calitate (tastatură, roata de pe mobil, un singur tab stop) și de aia
   rămâne implicit — dar înseamnă și că lista nu poate purta nimic în afară de
   text: fără drapel, fără rând secundar, fără grile de calendar, fără grupuri.

   Când lista trebuie să fie a DS-ului, controlul devine un `<button>` care
   deschide `.popover`. Ăsta e `.select-trigger`. Aceeași alegere dintr-o listă
   închisă, alt mecanism de deschidere. Regula de alegere între cele două forme
   stă în docs/components/select.md — implicit e nativul, trigger-ul cere un
   motiv.

   Trei consumatori azi, și de aia primitiva există: butonul de dată care
   deschide `.calendar` (până acum un `.input` readonly într-un div cu
   `position:relative` scris inline), bara de filtre, și selectorul de monedă
   din `.amount-row`.

   NU e `.btn`. Un buton propune o ACȚIUNE și își poartă intenția în variantă
   (primary / outline / ghost); ăsta poartă o VALOARE și trebuie citit ca un
   câmp. De asta împrumută înălțimea, chenarul, radiusul și halo-ul neutru ale
   lui `.select` și nu are nicio treaptă comună cu `.btn`.

   `.cur-select` / `.cur-select-trigger` (selectorul de monedă, v6.1) sunt de
   azi ALIAS-uri, nu o a doua implementare: fiecare regulă de mai jos le
   numește în același `:is()` cu numele generic, deci nu există două corpuri de
   declarații care pot deriva. Markupul canonic e cel nou; numele vechi rămân
   valide și se scot cel mai devreme la N+2 (VERSIONING.md).
   ═══════════════════════════════════════════════════════════════ */

/* Ancora — singurul ei rol e contextul de poziționare al popoverului. Există ca
   clasă fiindcă alternativa e `style="position:relative"` scris în pagină, iar
   aia nu se poate audita. */
/* --select-w: lățimea trigger-ului, ca să nu se strângă și să nu se lărgească
   după valoarea aleasă. Un control care sare cu 40px când alegi „Anul curent" în
   loc de „Trimestrul curent" mută tot ce e pe rândul lui — iar într-un toolbar
   asta înseamnă butoane care fug de sub cursor.

   În FORMULAR nu e nevoie de el: `.select-anchor.block` ia lățimea câmpului, deci
   valoarea n-o mai poate schimba. În `.filter-bar` nici atât: bara dă fiecărui
   filtru o parte egală din rând, oricare i-ar fi valoarea (Filter bar, mai jos).
   INLINE (antet, bară de unelte) lățimea e a apelantului, fiindcă doar
   el știe care e cea mai lungă opțiune din listă — CSS-ul nu poate măsura un
   popover poziționat absolut, iar el nu contribuie la lățimea ancorei tocmai ca
   să poată pluti. Deci: `style="--select-w: 200px"`, o dată, pe ancoră.

   Ce NU se face: lăsat pe `auto` și sperat. Ce se întâmplă când valoarea nu
   încape: `.select-value` se taie cu elipsă (regula de mai jos), nu împinge
   săgeata afară din buton. */
:is(.select-anchor, .cur-select) {
  position: relative; display: inline-flex; flex-shrink: 0;
  width: var(--select-w, auto);
}
/* Într-un `.field` trigger-ul ține lățimea, exact ca `.select`. Inline (filtru,
   antet, rând de sumă) rămâne strâns pe conținut. */
.select-anchor.block { display: flex; width: 100%; }
.select-anchor.block > .select-trigger { width: 100%; }
/* Trigger-ul umple ancora ORICÂND aceasta are o lățime — `.block` (100%) sau
   `--select-w` (fix). Fără regula asta, ancora se lărgește și butonul rămâne la
   lățimea textului: lățimea pare setată, dar controlul tot sare de la o valoare
   la alta, fiindcă cea care s-a fixat e cutia goală din jurul lui.
   `flex: 1 1 auto` nu strică ancora fără lățime: acolo `width: auto` o strânge
   pe conținut, deci trigger-ul își păstrează lățimea lui naturală.
   `min-width: 0` e ce lasă trigger-ul să se strângă SUB textul lui: fără el, un
   item flex nu coboară sub lățimea conținutului, deci o valoare mai lungă decât
   ancora ieșea din ea, peste filtrul vecin, iar elipsa de pe `.select-value` nu
   apuca să taie nimic. */
:is(.select-anchor, .cur-select) > :is(.select-trigger, .cur-select-trigger) { flex: 1 1 auto; min-width: 0; }
/* Aceeași lățime, fără carcasă. Forma React (react/select.tsx) nu are
   `.select-anchor`: Radix portalează lista în <body> și o poziționează singur,
   deci nu mai există un părinte relativ de care să ancoreze. Trigger-ul rămâne
   atunci singur în `.field`, și are nevoie de propriul modificator de lățime. */
.select-trigger.block { width: 100%; }

:is(.select-trigger, .cur-select-trigger) {
  display: inline-flex; align-items: center; gap: 8px;
  height: 40px; padding: 0 14px;
  /* `start`, declarat. Trigger-ul e un <button>, iar `text-align: center` e
     DEFAULT-UL DE BROWSER pe butoane — nu vine din pagină, vine din foaia de
     stil a motorului. A stat ascuns cât timp `.select-value` era `flex: 0 1 auto`
     și cutia lui avea exact lățimea textului: nu era loc în care să se centreze.
     Din clipa în care slotul umple trigger-ul (ca să poată tăia cu elipsă pe o
     lățime fixă), centrarea moștenită a devenit vizibilă — toate valorile au
     sărit în mijloc.
     Un control nu-și ia alinierea din default-uri pe care nu le declară, la fel
     cum .check-item nu-și ia mărimea din gazdă. Și e chiar diferența pe care o
     susține docs/components/select.md § „Trigger-ul nu e un buton": un buton
     își centrează eticheta, un câmp își aliniază valoarea la start. */
  text-align: start;
  background: var(--c-white);
  border: 1px solid var(--c-input-border);
  border-radius: var(--r-lg);
  font-family: var(--font-sans);
  font-size: 14px; font-weight: 400; line-height: 20px;
  color: var(--c-ink); cursor: pointer;
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
}

/* ── Mărimi — aceeași scară ca .select (40 implicit, 44 lg), plus o treaptă de
   30px pentru antete și bare dense, care la .select nu există fiindcă un
   <select> nativ de 30px nu mai are unde să-și pună săgeata. ── */
.select-trigger.sm { height: 30px; padding: 0 10px; gap: 6px; border-radius: var(--r-md); }
:is(.select-trigger.lg, .cur-select-trigger) { height: 44px; padding: 0 20px; font-size: 16px; }

/* ── Variante ──────────────────────────────────────────────────── */
/* Tonal — aceeași pereche ca .input.tonal / .select.tonal, aceeași poziție în
   fișier: varianta stă ÎNAINTEA stărilor, ca ele să câștige prin ordine. */
.select-trigger.tonal { background-color: var(--c-bej-0); border-color: var(--c-input-border-tonal); }
/* Fără chrome — trigger-ul dintr-un antet sau dintr-o bară de unelte, unde
   chenarul ar desena o a doua cutie peste cea care îl conține deja. Hover-ul
   nu mai poate întuneca o bordură transparentă, deci trece pe fill (--c-bej-1),
   exact cum face .calendar-select, care e aceeași idee pe un <select> nativ. */
.select-trigger.bare {
  background: transparent; border-color: transparent;
  border-radius: var(--r-md); padding-inline: 6px;
  font-weight: 600;
}
/* Valoarea nealeasă încă — „Toate categoriile", „Alege data". Aceeași culoare
   ca .amount-value.placeholder: mist, nu fog, fiindcă e text absent, nu
   secundar. */
.select-trigger.placeholder { color: var(--c-mist); }

/* ── Stări — ordinea e regula, nu specificitatea (vezi .input:hover) ── */
/* Hover-ul se retrage complet pe focus și pe meniul deschis. Amândouă sunt
   garantate de ORDINE: regulile de focus/deschis vin imediat dedesubt și au
   aceeași greutate sau mai mare. Grupul din :where() nu adaugă nimic. */
@media (hover: hover) {
  :is(.select-trigger, .cur-select-trigger):hover:where(:not(:disabled):not(.is-disabled):not(.static)) { border-color: var(--c-input-border-hover); }
  .select-trigger.bare:hover:where(:not(:disabled):not(.is-disabled):not(.static)) { background: var(--c-bej-1); border-color: transparent; }
}

/* Deschis = `aria-expanded="true"`. Atributul e oricum obligatoriu pe orice
   declanșator de popover (docs/components/popover.md § Accesibilitate), deci
   starea se citește de unde e deja adevărată. `.open` pe ancoră rămâne valid
   — e forma pe care o are markupul monedei din v6.1 — dar e alias, nu canon:
   două locuri unde scrie „e deschis" înseamnă unul care poate minți.
   Vizual, deschis ARATĂ ca focus: același border fog, același halo. Un al
   doilea tratament n-ar spune nimic în plus. */
:is(.select-trigger, .cur-select-trigger):focus-visible,
:is(.select-trigger, .cur-select-trigger)[aria-expanded="true"],
:is(.select-anchor, .cur-select).open > :is(.select-trigger, .cur-select-trigger) {
  outline: none;
  border-color: var(--c-fog);
  background-color: var(--c-white);
  box-shadow: 0 0 0 3px rgba(122, 117, 112, 0.14);
}
/* .bare deschis nu-și inventează un chenar din nimic — își ține fill-ul de
   hover și lasă halo-ul să spună restul. */
.select-trigger.bare:focus-visible,
.select-trigger.bare[aria-expanded="true"],
.select-anchor.open > .select-trigger.bare { border-color: transparent; background: var(--c-bej-1); }

:is(.select-trigger, .cur-select-trigger).error { border-color: var(--c-error); background-color: var(--c-error-bg); }
/* Validare pe blur, ca la .input / .select: pe focus-in eroarea se ridică. */
:is(.select-trigger, .cur-select-trigger).error:focus-visible { border-color: var(--c-fog); background-color: var(--c-white); }
:is(.select-trigger, .cur-select-trigger).success { border-color: var(--c-success); }

/* `:disabled` e forma corectă pe un <button>; `.is-disabled` e alias pentru
   markupul care nu poate pune atributul. Aceleași declarații, un singur corp. */
:is(.select-trigger, .cur-select-trigger):disabled,
:is(.select-trigger, .cur-select-trigger).is-disabled {
  background-color: var(--c-bej-1); border-color: var(--c-input-border);
  color: var(--c-mist); cursor: not-allowed; pointer-events: none;
}
/* Săgeata dispare pe dezactivat: nu mai există meniu de deschis, deci n-are ce
   promite. */
:is(.select-trigger, .cur-select-trigger):disabled .chev,
:is(.select-trigger, .cur-select-trigger).is-disabled .chev { opacity: 0; }
/* .bare dezactivat rămâne fără chrome. Fill-ul bej-1 și --c-input-border de mai
   sus ar desena — într-un antet sau în addon-ul unui .input-group — exact cutia
   pe care .bare o scoate. Dezactivarea o spun textul mist și săgeata ascunsă;
   fundalul rămâne al gazdei (o carcasă dezactivată e oricum bej-1). */
.select-trigger.bare:disabled,
.select-trigger.bare.is-disabled { background-color: transparent; border-color: transparent; }

/* Static — valoarea e fixă și se afișează în forma controlului, dar nu e o
   alegere. Nu e disabled: nimic nu e blocat, pur și simplu nu e nimic de ales.
   Hover-ul nu-l atinge (e exclus în regula de hover de mai sus), iar săgeata nu
   se scrie deloc în markup — un chevron pe un control care nu deschide nimic e
   o promisiune falsă, și nu poate fi scoasă din CSS ce n-a fost pus acolo. */
:is(.select-trigger, .cur-select-trigger).static { cursor: default; }

/* ── Copii ─────────────────────────────────────────────────────── */
/* Săgeata. `margin-left: auto` contează doar pe trigger-ul care ține lățimea
   (.select-anchor.block, unde există spațiu liber); inline nu face nimic. */
:is(.select-trigger, .cur-select-trigger) > .chev {
  color: var(--c-fog); flex-shrink: 0;
  margin-left: auto; transition: transform 160ms;
  /* ═══ REGULA CHEVRONULUI (vezi și .accordion-chevron, .stepper-trigger .chev,
     .sidebar-grp-caret — toate o urmează): un chevron care se rotește la
     DESCHIDERE merge ÎN SENSUL ACELOR DE CEASORNIC, iar starea de repaus se scrie
     `rotate(0deg)`, nu se lasă pe `none`. ═══

     `rotate(0deg)` EXPLICIT, nu `none` — de asta atârnă direcția rotirii.
     Cu `transform: none` în repaus, browserul nu interpolează un UNGHI, ci
     matricea: `none` devine matricea identitate, iar `rotate(180deg)` devine
     matrix(-1,0,0,-1,0,0). Între cele două, la EXACT 180°, axa e ambiguă —
     decompoziția are două soluții egal de valide — și fiecare motor (și fiecare
     versiune) alege una. Chevronul se rotea deci în sensuri diferite pe mașini
     diferite, ceea ce e mai rău decât să se rotească greșit peste tot.
     Cu ambele capete scrise ca `rotate()`, interpolarea e pe listă de funcții:
     unghiul merge 0 → 180, adică ÎN SENSUL ACELOR DE CEASORNIC, peste tot.
     La închidere face drumul invers, 180 → 0. NU-l „simplifica" înapoi la
     `none`: arată identic în repaus și readuce nedeterminismul. */
  transform: rotate(0deg);
}
:is(.select-trigger, .cur-select-trigger)[aria-expanded="true"] > .chev,
:is(.select-anchor, .cur-select).open > :is(.select-trigger, .cur-select-trigger) > .chev { transform: rotate(180deg); }
/* Valoarea, când lățimea e constrânsă (filtru îngust, antet de calendar):
   se taie cu elipsă în loc să împingă săgeata afară. */
/* `flex: 1` e ce face elipsa să funcționeze de fapt: fără el slotul e
   `flex: 0 1 auto`, deci se strânge doar dacă e înghesuit, și pe un trigger cu
   lățime fixă rămâne la lățimea textului și împinge săgeata. Cu `flex: 1` ia
   spațiul rămas și se taie în el. */
.select-trigger > .select-value { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Iconița de context din stânga (o categorie, un tip de document). Urmează
   textul, nu-l concurează — aceeași regulă ca `.popover-item > svg`. */
:is(.select-trigger, .cur-select-trigger) > .ic:not(.chev) { color: var(--c-fog); flex-shrink: 0; }

.field { display: flex; flex-direction: column; gap: 4px; }
.field-label {
  font-family: var(--font-sans);
  font-size: 12px; font-weight: 500; line-height: 16px;
  letter-spacing: 0.04em; color: var(--c-fog);
  padding-left: 16px;
}
.field-help {
  font-family: var(--font-sans);
  font-size: 12px; font-weight: 400; line-height: 16px;
  color: var(--c-fog); padding-left: 16px;
}
/* Error text sub 18px pe tint/bej → token split --c-error-text (AA).
   Label-ul NU își schimbă culoarea în eroare — rămâne fog (în bordură: ink, vezi carcasa). */
.field-help.error   { color: var(--c-error-text); }
.field-help.success { color: var(--c-success-text); }

/* ═══ Orientarea câmpului — .field.horizontal / .field.responsive ═══
   Vertical (implicit) = eticheta DEASUPRA controlului, deci .field-label:
   12px uppercase fog, împins 16px ca să cadă pe textul din input.
   Orizontal = eticheta LÂNGĂ control, deci nu mai e .field-label — e .label
   (15px ink), regula care exista deja pentru checkbox, radio și switch.
   Orientarea schimbă axa, nu vocabularul: dacă eticheta trece la dreapta
   controlului, ea și-a schimbat deja clasa înainte să schimbi orientarea.

   .field-content ține textul (titlu + descriere) pe o coloană, ca rândul să
   aibă exact doi copii: textul și controlul. Fără el, un div fără nume face
   aceeași treabă și nimeni nu mai știe unde se pune descrierea.

   .field.responsive = orizontal peste 1024px, vertical dedesubt. Un singur
   breakpoint în tot sistemul (foundation-layout), și motivul e concret: un
   rând cu titlu de două rânduri plus switch la dreapta nu încape pe telefon.

   Astea înlocuiesc rândurile scrise cu style inline în components/switch.html
   („listă de setări") — același layout, copiat de trei ori pe aceeași pagină.
   ═══ */
.field.horizontal,
.field.responsive {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
/* Textul cedează, controlul nu: `min-width: 0` lasă titlul să se rupă pe două
   rânduri, iar `flex-shrink: 0` pe celălalt copil ține switch-ul la dimensiunea
   lui. Invers, rândul iese din card exact pe telefon, unde tap target-ul contează. */
.field-content { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.field.horizontal > :not(.field-content),
.field.responsive > :not(.field-content) { flex-shrink: 0; }
/* Cei 16px de pe .field-label / .field-help aliniază textul cu interiorul
   inputului de dedesubt. Într-un .field-content nu există input dedesubt, deci
   nu există cu ce să se alinieze — padding-ul ar fi o indentare fără cauză. */
.field-content .field-label,
.field-content .field-help { padding-left: 0; }
@media (max-width: 1023.98px) {
  .field.responsive { flex-direction: column; align-items: stretch; gap: 4px; }
  .field.responsive > :not(.field-content) { flex-shrink: 1; }
  /* Colapsat, controlul e din nou dedesubt — deci padding-ul de aliniere se întoarce. */
  .field.responsive .field-content .field-label,
  .field.responsive .field-content .field-help { padding-left: 16px; }
}

/* .kbd — DEPRECAT (v8.4-dev, 2026-10-08). Scurtătura de tastatură e un badge alb:
   .badge.sm.outline (components/badge.json → variants.shortcut). Cutia proprie —
   mono 11, 28px, --r-sm, contur --c-rule — era al doilea fel de etichetă mică din
   sistem lângă badge: altă tipografie, altă rază, alt contur, pentru același rol
   (un text scurt, non-semantic, așezat pe o suprafață).
   Rămâne ALIAS, nu regulă proprie: e grupat în selectorii lui .badge, .badge.sm și
   .badge.outline (secțiunea Badges), deci o copie ștampilată și referința Dublin
   (.search-wrap, oglinda producției) randează badge-ul alb fără să-și mute
   call-site-urile. Se scoate cel mai devreme la N+2 (VERSIONING.md). */

/* Search input — DEPRECAT din v8, înlocuit de .input-group cu addon-uri
   (docs/components/input.md, secțiunea Input group). Rămâne în fișier cât timp
   references/referinta-dublin-aligned.html — oglinda producției — îl folosește;
   se scoate odată cu sync-ul în BFF. Paginile din doc-originale/ nu mai contează
   aici: din PR #75 au stylesheet-ul lor propriu. Diferența nu e cosmetică: aici iconița și scurtătura sunt
   `position: absolute`, deci nu rezervă spațiu — de asta inputul are un
   `padding-left: 40px` scris de mână, care se rupe la orice altă iconiță.
   .input-group le pune în flux, deci spațiul e calculat, nu ghicit. */
.search-wrap { position: relative; display: flex; align-items: center; }
.search-wrap .search-ic {
  position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  color: var(--c-fog); pointer-events: none; width: 16px; height: 16px;
}
.search-wrap .input { padding-left: 40px; }
/* Aceeași regulă ca scurtătura din .input-group: aer egal sus, dreapta și jos.
   line-height fixat, ca badge-ul să aibă 22px și aici (altfel își ia înălțimea din
   context); right = bordura 1 + (40 − 2 − 22) / 2 = 9. */
.search-wrap .kbd {
  position: absolute; right: calc(1px + (40px - 2px - 22px) / 2); top: 50%; transform: translateY(-50%);
  line-height: 20px;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════════
   CARCASA — un singur motor, un singur nume: .input-group.
     control + addon-uri (iconiță, text, buton, scurtătură) pe rând cu el, sau
     control + o bară block-start / block-end — inclusiv ETICHETA în
     interiorul bordurii, care e un block-start cu un .field-label.
   Exact modelul shadcn: tot ce e în bordură e un InputGroup.
   Până în v8 eticheta din bordură se numea .field.input-em, cu eticheta
   ÎNAINTEA inputului în DOM. Scos în v8, fără alias: două nume pentru
   același lucru e cum a ajuns aplicația să construiască al șaselea.

   Regula de bază, comună: BORDURA TRECE PE CARCASĂ. Controlul dinăuntru își
   pierde bordura, fundalul, înălțimea și radius-ul, iar carcasa ține focusul
   (:focus-within), hover-ul și eroarea, cu exact valorile lui .input — un
   formular nu poate avea două feluri de a arăta focus.

   Addon-uri (.input-group), nume identice cu shadcn fiindcă înseamnă același lucru:
     inline-start   implicit — pe rând cu controlul, înaintea lui
     inline-end     pe rând cu controlul, după el
     block-start    rând propriu, deasupra unui TEXTAREA — bară, nu etichetă
     block-end      rând propriu, dedesubt — contor, buton de trimitere
     block-start + .field-label = ETICHETA în bordură
   `order` face poziționarea, nu ordinea din DOM: addon-ul se scrie DUPĂ
   control, ca tab-ul să ajungă întâi în câmp și abia apoi la butonul din
   addon. Aceeași notă e în documentația shadcn, din același motiv.

   Focus la click pe addon (cursor: text) e o promisiune pe care CSS-ul nu o
   poate ține singur: carcasa e un <div>, nu un <label>, fiindcă addon-ul
   poate conține butoane și un <button> într-un <label> ar activa controlul
   la fiecare apăsare. Aplicația pune handler-ul, cum îl pune și shadcn.
   ═══════════════════════════════════════════════════════════════ */

/* ─── Carcasa: conturul, o dată ─── */
.input-group {
  background: var(--c-white);
  border: 1px solid var(--c-input-border);
  border-radius: var(--r-lg);
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
}
.input-group {
  display: flex; align-items: center;
  width: 100%; min-width: 0;
  height: 40px; padding: 0 16px; gap: 8px;
}
.input-group.lg { height: 44px; padding: 0 20px; }
/* Suprafața NU e o decizie a carcasei — e regula de context a inputului:
   alb pe card bej-1, .tonal pe card alb sau bej-0. */
.input-group.tonal { background: var(--c-bej-0); border-color: var(--c-input-border-tonal); }

/* ─── Controlul: își predă carcasei tot ce ține de contur ─── */
.input-group > .input {
  height: auto; padding: 0;
  border: none; border-radius: 0;
  background: transparent;
}
/* `flex: 1 1 auto` plus `min-width: 0` — fără al doilea, un <input> refuză să
   scadă sub lățimea lui intrinsecă și împinge addon-ul afară din carcasă. */
.input-group > .input { flex: 1 1 auto; min-width: 0; }
/* inner input nu-și mai afișează halo-ul propriu — carcasa deține focusul */
.input-group > .input:focus,
.input-group > .input.is-focused { box-shadow: none; background: transparent; }

/* Textarea în carcasă: fără resize, altfel utilizatorul trage marginea peste
   toolbar-ul din .block-end. Excepția — eticheta în bordură — e mai jos. */
.input-group > textarea.input {
  min-height: 72px; line-height: 20px;
  resize: none; overflow-y: auto; scrollbar-width: thin;
}

/* ─── Stări, pe carcasă, o dată ───
   Garda de hover e :focus-within, și doar ea. .is-focused (clasa care
   DESENEAZĂ focusul fără focus real) nu e în :not(): e declarată mai jos și
   câștigă prin ordine. Când lipsea din listă, hover peste un câmp „focalizat"
   picta bordura de hover peste cea fog — bug găsit comparând stările randate,
   nu citind. */
@media (hover: hover) {
  .input-group:hover:where(:not(:focus-within)) { border-color: var(--c-input-border-hover); }
}
/* Deschis arată ca focus (docs/components/select.md), iar în carcasă halo-ul e al
   carcasei — deci o listă deschisă din addon aprinde carcasa, iar trigger-ul își
   ține doar fill-ul (vezi „O VALOARE în addon", mai jos). De obicei :focus-within o
   face deja: _select.js lasă focusul pe trigger. Radix nu: mută focusul în listă,
   pe care o portalează în <body>, deci în afara carcasei — fără :has() câmpul ar
   sta stins exact cât timp îi alegi moneda. Tot :has() aprinde și demourile scrise
   deschise. */
.input-group:focus-within,
.input-group.is-focused,
.input-group:has(> .input-group-addon .select-trigger[aria-expanded="true"]) {
  border-color: var(--c-fog);
  box-shadow: 0 0 0 3px rgba(122,117,112,0.14);
}
/* Eroare: bordura și tintul pe carcasă; la focus-within eroarea se ridică
   (validare pe blur). Mesajul e al câmpului (.field-help.error), nu al carcasei. */
.input-group.error { border-color: var(--c-error); background: var(--c-error-bg); }
.input-group.error:focus-within,
.input-group.error:has(> .input-group-addon .select-trigger[aria-expanded="true"]) { border-color: var(--c-fog); background: var(--c-white); }
.input-group.success { border-color: var(--c-success); }
.input-group:has(> .input:disabled) {
  background: var(--c-bej-1); border-color: var(--c-input-border); color: var(--c-mist);
}

/* ─── Eticheta în interiorul bordurii ───
   Forma canonică (v8) e cea din shadcn: un .input-group cu un addon block-start
   care ține un .field-label. Nu e o bară de instrumente, e ETICHETA — și se
   recunoaște prin conținut (> .field-label), nu printr-o clasă nouă: un nume în
   plus ar fi fost exact al șaselea lucru.

   Geometria: 8 / 16 / 2 / 7 → 53px — la pixel cea a fostului .field.input-em,
   ca migrarea Dublin/landing să nu mute nimic. Diferențele față de un
   block-start obișnuit (toolbar pe textarea): gap 0 în loc de 8, addon-ul pierde
   padding-ul de jos, controlul pierde cei 10px de sus, textarea se
   redimensionează vertical (nu are toolbar sub ea peste care să tragi).

   Eticheta: --c-accent în repaus, --c-accent-deep pe focus; bordura rămâne
   neutră — brandul marchează eticheta, nu starea. În eroare eticheta IESE din
   brand și devine ink, nu roșu: brandul nu poartă sens negativ, pink lângă roșu
   pe tint roșu măsoară 3.33:1 (sub AA), iar bordura + tintul + mesajul spun deja
   „eroare” — un label roșu ar fi a patra codificare. Rămâne ink și pe focus.
   uppercase e AICI și numai aici: în interiorul bordurii eticheta se citește ca
   un caption, nu ca o propoziție; .field-label obișnuit e sentence case.

   POLITICĂ, nu CSS: doar client-facing (Dublin, landing). Formularele interne
   folosesc .field neutru. Regula e în docs/components/input.md și în
   anti-pattern — o clasă e un mecanism slab de impunere, un audit e unul bun. */
.input-group:has(> .input-group-addon.block-start > .field-label) { gap: 0; }
.input-group > .input-group-addon.block-start:has(> .field-label) { padding: 8px 16px 0; cursor: default; }
.input-group:has(> .input-group-addon.block-start > .field-label) > .input { padding: 2px 16px 7px; }
.input-group:has(> .input-group-addon.block-start > .field-label) > textarea.input { min-height: 60px; resize: vertical; }
.input-group > .input-group-addon.block-start > .field-label {
  flex: 1;                 /* eticheta umple bara: click oriunde pe rând focalizează */
  padding-left: 0;
  color: var(--c-accent);
  text-transform: uppercase;
  transition: color 140ms;
}
.input-group:focus-within > .input-group-addon.block-start > .field-label { color: var(--c-accent-deep); }
.input-group.error > .input-group-addon.block-start > .field-label,
.input-group.error:focus-within > .input-group-addon.block-start > .field-label { color: var(--c-ink); }

/* ─── Addon-ul: slotul (.input-group) ───
   Nu-și impune fundal — e în interiorul carcasei, care are deja unul; un fundal
   propriu ar face un al doilea dreptunghi în același contur. */
.input-group-addon {
  display: flex; align-items: center; justify-content: center;
  gap: 8px; flex-shrink: 0;
  order: -1;                       /* implicit = inline-start */
  font-family: var(--font-sans); font-size: 14px; line-height: 20px;
  color: var(--c-fog);
  cursor: text; user-select: none;
}
.input-group-addon > svg:not([width]) { width: 16px; height: 16px; }
.input-group-addon.inline-start { order: -1; }
.input-group-addon.inline-end { order: 1; }

/* Rând propriu (textarea): carcasa devine coloană, iar padding-ul trece de pe
   carcasă pe copii — altfel barele ar avea 16px lateral și 0 vertical. */
.input-group:has(> .input-group-addon.block-start),
.input-group:has(> .input-group-addon.block-end) {
  flex-direction: column; align-items: stretch;
  height: auto; padding: 0;
}
.input-group:has(> .input-group-addon.block-start) > .input,
.input-group:has(> .input-group-addon.block-end) > .input { padding: 10px 16px; }
.input-group-addon.block-start,
.input-group-addon.block-end {
  width: 100%; justify-content: flex-start;
  padding: 8px 16px;
}
.input-group-addon.block-start { order: -1; }
.input-group-addon.block-end { order: 1; }
/* Linia dintre bară și control, când bara e un toolbar propriu. Opțională. */
.input-group-addon.block-start.ruled { border-bottom: 1px solid var(--c-rule-soft); }
.input-group-addon.block-end.ruled { border-top: 1px solid var(--c-rule-soft); }

/* Butonul din addon e .btn — NU o clasă nouă (scripts/check-icon-buttons.py).
   Mărimile din interior: .btn.xs / .btn.sm. Padding-ul carcasei scade pe partea
   cu butonul: 16px lângă un buton care are deja aria lui de tap se citește ca 24.
   Aceeași regulă pentru un select din addon (.select-anchor, mai jos): trigger-ul
   are și el aria lui, și fill-ul de hover până la 6px de bordură, ca butonul. */
.input-group:has(> .input-group-addon.inline-start > :is(.btn, .select-anchor)) { padding-left: 6px; }
.input-group:has(> .input-group-addon.inline-end > :is(.btn, .select-anchor)) { padding-right: 6px; }
/* Scurtătura (badge-ul alb, .badge.sm.outline) la capăt: AER EGAL sus, dreapta
   și jos. Un obiect cu contur propriu se citește după aerul din jurul lui; cu
   padding-ul de text al carcasei (16) stătea la 8 sus și jos și la 16 în dreapta,
   adică tras spre text. Badge-ul .sm are 22px în addon (line-height 20 + 1 + 1),
   deci aerul e (înălțimea interioară − 22) / 2: 38 → 8 pe carcasa implicită,
   42 → 10 pe .lg. Aceeași regulă ca butonul de mai sus — .btn.xs, 26px, are
   (38 − 26) / 2 = 6 de jur împrejur. Calculul stă în calc() ca să se vadă de unde
   vine numărul: dacă se mișcă înălțimea carcasei sau a badge-ului, se mișcă aici. */
.input-group-addon > .badge { line-height: 20px; }
.input-group:has(> .input-group-addon.inline-end > .badge) { padding-right: calc((40px - 2px - 22px) / 2); }
.input-group.lg:has(> .input-group-addon.inline-end > .badge) { padding-right: calc((44px - 2px - 22px) / 2); }
/* Butonul din addon e o acțiune A CÂMPULUI, nu CTA-ul ecranului: variante plate
   (.ghost, .outline, .outline-pink), fără altitudine. Dacă un ecran pune totuși
   una plină, grupul îi ia ridicarea — o pastilă care se ridică din interiorul
   unei borduri concurează cu submit-ul de sub formular. `box-shadow: none` e
   țintit pe variantele pline: pe .outline ar șterge chiar conturul, care e inset. */
@media (hover: hover) {
  .input-group .btn:hover:not(:disabled) { transform: none; }
  .input-group .btn.primary:hover:not(:disabled),
  .input-group .btn.secondary:hover:not(:disabled) { box-shadow: none; }
}

/* ─── O VALOARE în addon e un select, nu un .btn ───
   Butonul din addon e o ACȚIUNE a câmpului (copiază, aplică). Moneda de lângă o
   sumă la mărime normală (components/money-input.json) nu e o acțiune: e o
   valoare aleasă dintr-o listă — deci e ce spune components/select.json despre
   orice declanșator care poartă o valoare: .select-anchor + .select-trigger,
   niciodată .btn. În carcasă e `.sm` (30px, treapta lui .btn.sm) și `.bare`:
   bordura e deja a carcasei, un chenar propriu ar desena o cutie în cutie.
   Lista e cea a select-ului, cu bifa la capăt, și pleacă din dreapta
   (.popover.align-end), fiindcă trigger-ul stă la capătul câmpului.

   Ancora se întinde pe toată înălțimea carcasei, iar trigger-ul stă centrat în
   ea. Fără asta lista cade din trigger-ul de 30px, adică la 3px sub carcasă în
   loc de 8 — măsurat, singurul meniu din sistem lipit de câmpul lui. Întinsă,
   ancora are marginea de jos a carcasei, deci lista respiră ca la orice select
   (7px sub bordura exterioară: cei 8 ai popoverului, minus bordura de 1px). */
.input-group-addon:has(> .select-anchor) { align-self: stretch; }
.input-group-addon > .select-anchor { align-self: stretch; align-items: center; }

/* Un singur halo — al carcasei. Pe focus și pe deschis .bare primea halo-ul
   primitivei, iar carcasa își punea halo-ul în jurul lui: două inele unul în altul.
   În addon halo-ul trigger-ului iese. Fill-ul bej-1 RĂMÂNE, intenționat: spune care
   parte a câmpului e activă — moneda, nu suma — și e semnul de focus la tastatură
   (fără el, Tab de pe sumă pe monedă n-ar schimba nimic vizibil). E același semn ca
   al cursorului de tastatură dintr-o listă (.popover-item.is-active, tot bej-1). Nu
   se înlocuiește cu un outline: ar fi al doilea inel, exact ce iese aici.
   Doar în addon. Într-un antet sau într-o bară de unelte nu există o carcasă care
   să preia halo-ul, deci acolo .bare și-l păstrează. Selectorul e descendent, nu
   `> .select-anchor >`: în React trigger-ul stă direct în addon — Radix portalează
   lista, deci nu există ancoră. */
.input-group-addon .select-trigger.bare:is(:focus-visible, [aria-expanded="true"]) { box-shadow: none; }

/* Text static în addon: prefix (https://), sufix (RON, .com), contor. .ink pentru
   unități pe care utilizatorul le citește ca parte din valoare. */
.input-group-text {
  display: flex; align-items: center; gap: 6px;
  color: var(--c-fog); white-space: nowrap;
}
.input-group-text.ink { color: var(--c-ink); }

/* ═══ Badges ═══ */
/* line-height în px, nu moștenit (2026-10-15). Moștenit, badge-ul lua 1.5 de la
   body (16.5px → badge de 22.5px) sau 1.43 din text-sm-ul Tailwind (15.71px):
   altă înălțime în fiecare context, iar Chrome rotunjește jumătatea de spațiu de
   deasupra în jos, deci literele stăteau cu ~1px peste punct și iconiță (Dublin
   a peticit local cu leading-4, 51a788e și 3cd6c0d). Regula pentru orice
   componentă centrată: line-height-ul în px din scară (11/12→16, 13→18, 14→20,
   15→22, 16→24), niciodată moștenit și niciodată unitless. Măsurat în Chromium la
   100–200%: 0 decalaje evitabile, față de 40 din 140 moștenit. */
.badge,
.kbd {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--r-full);
  font-family: var(--font-sans);
  font-size: 11px; line-height: 16px; font-weight: 500;
  letter-spacing: 0.04em; text-transform: uppercase;
  white-space: nowrap;
}
.badge.success { background: var(--c-success-bg); color: var(--c-success-text); }
.badge.warn    { background: var(--c-warn-bg);    color: var(--c-warn-text); }
.badge.error   { background: var(--c-error-bg);   color: var(--c-error-text); }
.badge.ai      { background: var(--c-ai-bg);      color: var(--c-ai); }
.badge.pink    { background: var(--c-pink-tint);  color: var(--c-pink-deep); }
.badge.neutral { background: var(--c-bej-1);      color: var(--c-fog); }
/* Status "citit / văzut" — periwinkle. Fill-ul e --c-read-bg, textul e --c-read-text:
   la 11px, --c-read pe tint dă 4.25:1, sub AA. Aceeași separare ca la eroare
   (--c-error / --c-error-text), din același motiv. */
.badge.read    { background: var(--c-read-bg);    color: var(--c-read-text); }
/* Live/realtime status — gradient AI (nu negru: negrul e distructiv).
   Înlocuiește vechiul .badge.dark, care lega live-ul de suprafața neagră. */
.badge.live    { background: var(--c-ai-grad);     color: #fff; }
/* Outline — fundal alb + contur neutru. Singura variantă definită de contur, nu de
   fill: se citește pe suprafețe bej/tint, unde un fill deschis dispare. Conturul e
   --c-input-border (control mic), nu --c-rule: la 11px, 0.10 alpha pe bej nu se vede.
   inset box-shadow, nu border — păstrează metricile identice cu restul badge-urilor. */
/* Badge-ul ALB. E și scurtătura de tastatură — într-un addon .inline-end de
   .input-group, la .sm (badge.json → variants.shortcut). `.kbd` = alias deprecat. */
.badge.outline,
.kbd { background: var(--c-white); color: var(--c-fog); box-shadow: inset 0 0 0 1px var(--c-input-border); }

.badge.filled.success { background: var(--c-success); color: #fff; }
.badge.filled.warn    { background: var(--c-warn);    color: #fff; }
.badge.filled.error   { background: var(--c-error);   color: #fff; }
.badge.filled.ai      { background: var(--c-ai-grad); color: #fff; }
.badge.filled.pink    { background: var(--c-pink);    color: #fff; }
.badge.filled.neutral { background: var(--c-ink);     color: #fff; }
.badge.filled.read    { background: var(--c-read);    color: #fff; }

.badge .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; opacity: 0.85; }
/* ── Dimensiune ── două trepte, aceeași tipografie (11px). Diferă doar padding-ul:
   4px de înălțime și 4px de lățime. Regula, mașin-lizibilă, e în badge.json → sizes.
   .badge     = implicit — badge de sine stătător, densitate normală
   .badge.sm  = compact — obligatoriu în .tabs, .tbl, .doc-list, .accordion-title, .sidebar */
.badge.sm,
.kbd { padding: 1px 7px; font-size: 11px; }

/* AI inline tags */
.ai-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-sans);
  font-size: 11px; line-height: 16px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  background: var(--c-pink-tint); color: var(--c-pink-deep);
  padding: 3px 8px; border-radius: var(--r-full);
}
.ai-feed {
  display: inline-flex; align-items: center; gap: 8px;
  font-size: 12px; line-height: 16px; color: var(--c-fog);
  padding: 5px 11px; background: var(--c-bej-1);
  border-radius: var(--r-full);
}
.ai-feed strong { color: var(--c-pink); font-family: var(--font-display); font-style: italic; font-weight: 500; }

/* ═══ Cards ═══ */
/* Card content — white cu border, radius xl (24px) */
.card {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-xl);
  padding: 24px;
}
.card.tonal { background: var(--c-bej-1); border: none; }
.card.dark  {
  background: var(--c-dark-grad);
  border-color: rgba(255,255,255,0.05); color: #fff;
}
.card.dark .t-h2, .card.dark .t-h3,
.card.dark .t48,  .card.dark .t32  { color: #fff; }
.card.dark .t-body, .card.dark .t16 { color: rgba(255,255,255,0.65); }
/* .card-title declară --c-ink în regula lui, deci bătea albul moștenit de la
   .card.dark și ieșea ink pe gradient — același defect reparat la .tn40 / .tn28.
   Raportat din Dublin, care îl repara în theme.css (2026-10-07). */
.card.dark .card-title { color: #fff; }
.card.feat {
  border-color: color-mix(in oklab, var(--c-pink) 30%, var(--c-rule));
  box-shadow: 0 24px 60px -20px rgba(238,67,121,0.20);
}
.card.lg { padding: 32px; }
.card.sm { padding: 18px; }

/* Card cu CTA — flex-column + buton la baza, full-width
   Pattern din references (.price în landing, .da-act.tva-due în Dublin):
   .card.cta > .btn:last-child se lipește de bază cu margin-top:auto.
   Cu sloturile cardului (.card-title + .card-body) devine Action card —
   geometria lui e în secțiunea „Action card", după sloturi. */
.card.cta { display: flex; flex-direction: column; }
.card.cta > .btn:last-child,
.card.cta > .btn.full { margin-top: auto; }

/* KPI card — white cu border, radius lg (20px) */
.kpi {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-lg);
  padding: 18px 20px;
  position: relative;
}
/* Mărimea (11px) vine din podeaua scalei — grupat cu .t11 în secțiunea TIPOGRAFIE.
   Aici doar ce e specific KPI-ului: tracking mai larg, așezarea cu punctul. */
.kpi .label {
  letter-spacing: 0.10em;
  margin-bottom: 14px;
  display: flex; align-items: center; gap: 8px;
}
.kpi .label .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--c-mist); }
.kpi.income .label .dot  { background: var(--c-pink); }
.kpi.expense .label .dot { background: var(--c-ink); }
.kpi.profit .label .dot  { background: var(--c-success); }
.kpi.due .label .dot     { background: var(--c-pink); }
.kpi .value {
  font-family: var(--font-sans); font-weight: 600;
  font-size: 32px; letter-spacing: -0.035em; line-height: 1;
  color: var(--c-ink); font-variant-numeric: tabular-nums;
}
.kpi .value .unit { font-size: 14px; color: var(--c-mist); margin-left: 4px; font-weight: 400; letter-spacing: 0; }
.kpi .delta {
  margin-top: 10px;
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; line-height: 16px; font-variant-numeric: tabular-nums;
}
/* Compact automat — cardul își interoghează PROPRIA lățime, nu viewportul.
   Sub 220px (ex: 3 KPI pe un rând într-o coloană de conținut de 1024px minus
   sidebar și ticker → ≈191px/card) cifra coboară de la 32px la 26px și
   spațiile se strâng, ca valoarea să încapă fără să iasă din card. Nicio
   variantă separată de markup, niciun breakpoint de viewport.
   Cerință: pune .kpi în grid sau dă-i o lățime — într-un flex row cu lățime
   din conținut, containment-ul l-ar colapsa. */
.kpi { container-type: inline-size; }
@container (max-width: 220px) {
  .kpi .label { margin-bottom: 10px; letter-spacing: 0.08em; }
  .kpi .value { font-size: 26px; letter-spacing: -0.03em; }
  .kpi .value .unit { font-size: 12px; margin-left: 3px; }
  .kpi .delta { margin-top: 8px; }
}
.kpi .delta.up      { color: var(--c-success-text); }
.kpi .delta.down    { color: var(--c-warn-text); }
.kpi .delta.neutral { color: var(--c-fog); }
.kpi.dark {
  background: var(--c-dark-grad); color: #fff;
  border-color: transparent;
}
.kpi.dark .label        { color: rgba(255,255,255,0.55); }
.kpi.dark .value        { color: #fff; }
.kpi.dark .value .unit  { color: rgba(255,255,255,0.50); }
.kpi.dark .delta.neutral { color: rgba(255,255,255,0.65); }

/* Se încarcă — aria-busy="true" pe card. Rămâne doar titlul (fără perioadă),
   iar cifrele devin .skeleton: una mare în locul valorii, fără unitate, și una
   subțire în locul rândului de detalii, fără etichete. Fiecare bară ține un rând
   din .value / .delta, deci cardul nu se mișcă când sosesc numerele (Dublin,
   DashboardCountersRow).
   Bara de detalii e 15.4em (≈ „Încasat 000.000 · Rest 000.000"), dar nu iese
   din card: 100cqi e lățimea de conținut a .kpi (e container de query). La
   1024px cu sidebar plin, un card are ≈182px de conținut — 15.4em ar fi intrat
   3px în padding. */
.kpi .value > .skeleton { width: 4em; }
.kpi .delta > .skeleton { width: min(15.4em, 100cqi); }
/* .delta e inline-flex: bara ar fi devenit element flex, fără rând și fără linie
   de bază, iar cardul ar fi ieșit cu ~3px mai scund decât cel încărcat. Ca
   inline-block, .delta are același rând și aceeași linie de bază ca textul. */
.kpi .delta:has(> .skeleton) { display: inline-block; }
.kpi.dark .skeleton { background: rgba(255,255,255,0.12); }

/* ═══ List row — rândul de pe telefon (.list-row în .list-group) ═══
   Calul de povară al ecranelor mobile: rânduri de setări, liste de navigare,
   perechi etichetă–valoare, rândurile unui meniu sau ale unei foi (.sheet).
   UN singur rând pentru toate (2026-09-16): .flow-menu-row (din flow chrome)
   e alias deprecat și poartă aceleași reguli prin selectori grupați.
   Pereche desktop: .popover-item pentru meniuri, .field.horizontal pentru
   setări, .tbl pentru înregistrări.
   Geometrie: min 56px, padding 0 20 (10 20 cu subtitlu); titlu 15 ink,
   subtitlu 13 fog — aceleași trepte ca .t-ctrl / .t-ctrl-desc; hairline
   --c-rule-soft între rânduri; grupat pe un card alb la --r-lg (.list-group —
   forma de listă a cardului, cu padding 0; aceeași suprafață ca .kpi). TOT
   rândul e ținta de atins. Variante după ce stă în dreapta: .link (chevron,
   ultimul .ic) · .value (cifră) · .control (.switch) · static. Distructiv:
   .danger (ink 600). Densitate: .list-group.sm → rânduri de 44, obligatorie în
   .sheet (list-row.json → sizes). Eticheta de grup e .t12 în ink, în
   .list-section (etichetă + grup). */
.list-section { display: flex; flex-direction: column; gap: 6px; }
.list-section > .t12 { color: var(--c-ink); }
.list-group {
  background: var(--c-white); border: 1px solid var(--c-rule-card);
  border-radius: var(--r-lg); overflow: hidden;
}
.list-row, .flow-menu-row {
  width: 100%; display: flex; align-items: center; gap: 12px;
  min-height: 56px; padding: 0 20px;
  background: transparent; border: none; border-bottom: 1px solid var(--c-rule-soft);
  text-align: left; color: var(--c-ink); font-family: var(--font-sans);
  font-size: 15px; line-height: 22px; font-weight: 400; letter-spacing: 0;
  text-decoration: none; -webkit-appearance: none; appearance: none;
  -webkit-tap-highlight-color: transparent;
}
.list-row:last-child, .flow-menu-row:last-child { border-bottom: none; }
.list-row.has-sub { padding: 10px 20px; }
.list-group.sm .list-row, .list-group.sm .flow-menu-row { min-height: 44px; }
.list-group.sm .list-row.has-sub { padding: 6px 20px; }
.list-row-body  { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.list-row-title { font-size: 15px; line-height: 22px; }
.list-row-sub   { font-size: 13px; line-height: 18px; color: var(--c-fog); }
.list-row-value { font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* Iconul din rând e glifă Lucide cu .ic (.ic.lg = 20). Primul e cel din stânga
   (meniu), ultimul e chevronul unui .link — ambele fog. */
.list-row > .ic, .flow-menu-row > .ic, .flow-menu-ic { color: var(--c-fog); flex: 0 0 auto; }
.flow-menu-ic { display: flex; }
.list-row > .badge, .flow-menu-row > .badge { font-variant-numeric: tabular-nums; }
/* link — interactiv (button / a / label); hover pe --c-bej-05, treapta de hover
   a rândurilor de listă (vezi tokenul) */
.list-row.link, button.list-row, a.list-row, label.list-row, .flow-menu-row { cursor: pointer; }
@media (hover: hover) {
  .list-row.link:hover, button.list-row:hover, a.list-row:hover, label.list-row:hover, .flow-menu-row:hover { background: var(--c-bej-05); }
  .list-row.link:hover > .ic:last-child { color: var(--c-ink); }
}
/* Press (2026-10-15): pe touch hoverul e închis în @media (hover: hover), deci
   :active e singurul răspuns la tap — același token ca hoverul, nu o culoare nouă. */
.list-row.link:active, button.list-row:active, a.list-row:active, label.list-row:active, .flow-menu-row:active { background: var(--c-bej-05); }
.list-row.link:active > .ic:last-child { color: var(--c-ink); }
/* value — etichetă fog stânga, cifră dreapta; nu e interactiv */
.list-row.value { justify-content: space-between; }
.list-row.value .list-row-title { color: var(--c-fog); }
/* control — <label> cuprinde rândul + .switch; switch-ul are propria țintă de 44 */
.list-row.control > .switch { flex: 0 0 auto; }
/* danger — distructiv = negru (--c-ink), 600, icon ink; niciodată roșu */
.list-row.danger, .flow-menu-row.danger { color: var(--c-ink); font-weight: 600; }
.list-row.danger > .ic, .flow-menu-row.danger > .ic, .flow-menu-row.danger .flow-menu-ic { color: var(--c-ink); }
/* stări — urmează .check-item.card */
.list-row.selected { background: var(--c-pink-tint); }
.list-row.selected > .ic:last-child { color: var(--c-accent-deep); }
@media (hover: hover) {
  .list-row.selected:hover { background: var(--c-pink-tint); }
}
.list-row.selected:active { background: var(--c-pink-tint); }
.list-row:disabled, .list-row.disabled { color: var(--c-mist); cursor: not-allowed; pointer-events: none; }
.list-row:disabled .list-row-sub, .list-row.disabled .list-row-sub,
.list-row:disabled > .ic, .list-row.disabled > .ic { color: var(--c-mist); }
.list-row:focus-visible { outline: 2px solid var(--c-fog); outline-offset: -2px; }

/* ═══ Quantum stack — o înregistrare citită ca quante (2026-10-07) ═══
   Formalizat din Dublin (shared/dublin-ui/quantum-stack.tsx, blocul
   „Quantum stack" din theme.css), copiat regulă cu regulă, ca la
   re-ștampilare Dublin să nu se miște. Fiecare quantă e un fapt
   etichetă–valoare (.quantum-pair); perechile care țin împreună împart un
   card alb (.quantum-group); cardurile se stivuiesc la 10px
   (.quantum-stack), ca în aplicațiile de banking. Clasele stau PESTE
   clasele DS (.list-group, .list-row.value, .btn.link) și le schimbă doar
   unde o quantă diferă, deci orice alt .list-group, rând de valoare sau
   link își păstrează aspectul DS.
   Ierarhia: .quantum-stack > .quantum-group (<dl>) > .quantum-pair
   (<div>: <dt>.list-row-title + <dd>.list-row-value), iar valoarea e
   informație (text, sumă, <time>) sau o acțiune (.btn.link.quantum-action).
   Un fapt a cărui valoare are nevoie de toată lățimea cardului (o
   cronologie, o propoziție) e un .quantum-block, frate al grupurilor în
   stivă: un card al lui, eticheta deasupra, corpul dedesubt (2026-10-07,
   din dialogul facturii). */
.quantum-stack { display: flex; flex-direction: column; gap: 10px; }

/* Pe un .list-group. Rândul DS e de 56px, cu hairline --c-rule-soft sub
   fiecare rând în afară de ultimul. Rândul quantei e de 40px în 8px de
   padding de card, fără hairline: un card cu un rând păstrează cei 56px ai
   DS-ului, iar perechile din același card stau la 40px, despărțite doar de
   spațiu. */
.quantum-group { padding-block: 8px; }

/* Pe un .list-row.value. Eticheta și valoarea la 14px în loc de 15px, iar
   valoarea la 500 în loc de 600, ca eticheta, o valoare text, o cifră și o
   acțiune să se citească pe aceeași linie de date. Eticheta își păstrează
   fog-ul și 400 din DS. Valoarea DS e nowrap pentru cifre; aici o valoare
   text lungă se rupe în loc să împingă eticheta, iar o sumă își ține propriul
   nowrap. */
.quantum-pair { min-height: 40px; border-bottom: 0; }
.quantum-pair .list-row-title { flex-shrink: 0; font-size: 14px; }
.quantum-pair .list-row-value { min-width: 0; white-space: normal; font-size: 14px; font-weight: 500; }

/* <dl> / <dt> / <dd> vin cu margini de user-agent (1em pe <dl>, 40px la
   stânga pe <dd>). Dublin le anula prin preflight-ul Tailwind; DS-ul nu are
   reset global, deci le anulează aici — fără ele, valoarea pleacă de lângă
   marginea cardului într-o pagină fără preflight. */
.quantum-group,
.quantum-pair > .list-row-title,
.quantum-pair > .list-row-value,
.quantum-block,
.quantum-block-label,
.quantum-block-body { margin: 0; }

/* Pe un .btn.link: 600 al butonului coboară la 500 al valorii, la 14px-ul
   implicit al .btn. Iconul de 16px vine din regula .btn > svg a treptei
   implicite. Doar linkul e ținta; rândul rămâne inert — abatere deliberată
   de la .list-row, al cărui rând întreg e ținta: un card grupat poate
   amesteca rânduri de informație și de acțiune. */
.btn.quantum-action { font-weight: 500; }

/* Pe un .list-group: un fapt a cărui valoare are nevoie de lățimea
   cardului, de pildă o .timeline. Eticheta stă deasupra, în fog-ul de 14px
   al perechii (nu eticheta de 11px majuscule a prototipului), corpul sub
   ea, pe toată lățimea, la 8px. Un corp text se citește ca valoarea unei
   perechi — 14px / 500, ink — și se rupe; o .timeline își pune singură
   tipografia. Cardul lui, nu un rând într-un grup: un bloc lângă perechi
   în același card ar cere o linie care să arate unde începe, iar grupurile
   n-au linii. */
.quantum-block { display: flex; flex-direction: column; gap: 8px; padding: 16px 20px; }
.quantum-block-label { font-size: 14px; line-height: 22px; color: var(--c-fog); }
.quantum-block-body { min-width: 0; font-size: 14px; line-height: 20px; font-weight: 500; color: var(--c-ink); }

/* ═══ Timeline — pașii unei înregistrări, în ordinea în care s-au întâmplat (2026-10-07) ═══
   Formalizat din Dublin (shared/dublin-ui/timeline.tsx, blocul „Timeline"
   din theme.css), copiat regulă cu regulă. O listă verticală de pași:
   un nod de 18px la 10px de text, un hairline de 1px de sub fiecare nod
   până la următorul, titlul deasupra detaliului. Static — pașii nu sunt
   butoane; un parcurs prin care omul NAVIGHEAZĂ e .stepper.
   Nodurile iau culorile de stare ale stepper-ului (.stepper-ind): done e
   tint-ul verde cu bifă verde, current tint-ul roz cu un punct roz, fix
   (fără puls: un pas curent nu se mișcă, iar un dialog lăsat deschis nici
   atât), upcoming un inel, failed tint-ul de eroare cu un X. Titlul spune
   starea în cuvinte, deci nodul e decorativ (aria-hidden). Nu e legată de
   un card sau de facturi: într-un .quantum-block, ori direct în pagină.
   Ierarhia: ol.timeline > li.timeline-step[data-state] > span.timeline-node
   + div.timeline-text > .timeline-title (+ .timeline-detail). */
.timeline { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 0; list-style: none; }
.timeline-step { position: relative; display: flex; align-items: flex-start; gap: 10px; }
/* Hairline-ul pleacă de sub nodul ăsta până la următorul, prin golul de
   12px, centrat pe nod. Ultimul pas nu are. */
.timeline-step:not(:last-child)::after {
  content: "";
  position: absolute; left: 8.5px; top: 19px; bottom: -13px;
  width: 1px; background: var(--c-rule-card);
}
/* 1px mai jos, ca nodul de 18px să se centreze pe primul rând de 20px al titlului. */
.timeline-node {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  width: 18px; height: 18px; margin-top: 1px;
  border-radius: 50%;
}
/* Semnul din nod — bifa (done), X-ul (failed) — e un .ic la 10px, sub
   scara .ic (cel mai mic e .xs, 12px): mărimea o dă nodul, ca .btn
   iconului lui. Linia de 1.25px e cea din Dublin (stroke 3 pe viewBox-ul
   de 24, la 10px), aici în pixeli de ecran, ca orice .ic. */
.timeline-node > .ic { width: 10px; height: 10px; stroke-width: 1.25; }
.timeline-step[data-state="done"] .timeline-node { background: var(--c-success-bg); color: var(--c-success); }
.timeline-step[data-state="current"] .timeline-node { background: var(--c-pink-tint); }
.timeline-step[data-state="upcoming"] .timeline-node { box-shadow: inset 0 0 0 1.5px var(--c-input-border); }
.timeline-step[data-state="failed"] .timeline-node { background: var(--c-error-bg); color: var(--c-error); }
.timeline-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--c-pink); }
.timeline-text { display: flex; flex-direction: column; min-width: 0; }
.timeline-title { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--c-ink); }
.timeline-detail { font-size: 13px; line-height: 18px; font-weight: 400; color: var(--c-fog); }

/* ═══ Review card ═══ */
/* Card cu header (titlu + acțiune) + rânduri label muted ↔ valoare tabular.
   Pattern dovedit în proto-autoangajare (pas 3 Review). Modificările sunt
   colapsabile în .card-edit (toggle prin attr `hidden` sau JS). */
.card-hd {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; margin-bottom: 8px;
}
.card-row {
  display: flex; justify-content: space-between; align-items: center;
  gap: 12px; padding: 10px 0;
  border-bottom: 1px solid var(--c-rule-soft);
}
.card-row:last-child { border-bottom: none; }
.card-val {
  color: var(--c-ink);
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.card-edit {
  margin-top: 14px; padding-top: 14px;
  border-top: 1px solid var(--c-rule-soft);
}
.card-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 12px; }

/* ═══ Tabs / Segmented ═══ */
.tabs {
  display: inline-flex; padding: 3px;
  background: var(--c-bej-1);
  border-radius: var(--r-full);
}
.tabs button {
  font-family: var(--font-sans);
  /* 14px, the DS baseline. Inherited as 13px from the original documentation;
     there was no reason for horizontal navigation to read one step below
     vertical navigation (.sidebar a is 14px). The 34px height is deliberate and
     unchanged: 34 + 2 x 3px of .tabs padding = 40px, so a tab group and a
     .btn (md) on the same line share a baseline. */
  font-size: 14px; line-height: 20px; font-weight: 500;
  background: transparent; border: none; cursor: pointer;
  height: 34px; padding: 0 16px; border-radius: var(--r-full);
  color: var(--c-fog); transition: all 140ms;
  letter-spacing: -0.005em;
  display: inline-flex; align-items: center; gap: 6px;
}
@media (hover: hover) {
  .tabs button:hover { color: var(--c-ink); }
}
.tabs button.active {
  background: var(--c-white); color: var(--c-ink);
  box-shadow: 0 1px 2px rgba(17,13,16,0.06);
}

/* tab-icon — SVG la stânga labelului; moștenește culoarea butonului */
.tabs button .tab-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; flex-shrink: 0;
  color: inherit; transition: color 140ms;
}
.tabs button .tab-icon svg { display: block; }

/* badge în tabs — folosește .badge.sm.neutral / .badge.sm.pink */
.tabs button .badge { font-variant-numeric: tabular-nums; transition: background 140ms, color 140ms; }

/* dark variant */
.tabs.dark { background: var(--c-dark-grad); }
.tabs.dark button       { color: rgba(255,255,255,0.55); }
@media (hover: hover) {
  .tabs.dark button:hover { color: #fff; }
}
.tabs.dark button.active { background: var(--c-accent); color: #fff; box-shadow: none; }
.tabs.dark button .badge { background: rgba(255,255,255,0.10); color: rgba(255,255,255,0.55); }
.tabs.dark button.active .badge { background: rgba(255,255,255,0.20); color: #fff; }

/* tab panels — ascunse implicit, vizibile când [aria-selected="true"] */
[role="tabpanel"]         { display: none; }
[role="tabpanel"].active  { display: block; }
[role="tabpanel"]:focus-visible { outline: 2px solid var(--c-fog); outline-offset: 3px; border-radius: var(--r-sm); }

/* ═══ Accordion / Disclosure ═══
   3 variante, aceeași anatomie:
     .accordion             → flush (hairline între rânduri) — default, liste lungi / FAQ
     .accordion.contained   → fiecare item e card alb cu border — obiecte independente
     .accordion.sm          → dens (44px hit target) — sidebar, sub-rânduri, sheet mobil
   Anatomie trigger: [.acc-lead] → .acc-text (.acc-title + .acc-sub opțional) →
   [.acc-meta] → .acc-chevron. Toate sloturile în afara .acc-text sunt opționale;
   layout-ul e flex, deci absența lor nu lasă gap-uri fantomă.
   Panelul: închis = height 0 + visibility hidden (scos din tab-order/AT),
   deschis = height auto. Animația e progressive enhancement — JS-ul din snippet
   pune height în px pe durata tranziției, apoi revine la auto. Fără JS,
   deschiderea e instantanee, dar corectă. */
/* ─── Nume: aliniate la shadcn/Radix (Accordion / AccordionItem /
   AccordionTrigger / AccordionContent). Fiecare regulă poartă și numele
   vechi `.acc-*` ca ALIAS DEPRECAT, ca o copie ștampilată să poată re-copia
   fișierul fără să-și mute call-site-urile în același pas — exact cazul
   pentru care există VERSIONING.md. Scrie `.accordion-*` în cod nou;
   aliasurile cad la v9.0. NU amesteca prefixele pe același element:
   selectorii compuși (`:has(> .accordion-trigger)`) cer un singur set. ─── */
.accordion { display: flex; flex-direction: column; font-family: var(--font-sans); }
.accordion-item,
.acc-item { border-top: 1px solid var(--c-rule); transition: border-color 140ms; }
.accordion > .accordion-item:last-child,
.accordion > .acc-item:last-child { border-bottom: 1px solid var(--c-rule); }

.accordion-trigger,
.acc-trigger {
  width: 100%; min-height: 56px; margin: 0;
  padding: 15px 12px;
  display: flex; align-items: center; gap: 12px;
  background: transparent; border: none; cursor: pointer;
  text-align: left; color: var(--c-ink);
  font-family: var(--font-sans);
  transition: background 140ms;
}
/* Hover — --c-bej-05, treapta de hover de rând a sistemului (2026-09-21),
   aceeași ca `.tbl tbody tr:hover`, `.list-row.link:hover` și
   `.doc-item.compact:hover`. Un rând de accordion E un rând, deci ia treapta de
   rând: până aici era singurul din DS care se umplea cu --c-bej-0 opac, adică
   ținea o a doua convenție pentru același gest.

   Nu e o înmuiere, e o întărire pe gazda care contează. Un accordion stă cel mai
   des pe alb (`.accordion.contained`, sau un accordion flush într-un `.card`):
   acolo bej-0 dădea ΔL* 1.66 — SUB treapta de 1.73 respinsă pe 2026-09-02 — iar
   bej-05 dă 3.39, exact valoarea pentru care a fost calibrat tokenul. Pe ground-ul
   de pagină (body.has-grid, bej-0) vechea regulă avea ΔL* ZERO: fundalul era
   identic cu gazda, iar tot semnalul stătea în punctele grid-ului acoperite de un
   fill opac. bej-05 e tot opac, deci acoperă punctele LA FEL, și mai adaugă 1.66
   de ton peste ele. Strict mai mult semnal pe ambele gazde, pe niciuna mai puțin.

   --c-bej-1 (tonal) rămâne exclus, din motivul de dinainte: pe un rând full-width
   s-ar citi ca selecție, nu ca hover, și ar sări mai mult decât starea deschis.

   Preț declarat: `.acc-sub` / `.acc-meta` (--c-fog, 13px) coboară de la 4.37:1 la
   4.19:1 — ambele sub AA, ambele deja datorie declarată în
   tests/error-tokens/baseline.json (perechea fog × bej-0), pe o stare tranzitorie.
   Aceeași citire ca `.tbl .date`, care stă pe bej-05 din 2026-09-09. Chevronul
   trece pe --c-ink; el rămâne semnalul principal, nu fundalul. */
@media (hover: hover) {
  .accordion-trigger:hover,
  .acc-trigger:hover { background: var(--c-bej-05); }
}
/* Press (2026-10-15): pe touch hoverul e închis în @media (hover: hover), deci
   :active e singurul răspuns la tap — același token ca hoverul, nu o culoare nouă. */
.accordion-trigger:active,
.acc-trigger:active { background: var(--c-bej-05); }
/* Hairline-urile care delimitează rândul se închid pe --c-rule-hover (o treaptă
   peste --c-rule). Linia de jos a unui rând e border-top al fratelui următor,
   deci regula prinde rândul, fratele de dedesubt și ultimul item pentru propriul
   border-bottom. `> .acc-trigger` (copil direct): un accordion imbricat nu aprinde
   hairline-ul părintelui. */
@media (hover: hover) {
  .accordion-item:has(> .accordion-trigger:hover:not(:disabled)),
  .accordion-item:has(> .accordion-trigger:hover:not(:disabled)) + .accordion-item,
  .acc-item:has(> .acc-trigger:hover:not(:disabled)),
  .acc-item:has(> .acc-trigger:hover:not(:disabled)) + .acc-item { border-top-color: var(--c-rule-hover); }
  .accordion > .accordion-item:last-child:has(> .accordion-trigger:hover:not(:disabled)),
  .accordion > .acc-item:last-child:has(> .acc-trigger:hover:not(:disabled)) { border-bottom-color: var(--c-rule-hover); }
}
.accordion-trigger:focus-visible,
.acc-trigger:focus-visible { outline: 2px solid var(--c-fog); outline-offset: -2px; border-radius: var(--r-xs); }
.accordion-trigger:disabled,
.acc-trigger:disabled { cursor: not-allowed; background: transparent; color: var(--c-mist); }
.accordion-trigger:disabled .accordion-sub,
.accordion-trigger:disabled .accordion-meta,
.accordion-trigger:disabled .accordion-lead,
.accordion-trigger:disabled .accordion-chevron,
.acc-trigger:disabled .acc-sub,
.acc-trigger:disabled .acc-meta,
.acc-trigger:disabled .acc-lead,
.acc-trigger:disabled .acc-chevron { color: var(--c-mist); }

/* text — un rând (.acc-title) sau două (.acc-title + .acc-sub) */
.accordion-text,
.acc-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.accordion-title,
.acc-title {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 15px; font-weight: 500; line-height: 20px;
  letter-spacing: -0.005em; color: inherit;
}
.accordion-sub,
.acc-sub { font-size: 13px; font-weight: 400; line-height: 1.45; color: var(--c-fog); }
/* .clamp — un singur rând vizibil în liste dense; textul complet rămâne în panel */
.accordion-sub.clamp,
.acc-sub.clamp { display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; }

/* sloturi opționale */
.accordion-lead,
.acc-lead {
  flex-shrink: 0; width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--c-fog); transition: color 140ms;
}
.accordion-item[data-state="open"] .accordion-lead,
.acc-item[data-state="open"] .acc-lead { color: var(--c-ink); }
.accordion-meta,
.acc-meta {
  flex-shrink: 0; font-size: 13px; font-weight: 500;
  color: var(--c-fog); font-variant-numeric: tabular-nums;
}
.accordion-chevron,
.acc-chevron {
  flex-shrink: 0; width: 20px; height: 20px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--c-fog);
  transition: transform 200ms cubic-bezier(0.2,0.7,0.3,1), color 140ms;
  /* Repaus explicit — vezi REGULA CHEVRONULUI la `.select-trigger > .chev`.
     Fără el, `none → rotate(...)` se interpolează ca matrice și la exact 180°
     axa e ambiguă: fiecare motor alege un sens. */
  transform: rotate(0deg);
}
@media (hover: hover) {
  .accordion-trigger:hover .accordion-chevron,
  .acc-trigger:hover .acc-chevron { color: var(--c-ink); }
}
.accordion-trigger:active .accordion-chevron,
.acc-trigger:active .acc-chevron { color: var(--c-ink); }
/* `> .accordion-trigger` (copil direct), nu descendent (2026-09-22): într-un accordion
   imbricat, itemul-părinte deschis rotea ȘI chevroanele itemelor-copil închise, deci un
   accordion în accordion arăta toate rândurile ca deschise. Aceeași grijă pe care o are
   deja regula de hairline de mai sus (`:has(> .accordion-trigger:hover)`).
   Prins pe De plată, în bono-ro/Dublin-vs: lunile dintr-o obligație sunt un accordion
   în panelul obligației. */
.accordion-item[data-state="open"] > .accordion-trigger .accordion-chevron,
/* `180deg`, nu `-180deg` (2026-09-21). Accordionul era singurul chevron din sistem
   care se rotea ANTIORAR la deschidere — scris explicit, deci era o decizie, dar o
   decizie care făcea ca același gest („deschide") să aibă două sensuri în același
   sistem, după componentă. Un utilizator nu ține minte care listă se rotește încotro;
   ține minte că lucrurile se deschid într-un fel. Acum toate merg în sensul acelor. */
.acc-item[data-state="open"] > .acc-trigger .acc-chevron { transform: rotate(180deg); color: var(--c-ink); }
/* chevron la început (pattern „tree / grup") — ordinea vizuală, nu cea din DOM */
.accordion.chevron-start .accordion-chevron,
.accordion.chevron-start .acc-chevron { order: -1; }

/* ─── .action — deschidere cu buton, nu cu rândul ───────────────────────────
   DOAR pe un accordion-item de sine stătător. Într-un grup, un buton pe fiecare
   rând dă N afordanțe care concurează, iar rândul nu mai e scanabil.
   Butoanele NU se cuibăresc, deci varianta asta nu pune un .btn în trigger — îl
   înlocuiește: `.accordion-header` e un div, nu un buton, iar `aria-expanded` /
   `aria-controls` stau pe .btn. Rândul nu mai e clickabil, deci nu are nici
   hover-ul de rând. Chevronul dispare: butonul E afordanța, iar două afordanțe
   pentru aceeași regiune sunt exact bug-ul pe care regula veche îl prevenea. */
.accordion-header {
  width: 100%; min-height: 56px;
  padding: 15px 12px;
  display: flex; align-items: center; gap: 12px;
  text-align: left; color: var(--c-ink);
  font-family: var(--font-sans);
}
.accordion.contained .accordion-header { padding: 16px 18px; }
.accordion.sm .accordion-header { min-height: 44px; padding: 11px 10px; }
/* butonul nu se întinde și nu se strânge — rămâne la lățimea etichetei */
.accordion-action { flex-shrink: 0; }
/* chevronul nu coexistă cu butonul */
.accordion.action .accordion-chevron { display: none; }

/* panel */
.accordion-content,
.acc-panel {
  height: 0; overflow: hidden; visibility: hidden;
  transition: height 220ms cubic-bezier(0.2,0.7,0.3,1), visibility 0s linear 220ms;
}
.accordion-item[data-state="open"] > .accordion-content,
.acc-item[data-state="open"] > .acc-panel { height: auto; visibility: visible; transition-delay: 0s; }
/* elementul măsurat de JS pentru înălțimea-țintă. flow-root, ca marginile
   copiilor să nu iasă din măsurătoare — offsetHeight ar da atunci mai puțin
   decât înălțimea reală și panelul s-ar deschide tăiat. */
.accordion-content-inner,
.acc-panel-inner { display: flow-root; }
.accordion-content-body,
.acc-panel-body {
  padding: 0 12px 18px; max-width: 680px;
  font-size: 14px; line-height: 1.6; color: var(--c-fog);
}
.accordion-content-body > :first-child,
.acc-panel-body > :first-child { margin-top: 0; }
.accordion-content-body > :last-child,
.acc-panel-body > :last-child { margin-bottom: 0; }
.accordion-content-body strong,
.acc-panel-body strong { color: var(--c-ink); font-weight: 600; }

/* contained — item = card */
.accordion.contained { gap: 8px; }
.accordion.contained > .accordion-item,
.accordion.contained > .acc-item {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-md);
}
.accordion.contained > .accordion-item:last-child,
.accordion.contained > .acc-item:last-child { border-bottom: 1px solid var(--c-rule-card); }
.accordion.contained > .accordion-item[data-state="open"],
.accordion.contained > .acc-item[data-state="open"] { border-color: var(--c-rule); }
/* Pe contained se închide tot borderul cardului pe --c-rule-hover. `> .acc-trigger`
   (copil direct) + :has(): hover-ul se aprinde din trigger, nu din :hover pe item,
   deci chenarul nu reacționează când citești sau selectezi text în panelul deschis.
   Câștigă și peste chenarul de stare al itemului deschis — rândul rămâne clickabil. */
@media (hover: hover) {
  .accordion.contained > .accordion-item:has(> .accordion-trigger:hover:not(:disabled)),
  .accordion.contained > .acc-item:has(> .acc-trigger:hover:not(:disabled)) { border-color: var(--c-rule-hover); }
}
.accordion.contained .accordion-trigger,
.accordion.contained .acc-trigger { padding: 16px 18px; border-radius: var(--r-md); }
.accordion.contained .accordion-content-body,
.accordion.contained .acc-panel-body { padding: 0 18px 18px; }

/* sm — dens */
.accordion.sm .accordion-trigger,
.accordion.sm .acc-trigger { min-height: 44px; padding: 10px; gap: 10px; }
.accordion.sm .accordion-title,
.accordion.sm .acc-title { font-size: 14px; }
.accordion.sm .accordion-sub,
.accordion.sm .acc-sub { font-size: 12px; }
.accordion.sm .accordion-lead,
.accordion.sm .accordion-chevron,
.accordion.sm .acc-lead,
.accordion.sm .acc-chevron { width: 16px; height: 16px; }
.accordion.sm .accordion-content-body,
.accordion.sm .acc-panel-body { padding: 0 10px 12px; font-size: 13px; }
.accordion.contained.sm .accordion-trigger,
.accordion.contained.sm .acc-trigger { padding: 12px 14px; }
.accordion.contained.sm .accordion-content-body,
.accordion.contained.sm .acc-panel-body { padding: 0 14px 14px; }

@media (max-width: 640px) {
  .accordion-trigger,
  .acc-trigger { padding: 14px 4px; }
  .accordion.contained .accordion-trigger,
  .accordion.contained .acc-trigger { padding: 14px 16px; }
  .accordion-content-body,
  .acc-panel-body { padding: 0 4px 16px; }
  .accordion.contained .accordion-content-body,
  .accordion.contained .acc-panel-body { padding: 0 16px 16px; }
}

/* la print, tot conținutul e vizibil — un document tipărit nu are stare deschis/închis */
@media print {
  .accordion-content,
  .acc-panel { height: auto !important; visibility: visible !important; }
  .accordion-chevron,
  .acc-chevron { display: none; }
}

/* ═══ Toggle / Switch ═══ */
.switch {
  width: 36px; height: 20px;
  background: var(--c-bej-1); border-radius: var(--r-full);
  position: relative; cursor: pointer;
  transition: background 140ms; border: none; padding: 0;
}
.switch::after {
  content: ''; position: absolute;
  top: 2px; left: 2px; width: 16px; height: 16px;
  background: #fff; border-radius: 50%;
  box-shadow: var(--sh-sm); transition: transform 160ms;
}
.switch.on { background: var(--c-accent); }
.switch.on::after { transform: translateX(16px); }
/* .on is not named here — .switch.on:hover below carries more weight and wins. */
@media (hover: hover) {
  .switch:hover:where(:not(:disabled)) { background: color-mix(in srgb, var(--c-bej-1), var(--c-ink) 7%); }
  .switch.on:hover:not(:disabled) { background: var(--c-accent-deep); }
}

/* ═══════════════════════════════════════════════════════════════
   FEEDBACK — DOUĂ componente, și doar două: .alert și .toast.

   MVP-ul regulilor IBM Carbon, cu logica noastră:

     .toast        tranzitoriu. Ceva s-a întâmplat. Dispare pe cronometru.
                   Poate purta O acțiune, dar numai OPȚIONALĂ — adică „nu
                   fac nimic" e un rezultat corect și sigur. Undo e cazul
                   canonic: cronometrul E fereastra de undo.
     .alert        persistent. Rămâne până se rezolvă cauza sau până îl
                   închide utilizatorul. Două plasări, un singur component:
                     inline          lângă lucrul despre care vorbește
                     .alert.page     full-bleed, lipit sus în pagină

   Regula care le separă, singura care trebuie ținută minte:
     TRANZITORIU E PERMIS DOAR CÂND „NU FAC NIMIC" E UN REZULTAT CORECT.
   De asta o eroare nu e aproape niciodată toast: eroarea cere ceva.

   Cinci stări pe amândouă, cu același sens: neutral · success · warning
   · error · brand.

   .banner, .page-banner și .alert.floating sunt DEPRECATE din v8 în
   favoarea lui .alert (vezi mai jos). Rămân funcționale până la
   îndepărtare — nu mai devreme de v10 (VERSIONING.md).

   Anatomia e aceeași pe amândouă: .ic + corp + dismiss. Iconul e
   dimensionat AICI, nu din atributele width/height ale SVG-ului.
   Numele butonului de închidere e <componentă>-dismiss, fără excepții.
   ═══ */

/* Suprafața comună. */
.alert,
.toast {
  border-radius: var(--r-md); padding: 14px 16px;
  display: flex; align-items: flex-start; gap: 12px;
  font-family: var(--font-sans); font-size: 14px; line-height: 1.5;
}

/* Iconul — mărimea și linia vin din .ic (secțiunea Icons); aici doar
   alinierea: centrat EXACT pe prima linie, (1lh − 16px) / 2 = 2.5px la
   14px/1.5. Un mesaj de un rând iese centrat vertical; unul de mai multe
   rânduri ține iconul lângă primul rând, nu la mijlocul blocului.
   `>` ca să nu prindă și glyphul din dismiss sau din acțiuni, care se
   centrează singure în buton. */
.alert > .ic,
.toast > .ic { margin-top: calc((1lh - 16px) / 2); }

/* Corpul — min-width: 0 e ce ține textul lung comprimabil, ca să nu
   împingă dismissul afară din suprafață. */
.alert-body,
.toast-body { flex: 1; min-width: 0; }

/* Dismissul — NU o clasă proprie. E butonul din design system:
   .btn.ghost.xs.icon-only (26×26, cerc, icon 12px — components/button.json).

   Aici stă doar o EXTENSIE SCOPATĂ, singurul lucru care nu poate veni din
   contractul butonului: culoarea. .btn.ghost e --c-fog implicit, iar pe un
   tint de warning sau de error fogul se ceartă cu textul de lângă el, deci
   dismissul moștenește culoarea stării.

   Clasele .alert-dismiss / .toast-dismiss au fost RETRASE (2026-09-01):
   inventau un al cincilea pas de mărime (24px) peste scara de 26/30/40/44 a
   lui .btn.icon-only — exact ce interzice button.json, „DO NOT invent a
   parallel size scale for icon buttons". .toast-close rămâne alias deprecat,
   fiind posibil în copiile ștampilate de la v7. */
.alert > .btn.icon-only,
.toast > .btn.icon-only {
  flex-shrink: 0; align-self: flex-start;
  color: inherit; opacity: 0.55;
  /* Butonul de 26px se centrează pe prima linie, ca iconul:
     (21px − 26px) / 2. Literal, nu 1lh — pe buton 1lh s-ar rezolva la
     line-height-ul butonului, nu la al textului din mesaj. */
  margin-top: -2.5px; margin-right: -6px;
}
@media (hover: hover) {
  .alert > .btn.icon-only:hover:not(:disabled),
  .toast > .btn.icon-only:hover:not(:disabled) {
    opacity: 1; color: inherit;
    background: color-mix(in oklab, currentColor 10%, transparent);
  }
}

/* .toast-close — alias DEPRECAT (2026-09-01) pentru .btn.ghost.xs.icon-only.
   Rămâne ca să nu rupă copiile ștampilate; nu se folosește în cod nou. */
.toast-close {
  flex-shrink: 0; box-sizing: border-box;
  width: 26px; height: 26px; border-radius: var(--r-full);
  background: transparent; border: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: inherit; opacity: 0.55; padding: 0;
  margin-top: -2px; margin-right: -6px;
  transition: opacity 120ms, background 120ms;
}
@media (hover: hover) {
  .toast-close:hover {
    opacity: 1;
    background: color-mix(in oklab, currentColor 10%, transparent);
  }
}

/* Acțiuni — butoane sm sub text. Aceeași regulă pe amândouă; pe toast,
   maximum UNA, și numai opțională. */
.alert-actions,
.toast-actions {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
}
.alert-actions .btn,
.toast-actions .btn { flex-shrink: 0; }

/* Butonul ghost din feedback moștenește culoarea stării — altfel un ghost
   pe tint de warning sau de error rămâne --c-fog și se ceartă cu textul
   de lângă el. .primary și .outline își păstrează culorile proprii:
   primary e brandul, outline e conturul neutru. */
.alert-actions .btn.ghost,
.toast-actions .btn.ghost { color: inherit; }
@media (hover: hover) {
  .alert-actions .btn.ghost:hover:not(:disabled),
  .toast-actions .btn.ghost:hover:not(:disabled) {
    color: inherit;
    background: color-mix(in oklab, currentColor 10%, transparent);
  }
}

/* Textul — strong poartă prima propoziție, linkul moștenește starea. */
.alert strong,
.toast strong { color: inherit; font-weight: 600; }
.alert a,
.toast a { color: inherit; text-decoration: underline; opacity: 0.85; }
@media (hover: hover) {
  .alert a:hover,
  .toast a:hover { opacity: 1; }
}

/* ─── Stările — perechea de culoare e regula, pe amândouă componentele.
   FUNDAL = tintul familiei. TEXT = tokenul -text al familiei.
   Niciodată fill-ul ca text: --c-error pe --c-error-bg dă 4.38:1, sub AA.

   .warn și .info sunt ALIASURI DEPRECATE (v8) pentru .warning și
   .neutral. Rămân ca să nu rupă copiile ștampilate din consumatori; nu
   se folosesc în cod nou. ─── */
.alert.neutral, .alert.info,
.toast.neutral, .toast.info {
  background: var(--c-bej-1); color: var(--c-ink);
}
.alert.success,
.toast.success {
  background: var(--c-success-bg); color: var(--c-success-text);
}
.alert.warning, .alert.warn,
.toast.warning, .toast.warn {
  background: var(--c-warn-bg); color: var(--c-warn-text);
}
.alert.error,
.toast.error {
  background: var(--c-error-bg); color: var(--c-error-text);
}
/* Brand — marker pink 3px stânga pe fundal alb, nu fill colorat.
   Corpul e --c-ink, nu --c-fog: brandul e un moment de atenție, iar
   fogul îl citea ca text secundar. Aldinul și iconul poartă pinkul.
   Tratamentul e IDENTIC pe toate trei suprafețele, inclusiv
   .alert.page.brand — vezi mai jos. */
.alert.brand,
.toast.brand {
  background: var(--c-white); color: var(--c-ink);
  border: 1px solid var(--c-rule);
  border-left: 3px solid var(--c-pink);
  padding: 13px 15px 13px 13px;
}
.alert.brand strong, .toast.brand strong { color: var(--c-pink-deep); }
.alert.brand .ic,   .toast.brand .ic    { color: var(--c-pink-deep); }

/* ═══ .alert.page — plasarea la nivel de pagină ═══

   În FLUX, nu fixed: se inserează ca prim copil al shell-ului și împinge
   conținutul în jos. Nu acoperă topnavul, nu are z-index, nu se poate
   suprapune cu un modal. Full-bleed, colțuri drepte, lipit sus fără gap.
   Aceleași cinci stări, același dismiss opțional. */
.alert.page {
  width: 100%;
  border-radius: 0;
  border-left: none;
  padding: 12px 20px;
  margin: 0;
}
/* Brandul păstrează markerul și pe bandă full-bleed: fundal alb, bara
   pink 3px în stânga, aldinul pink, restul textului --c-ink. Un tint
   roz pe toată lățimea ar fi un al doilea tratament pentru aceeași
   stare — marker peste tot, o singură regulă de ținut minte. */
.alert.page.brand {
  background: var(--c-white); color: var(--c-ink);
  border: none;
  border-left: 3px solid var(--c-pink);
  padding: 12px 20px 12px 17px;
}
.alert.page.brand strong { color: var(--c-pink-deep); }
.alert.page.brand .ic    { color: var(--c-pink-deep); }

/* ═══ Toast — stiva, ridicarea, intrarea ═══

   POZIȚIE: SUS-CENTRAT. Confirmarea apare unde se uită deja utilizatorul
   după ce a apăsat ceva, și nu acoperă CTA-ul de jos pe mobil.
   .toast-stack.top-center e alias no-op DEPRECAT (v8) — poziția e acum
   implicită, modificatorul nu mai are ce să schimbe.

   STRATUL: DEASUPRA SCRIMULUI. z-index 300 > .modal-backdrop (200). Până la
   2026-10-07 era 100, deci un toast declanșat dintr-un modal ieșea SUB voal și
   blur — confirmarea exista, dar nu se vedea (Dublin: „Descarcă PDF", „Trimite pe
   email" din detaliul facturii). MUI (modal 1300 · snackbar 1400), Bootstrap
   (modal 1055 · toast 1090) și Atlassian (modal · flag deasupra) țin toastul peste
   modal. Harta: grilă 0–1 · popover/dropdown 20–40 · rail 50 · sheet 60 · fab 90 ·
   modal/backdrop 200 · toast 300.
   Stratul singur nu ajunge pentru cititorul de ecran: VoiceOver ignoră regiunile
   live din afara unui dialog `aria-modal`. Cu un modal deschis, gazda mută stiva în
   [role="dialog"] și o întoarce în body la închidere (toast.md § „Toast și modal"). */
.toast-stack {
  position: fixed; top: 24px; left: 50%; transform: translateX(-50%);
  z-index: 300;
  display: flex; flex-direction: column; gap: 10px;
  width: min(560px, calc(100vw - 32px));
  pointer-events: none;
}
.toast-stack.top-center { /* alias no-op — vezi comentariul de mai sus */ }
.toast {
  pointer-events: auto;
  background: var(--c-white); color: var(--c-ink);
  border: 1px solid var(--c-rule-card);
  box-shadow: var(--sh-toast);
  animation: toast-in 220ms cubic-bezier(0.16,1,0.3,1);
}
/* Bordura = tokenul -text al familiei la 70%: aceeași pereche de
   culoare, plus o margine. Tintul singur e la 1.05–1.12:1 față de pagină
   (topnav alb, bej-0) — sub 3:1 cerut pentru marginea unui container.
   Neutral ia --c-fog, nu --c-ink: ink-ul dădea 19:1, o margine de trei
   ori mai grea decât a celorlalte stări.

   70% = media opacităților minime care duc fiecare stare la 3:1 pe bej-0
   (neutral 76.5 · success 63.4 · warning 68.5 · error 62.6 → 67.75),
   rotunjită în sus. O singură valoare pentru toate, ca marginile să aibă
   aceeași greutate. Rezultat, vs alb / bej-0: success 3.6/3.4 ·
   warning 3.2/3.1 · error 3.6/3.4 · neutral 2.9/2.7 — neutral rămâne puțin sub 3:1, pentru că fog-ul
   pornește mai deschis. Pe 100% toate dădeau 4.4–6.4:1.

   Bordura de 1px exista deja (transparentă), deci zero salt de layout. */
.toast.neutral, .toast.info { border-color: color-mix(in srgb, var(--c-fog)          70%, transparent); }
.toast.success              { border-color: color-mix(in srgb, var(--c-success-text) 70%, transparent); }
.toast.warning, .toast.warn { border-color: color-mix(in srgb, var(--c-warn-text)    70%, transparent); }
.toast.error                { border-color: color-mix(in srgb, var(--c-error-text)   70%, transparent); }

@keyframes toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
  .toast { animation: none; }
}
@media (max-width: 1023.98px) {
  .toast-stack { left: 16px; right: 16px; width: auto; transform: none; top: 16px; }
}

/* ═══ .alert.floating — DEPRECAT (2026-08-26), înlocuit de .toast ═══
   Își hardcoda umbra în loc de --sh-popover, nu avea container de
   stivuire și nu avea politică de dismiss. Rămâne funcțional până la
   îndepărtare. Migrare: .alert.floating.<stare> → .toast.<stare> într-un
   .toast-stack. */
.alert.floating {
  position: fixed; right: 24px; bottom: 24px;
  z-index: 100; max-width: 420px;
  box-shadow: 0 12px 32px rgba(20,16,12,0.14), 0 2px 8px rgba(20,16,12,0.06);
}
@media (max-width: 1023.98px) {
  .alert.floating { left: 16px; right: 16px; max-width: none; }
}

/* ═══ Insight / promise card ═══ */
.insight {
  background: var(--c-pink-ultra);
  border: 1px solid color-mix(in oklab, var(--c-pink) 18%, var(--c-rule));
  border-radius: var(--r-xl); padding: 28px;
}
.insight .lb {
  font-family: var(--font-sans);
  font-size: 11px; font-weight: 600;
  letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--c-pink-deep); margin-bottom: 10px;
}
.insight .h {
  font-family: var(--font-sans); font-weight: 600;
  font-size: 22px; letter-spacing: -0.022em; line-height: 1.25;
  color: var(--c-ink); margin: 0 0 12px;
}
.insight .h em { font-family: var(--font-display); font-style: italic; font-weight: 500; color: var(--c-pink); }
.insight .p {
  font-family: var(--font-sans); font-size: 14px; line-height: 1.6;
  color: var(--c-fog); margin: 0 0 18px;
}
.insight .sig {
  display: flex; align-items: center; gap: 12px;
  padding-top: 16px;
  border-top: 1px solid color-mix(in oklab, var(--c-pink) 14%, transparent);
}
.insight .sig .nm { font-size: 14px; color: var(--c-ink); font-weight: 500; }
.insight .sig .rl { font-size: 12px; color: var(--c-fog); }

/* ═══ Live dot pulse ═══ */
.pulse-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--c-success);
  box-shadow: 0 0 0 0 rgba(31, 122, 77, 0.5);
  animation: pulse-dot 2.4s ease-in-out infinite;
  display: inline-block;
}
@keyframes pulse-dot {
  0%   { box-shadow: 0 0 0 0   rgba(31, 122, 77, 0.45); }
  70%  { box-shadow: 0 0 0 6px rgba(31, 122, 77, 0);    }
  100% { box-shadow: 0 0 0 0   rgba(31, 122, 77, 0);    }
}

/* ═══ Vox FAB ═══ */
.vox-fab {
  position: fixed; bottom: 24px; right: 24px; z-index: 90;
  background: var(--c-dark-grad); color: #fff;
  border: none; border-radius: var(--r-lg);
  padding: 12px 18px;
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  box-shadow: 0 12px 32px -10px rgba(17,13,16,0.35);
  cursor: pointer; transition: transform 140ms;
  font-family: var(--font-sans);
  /* <button> nu moștenește font-size (default-ul browserului e ~13.33px). Baza de
     14 e eticheta FAB-ului, iar .t-actor relativ iese din ea fix 15px. */
  font-size: 14px;
}
@media (hover: hover) {
  .vox-fab:hover { transform: translateY(-2px); }
}
/* `:not(.t-actor)` — altfel rândul ăsta bate .t-actor pe specificitate (0,1,2
   față de 0,1,0) și numele agentului ar lua 14px fix în loc de 1em + 1px, exact
   în markupul canonic, unde .t-actor E primul copil. Mărimea lui .t-actor e
   contract (docs/foundation.md); FAB-ul n-are voie s-o rescrie pe tăcute. */
.vox-fab span:first-child:not(.t-actor) { font-size: 14px; font-weight: 500; }
.vox-fab em { font-family: var(--font-display); font-style: italic; color: var(--c-pink); font-weight: 500; }
.vox-fab .meta { font-size: 11px; color: rgba(255,255,255,0.55); letter-spacing: 0.04em; }
/* Variantă AI (gradient) — semnătura Vox. Negrul e acum distructiv, deci FAB-ul Vox
   folosește gradientul AI ca să nu citească „ștergere". Recomandat pentru Vox. */
.vox-fab.ai { background: var(--c-ai-grad); }
/* Brandul nu se poate purta pe gradientul AI: pinkul lui .t-actor măsoară
   1.47:1 pe stopul de pornire (--c-pink-deep) și 2.38:1 pe cel violet. Nu e
   nici pe departe suficient, la nicio mărime. Albul dă 5.39 / 8.76.
   Excepția e ÎNGUSTĂ, doar pe .ai: pe .vox-fab simplu (--c-dark-grad) pinkul
   e corect — 4.88:1 și 5.46:1 — și rămâne, fiindcă acolo e semnătura de brand.
   Istoric: rândul pentru `em` exista deja, din exact același motiv; markupul
   canonic a trecut pe .t-actor și corectura n-a urmat. Pe vremea aia primul
   stop era --c-pink însuși, deci ieșea pink pe pink, 1.00:1 — vezi nota de la
   --c-ai-grad. Sunt același caz, deci un rând.
   Măsurat cu scripts/check-contrast.mjs. */
.vox-fab.ai em,
.vox-fab.ai .t-actor { color: #fff; }
/* .meta e text discret prin alpha — pe --c-dark-grad rgba(255,255,255,0.55)
   dă 5.94:1 și e corect. Pe gradientul AI, aceeași alpha dă 2.53:1 și 3.77:1:
   pică pe ambele stopuri, chiar și după ce primul stop s-a adâncit. Alpha
   diluează exact contrastul pe care mutarea tokenului tocmai l-a câștigat,
   deci pe .ai discreția o preia MĂRIMEA (11px), nu alpha: alb solid, 5.39 și
   8.76. Măsurat cu scripts/check-contrast.mjs. */
.vox-fab.ai .meta { color: #fff; }

/* ═══ Avatars ═══ */
.avatar {
  width: 36px; height: 36px; border-radius: 50%;
  background: var(--c-pink); color: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-sans); font-size: 11px; line-height: 16px; font-weight: 600;
  flex-shrink: 0;
}
.avatar.lg       { width: 44px; height: 44px; font-size: 13px; line-height: 18px; }
.avatar.sm       { width: 28px; height: 28px; font-size: 11px; }   /* 10 → 11 (2026-09-16): podeaua scalei */
.avatar.dark   { background: var(--c-dark-grad); }
.avatar.tinted { background: var(--c-pink-tint); color: var(--c-pink-deep); }

/* ═══ Tables ═══ */
.tbl { width: 100%; border-collapse: collapse; font-family: var(--font-sans); }
.tbl thead th {
  /* 12px, up from 11px (2026-09-02). The column label is the only thing on the
     screen that tells you what the number under it means; 11px put it below
     every other label in the system. Tracking came down 0.10em → 0.05em in the
     same step: 0.10em was tuned for 11px, and on larger uppercase letters the
     same ratio runs the label optically wide. The header is the one place in
     the DS on 0.05em — .popover-label, .t-eyebrow and .spec-tbl thead are all
     11px and stay on 0.10em. */
  font-size: 12px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--c-fog); text-align: left;
  padding: 10px 22px; background: var(--c-bej-1);
  border-bottom: 1px solid var(--c-rule-soft);
}
.tbl thead th.right { text-align: right; }
.tbl tbody td {
  padding: 14px 22px; font-size: 14px; color: var(--c-ink);
  border-bottom: 1px solid var(--c-rule-soft);
  font-variant-numeric: tabular-nums; letter-spacing: -0.003em;
}
.tbl tbody tr:last-child td { border-bottom: none; }
/* Hover — --c-bej-05, nu --c-bej-1 (2026-09-09). Corpul tabelului e alb, iar
   `.tbl thead th` e bej-1: cât timp hover-ul era tot bej-1, rândul pe care stă
   cursorul lua EXACT valoarea antetului de deasupra lui și se citea ca un al
   doilea cap de tabel. bej-05 e jumătatea drumului — ΔL* 3.39 de la alb, față
   de 6.82 — deci rândul se distinge de corp fără să se confunde cu antetul.
   Bonus de contrast: `.tbl .date` (--c-fog, 13px) urcă de la 3.84:1 pe bej-1 la
   4.19:1 aici. Tot sub AA, dar în sensul bun. */
@media (hover: hover) {
  .tbl tbody tr:hover { background: var(--c-bej-05); }
}
.tbl .right  { text-align: right; }
.tbl .name   { color: var(--c-ink); font-weight: 500; }
.tbl .amount { color: var(--c-ink); font-weight: 500; }
.tbl .date   { color: var(--c-fog); font-size: 13px; }

/* ═══ Data list — WIP · ce devine un tabel sub 1024px ═══
   .tbl are nevoie de lățime; pe telefon fiecare rând devine un card
   (.data-card), fiecare celulă o pereche etichetă (.t11) – valoare (14),
   trei-patru câmpuri cel mult, ultimul la dreapta (suma, 600 tabular).
   Antet opțional de grup (.data-list-hdr: lună · total). Stare goală:
   .data-list-empty — disc cu icon, o propoziție, un primary.
   STATUS WIP (data-list.json, 2026-09-16): perechea tabel → data-list e
   decisă; forma cardului NU — se așază pe un ecran real (lista Facturi) cu
   sortare, filtre, selecție multiplă, paginare și totaluri. Până atunci:
   vizibil, nu de încredere. Suprafața e cea a .list-group (alb,
   --c-rule-card, --r-lg) și o va moșteni explicit când iese din WIP. */
.data-list { display: flex; flex-direction: column; gap: 12px; }
.data-list-hdr {
  display: flex; align-items: baseline; gap: 10px; padding: 4px 0 0;
  font-size: 12px; font-weight: 500; letter-spacing: 0.04em; text-transform: uppercase; color: var(--c-ink);
}
.data-list-hdr .sum { font-weight: 400; text-transform: none; letter-spacing: 0; font-variant-numeric: tabular-nums; color: var(--c-fog); }
.data-card {
  width: 100%; text-align: left;
  background: var(--c-white); border: 1px solid var(--c-rule-card); border-radius: var(--r-lg);
  padding: 16px 20px; display: flex; flex-direction: column; gap: 12px;
  font-family: var(--font-sans); color: var(--c-ink); text-decoration: none;
  -webkit-appearance: none; appearance: none;
}
button.data-card, a.data-card { cursor: pointer; }
@media (hover: hover) {
  button.data-card:hover, a.data-card:hover { border-color: var(--c-input-border-hover); }
}
/* Press (2026-10-15): pe touch hoverul e închis în @media (hover: hover), deci
   :active e singurul răspuns la tap — același token ca hoverul, nu o culoare nouă. */
button.data-card:active, a.data-card:active { border-color: var(--c-input-border-hover); }
.data-card-top   { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.data-card-title { font-size: 15px; font-weight: 600; line-height: 22px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.data-card-grid  { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr) max-content; gap: 12px; }
.data-card-grid.two  { grid-template-columns: minmax(0,1fr) max-content; }
.data-card-grid.four { grid-template-columns: repeat(3, minmax(0,1fr)) max-content; }
.data-field { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.data-field:last-child { text-align: right; }
.data-field > .v { font-size: 14px; line-height: 20px; font-variant-numeric: tabular-nums; }
.data-field:last-child > .v { font-weight: 600; white-space: nowrap; }
/* stare goală — disc cu icon, o propoziție, un primary. Discul e .disc, nu .ic:
   .ic e glifa Lucide dinăuntru. */
.data-list-empty {
  background: var(--c-white); border: 1px solid var(--c-rule-card); border-radius: var(--r-lg);
  padding: 40px 24px; display: flex; flex-direction: column; align-items: center; gap: 16px; text-align: center;
}
.data-list-empty > .disc {
  width: 44px; height: 44px; border-radius: 50%; background: var(--c-bej-1);
  display: flex; align-items: center; justify-content: center; color: var(--c-ink);
}
.data-list-empty > p { margin: 0; font-size: 15px; line-height: 22px; max-width: 26ch; }

/* ═══ Topnav v3 — DEPRECATED ═══
   .v3-topnav is retired. Kept only so legacy/frozen pages still render.
   Do not use in new work; do not reference in docs. */
.v3-topnav {
  position: fixed; bottom: 18px; left: 50%; transform: translateX(-50%); z-index: 95;
  display: flex; gap: 1px;
  background: var(--c-dark); border-radius: var(--r-full); padding: 3px;
  box-shadow: 0 8px 24px rgba(17,13,16,0.22);
}
.v3-topnav a {
  width: 32px; height: 28px;
  display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--font-sans); font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: 0.02em; color: rgba(255,255,255,0.55);
  border-radius: var(--r-full); transition: color 140ms, background 140ms;
}
@media (hover: hover) {
  .v3-topnav a:hover  { color: #fff; }
}
.v3-topnav a.active { background: var(--c-accent); color: #fff; }
.v3-topnav .sep { font-size: 9px; color: var(--c-dark-mid); padding: 0 4px; align-self: center; letter-spacing: 0.10em; }

/* ═══ Modal ═══ */
/*
  Structură HTML:
  <div class="modal-backdrop" onclick="if(event.target===this)closeModal()">
    <div class="modal">
      <button class="btn outline sm icon-only" type="button" aria-label="Închide"><!-- SVG X --></button>
      <div class="t-eyebrow"><span class="dot"></span>Label</div>
      <h2 class="t28">Titlu <em>modal</em></h2>
      <p class="t14" style="margin:8px 0 24px">Descriere opțională.</p>
      <!-- conținut: .field + .input din DS -->
      <button class="btn primary lg" style="width:100%;margin-top:22px">Acțiune</button>
    </div>
  </div>

  JS minimal:
  backdrop.classList.toggle('open')
  document.body.style.overflow = open ? 'hidden' : ''
  document.addEventListener('keydown', e => { if(e.key==='Escape') closeModal() })
*/
.modal-backdrop {
  position: fixed; inset: 0; z-index: 200;
  background: var(--c-backdrop);
  backdrop-filter: blur(var(--scrim-blur)); -webkit-backdrop-filter: blur(var(--scrim-blur));
  display: none; align-items: center; justify-content: center;
  padding: 32px;
  animation: modal-fade 180ms ease-out;
}
.modal-backdrop.open { display: flex; }

@keyframes modal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.modal {
  background: var(--c-bej-1);
  border-radius: var(--r-xl);
  padding: 32px 36px 28px;
  width: 100%; max-width: 480px;
  outline: 1px solid var(--c-white); outline-offset: 16px;
  position: relative;
  animation: modal-in 220ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.modal.sm { max-width: 360px; }
.modal.lg { max-width: 640px; }

@keyframes modal-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Închiderea modalului — butonul din DS: .btn.outline.sm.icon-only.
   Aici stă doar ce ține de CONTAINER, nu de buton: poziția. Butonul nu
   știe unde stă; modalul știe.

   X-ul și „Anulează" fac ACELAȘI lucru: renunță la dialog. Deci sunt același
   buton, în două forme — .btn.outline.sm.icon-only în colț și .btn.outline
   în rândul de acțiuni (întins de grup în .modal-actions.stacked). Un ghost
   la „Anulează" și un buton plin în colț erau două tratamente pentru o
   singură acțiune. (2026-09-03)

   Migrat 2026-09-01 de la clasa proprie .modal-close: 32px nu era pe scara
   26/30/40/44, și își lua fundalul din --c-rule-soft — un token de DIVIDER
   folosit ca fill, aceeași confuzie de rol ca --c-danger vs --c-ink. */
.modal > .btn.icon-only { position: absolute; top: 16px; right: 16px; }

/* ═══════════════════════════════════════════════════════════════
   SHEET — foaia de jos pe mobil (.sheet, < 1024px)
   O singură foaie pentru tot ce urcă de jos pe telefon: meniuri și
   selectoare deschise dintr-un buton sau dintr-un rând (.attached),
   explicații și confirmări care se citesc ca un card (.floating),
   formulare scurte de cel mult două câmpuri (.form). Înlocuiește sub
   1024px .popover și .context-menu DESCHISE DINTR-UN BUTON SAU RÂND;
   .popover ancorat de un câmp (typeahead, combobox, .stepper.mobile)
   rămâne .popover — tastatura e sus, o foaie ar sta sub ea. .modal
   rămâne pentru decizii (T4). Scrimul e cel canonic (--c-backdrop +
   --scrim-blur), ca la .modal-backdrop.
   Poziționare: absolut în shell-ul gazdă (.app-mobile sau .flow-chrome —
   amândouă contexte de stivuire izolate), deci umple și rama de telefon
   din paginile de referință; un .modal-backdrop (fixed, 200) pictează
   oricum deasupra.
   Închiderea e explicită: X (.btn.sm.icon-only, bej-0 opac; alb pe .form)
   + tap pe scrim. FĂRĂ grabber — se citește ca drag și declanșează
   pull-to-refresh.
   Rândurile din foaie sunt .list-row (secțiunea List row), la 44px.
   Aliasuri deprecate (2026-09-16; se scot la următorul major):
   .flow-sheet-scrim → .sheet-scrim · .flow-sheet → .sheet.floating ·
   .flow-sheet.steps → .sheet.attached cu .stepper.vertical · .doc-sheet →
   .sheet.attached cu .doc-list (regulile .doc-sheet* rămân în blocul lor).
   ═══════════════════════════════════════════════════════════════ */
.sheet-scrim, .flow-sheet-scrim {
  position: absolute; inset: 0; z-index: 60;
  background: var(--c-backdrop);
  -webkit-backdrop-filter: blur(var(--scrim-blur)); backdrop-filter: blur(var(--scrim-blur));
  display: flex; flex-direction: column; justify-content: flex-end;
  animation: flowScrimIn .15s ease;
}
.sheet-scrim[hidden], .flow-sheet-scrim[hidden] { display: none !important; }
.sheet, .flow-sheet {
  position: relative; background: var(--c-white);
  animation: flowSheetUp .22s cubic-bezier(.2,.8,.2,1);
}
/* attached (implicit pe mobil) — toată lățimea, colțuri sus, padding jos = safe area.
   Pentru liste de acțiuni și selectoare. */
.sheet.attached {
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 16px 18px calc(var(--safe-bottom, 0px) + 16px);
}
/* floating — margine de 12 de jur împrejur, toate colțurile, ridicată peste
   inset. Pentru explicații și confirmări care se citesc ca un card. */
.sheet.floating, .flow-sheet {
  border-radius: var(--r-xl);
  margin: 12px 12px calc(var(--safe-bottom, 0px) + 12px);
  padding: 16px 18px 18px;
}
.sheet.floating { display: flex; flex-direction: column; gap: 12px; }
/* form — suprafața trece pe bej-0 ca .input.lg / .select.lg albe să se citească
   drept câmpuri pe un fundal. Laturi de 20 = gutterul paginii. Un singur
   .btn.primary.lg.full comite. Cel mult două câmpuri; mai mult e un ecran. */
.sheet.form {
  background: var(--c-bej-0);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 16px var(--layout-pad-mob) calc(var(--safe-bottom, 0px) + 16px);
  display: flex; flex-direction: column; gap: 16px;
}
.sheet.form > .btn.icon-only { background: var(--c-white); }
.sheet.form > .btn.primary { margin-top: 4px; }
/* X — același buton ca la .flow-sheet: .btn.sm.icon-only; containerul dă poziția
   și fundalul opac (migrat 2026-09-01 de la .flow-sheet-close) */
.sheet > .btn.icon-only, .flow-sheet > .btn.icon-only {
  position: absolute; top: 12px; right: 12px; z-index: 1;
  background: var(--c-bej-0); color: var(--c-ink); box-shadow: none;
}
@media (hover: hover) {
  .sheet > .btn.icon-only:hover:not(:disabled), .flow-sheet > .btn.icon-only:hover:not(:disabled) {
    background: color-mix(in srgb, var(--c-bej-0), var(--c-ink) 8%);
  }
}
/* Titlul e .t18 pe un div (foaia e chrome; h1-ul paginii rămâne în ecran), lede-ul
   e .t14 în fog. Foaia dă doar padding-ul, nu tipografia. Dreapta lasă loc X-ului. */
.sheet > .t18 { padding: 4px 48px 10px 6px; }
.sheet.floating > .t18 { padding-bottom: 0; }
.sheet > .t14 { margin: 0; padding: 0 6px; color: var(--c-fog); }
/* Rândurile din foaie: .list-row la 44, aliniate la 6px ca titlul. Foaia e deja
   suprafața, deci grupul își pierde cardul; două grupuri = spațiu de 4px între. */
.sheet .list-group, .flow-sheet .list-group { background: transparent; border: none; border-radius: 0; overflow: visible; }
.sheet .list-group + .list-group { margin-top: 4px; }
.sheet .list-row, .flow-sheet .list-row, .sheet .flow-menu-row, .flow-sheet .flow-menu-row {
  min-height: 44px; padding-left: 6px; padding-right: 6px;
}
@keyframes flowScrimIn {
  from { opacity: 0 } to { opacity: 1 }
}
@keyframes flowSheetUp {
  from { transform: translateY(20px); opacity: 0 }
  to   { transform: translateY(0);    opacity: 1 }
}
@media (prefers-reduced-motion: reduce) {
  .sheet-scrim, .flow-sheet-scrim, .sheet, .flow-sheet { animation: none; }
}

/* ═══ Utilities ═══ */
.stack-xs > * + * { margin-top: var(--sp-4); }
.stack-sm > * + * { margin-top: var(--sp-8); }
.stack    > * + * { margin-top: var(--sp-16); }
.stack-lg > * + * { margin-top: var(--sp-24); }
.stack-xl > * + * { margin-top: var(--sp-40); }

.row    { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.row.sm { gap: 6px; }
.row.lg { gap: 16px; }

.divider        { height: 1px; background: var(--c-rule-soft); border: none; margin: 20px 0; }
.divider.strong { background: var(--c-rule); }

/* ─── Responsive ─── */
@media (max-width: 1023.98px) { .v3-topnav { bottom: 12px; } }

/* ═══ Checkbox & Radio ═══ */

/* Grup vertical */
.check-group, .radio-group {
  display: flex; flex-direction: column; gap: 10px;
}
/* Grup orizontal compact */
.check-group.row, .radio-group.row {
  flex-direction: row; flex-wrap: wrap; gap: 16px;
}

/* Item wrapper — label clickabil.
   ─── Tipul se declară AICI, nu doar pe .check-label (2026-09-21) ───
   Regula .check-label/.radio-label de mai jos dă 15/22, dar ea se aplică doar dacă
   markup-ul poartă clasa. Un `<label class="check-item"><input><span>text</span></label>`
   — forma cea mai scurtă, și exact cea care era în `markup`-ul contractului — nu o
   poartă, deci textul cădea pe font-size-ul MOȘTENIT de la gazdă. Asta a produs trei
   citiri diferite ale aceluiași control: designul cere 15, pagina de documentație
   randa 16 (`body { font-size: 16px }`), iar aplicația randa 14 (baza ei e 14).
   Niciuna nu era un bug de implementare — contractul pur și simplu nu declara mărimea,
   deci fiecare gazdă răspundea cu a ei.

   Un control nu-și poate lua mărimea din pagină: la fel ca .btn (14/600) sau .input
   (15), .check-item/.radio-item își poartă acum propria treaptă. 15/22 = .t-ctrl,
   aceeași scară ca switch-ul, ca omologii să arate sincron. Descrierea rămâne 13/18
   (.sublabel = .t-ctrl-desc). Culoarea intră în același pas și din același motiv:
   un <span> gol moștenea și culoarea gazdei.
   .check-label / .radio-label rămân — acum sunt REDUNDANTE, nu necesare, și merită
   scrise în continuare fiindcă numesc slotul când itemul are și .sublabel. */
.check-item, .radio-item {
  display: inline-flex; align-items: flex-start; gap: 10px;
  cursor: pointer; user-select: none;
  font-family: var(--font-sans); font-weight: 400;
  font-size: 15px; line-height: 22px; letter-spacing: 0;
  color: var(--c-ink);
}
.check-item input, .radio-item input {
  flex-shrink: 0; margin: 2px 0 0; /* aliniere cu prima linie de text */
  appearance: none; -webkit-appearance: none;
  width: 18px; height: 18px;
  background: var(--c-white);
  border: 1.5px solid var(--c-input-border);
  cursor: pointer;
  transition: border-color 140ms, background 140ms, box-shadow 140ms;
  position: relative;
}
.check-item input[type="checkbox"] { border-radius: var(--r-xs); }
.radio-item  input[type="radio"]   { border-radius: 50%; }

/* Hover */
@media (hover: hover) {
  .check-item input:hover:where(:not(:disabled)),
  .radio-item  input:hover:where(:not(:disabled)) {
    border-color: var(--c-input-border-hover);
    background: var(--c-bej-0);
  }
}

/* Checked */
.check-item input[type="checkbox"]:checked,
.radio-item  input[type="radio"]:checked {
  background: var(--c-accent);
  border-color: var(--c-accent);
}

/* Checkmark SVG inline via pseudo */
.check-item input[type="checkbox"]:checked::after {
  content: '';
  position: absolute; inset: 0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='18' height='18' viewBox='0 0 18 18' fill='none'%3E%3Cpath d='M4.5 9L7.5 12L13.5 6' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
}

/* Radio dot */
.radio-item input[type="radio"]:checked::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #fff;
}

/* Focus */
.check-item input:focus-visible,
.radio-item  input:focus-visible {
  outline: 2px solid var(--c-fog);
  outline-offset: 3px;
}

/* Disabled */
.check-item input:disabled,
.radio-item  input:disabled {
  background: var(--c-bej-1);
  border-color: var(--c-rule);
  cursor: not-allowed;
}
.check-item input:disabled:checked,
.radio-item  input:disabled:checked {
  background: var(--c-mist);
  border-color: var(--c-mist);
}
.check-item:has(input:disabled),
.radio-item:has(input:disabled) { cursor: not-allowed; }

/* Error */
.check-item input.error,
.radio-item  input.error {
  border-color: var(--c-error);
  background: var(--c-error-bg);
}
/* Bifat + eroare: boxul de control rămâne roșu — eroarea învinge selecția, la
   fel ca pe card. Altfel fill-ul accent al stării checked ar ascunde eroarea. */
.check-item input.error:checked,
.radio-item  input.error:checked {
  border-color: var(--c-error);
  background: var(--c-error);
}
/* Label roșu în eroare — text <18px → --c-error-text (AA pe bej/alb). Automat,
   fără style inline. Sublabel-ul își păstrează culoarea fog (regula lui e mai
   specifică), ca eroarea să nu devină prea roșie. */
/* Eticheta roșie: și pe slotul numit, și pe wrapper — al doilea selector prinde
   textul scris ca <span> gol, care moștenește culoarea de la .check-item. */
.check-item:has(input.error),
.radio-item:has(input.error),
.check-item:has(input.error) .check-label,
.radio-item:has(input.error) .radio-label {
  color: var(--c-error-text);
}

/* Label text — pe scala .t-ctrl (15/22) + .t-ctrl-desc (13/18),
   comună cu switch-ul, pentru aspect sincron între controale omoloage. */
/* ═══ Label — eticheta unui CONTROL, lângă el.
   NU e .field-label, care e eticheta unui CÂMP, deasupra lui: aia e 12px
   uppercase fog, asta e 15px ink în flux normal. Două lucruri diferite care
   s-au numit amândouă „label" în alte sisteme; aici sunt separate.
   .check-label / .radio-label sunt aceeași regulă, sub numele lor istorice.
   .field-title e alias DECLARAT al lui .label, nu o a doua implementare (ca
   .modal-actions față de .btn-group): titlul unui .field orizontal în care
   titlul NU e un <label> — un grup de checkboxuri, un card de alegere. Numele
   există fiindcă se citește în context; regula e aceeași, la fel și mărimea. ═══ */
.label, .field-title,
.check-label, .radio-label {
  font-family: var(--font-sans); font-weight: 400;
  font-size: 15px; line-height: 22px;
  color: var(--c-ink);
}
.label .sublabel,
.field-title .sublabel,
.check-label .sublabel,
.radio-label .sublabel {
  display: block; font-weight: 400;
  font-size: 13px; line-height: 18px;
  color: var(--c-fog); margin-top: 2px;
}

/* Indeterminate (checkbox only) */
.check-item input[type="checkbox"]:indeterminate {
  background: var(--c-accent); border-color: var(--c-accent);
}
.check-item input[type="checkbox"]:indeterminate::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 9px; height: 2px;
  background: #fff; border-radius: 1px;
  background-image: none;
}

/* Card variant — întreg item-ul e clickabil, chenar accent + fill solid când e selectat.
   Funcționează identic pentru single-line (doar titlu) și 2-line (titlu + .sublabel):
   controlul stă centrat optic pe blocul de text în ambele cazuri. */
.check-item.card,
.radio-item.card {
  display: flex; width: 100%;
  padding: 16px 18px; gap: 12px; align-items: center;
  border: 1.5px solid var(--c-rule-card);
  border-radius: var(--r-lg);
  background: var(--c-white);
  transition: border-color 140ms, background 140ms;
}
/* Pe card controlul e centrat pe blocul de text — anulează margin-top-ul de
   aliniere-la-prima-linie folosit în lista default (.check-item / .radio-item). */
.check-item.card input,
.radio-item.card input { margin: 0; }

/* Hover — regula unificată de hover pe controale de formular (vezi .input,
   .select): border → --c-input-border-hover, fără fill. Doar pe carduri
   normale — neselectate, fără eroare, ne-disabled; cardul selectat/eroare
   își păstrează chenarul. (.doc-item rămâne pe --c-fog — nu e control de
   formular, e o suprafață de listă.) */
@media (hover: hover) {
  .check-item.card:hover:where(:not(:has(input:disabled))),
  .radio-item.card:hover:where(:not(:has(input:disabled))) {
    border-color: var(--c-input-border-hover);
  }
}

/* Selectat — chenar accent + fill SOLID (--c-accent-tint), nu transparent */
.check-item.card:has(input:checked),
.radio-item.card:has(input:checked) {
  border-color: var(--c-accent);
  background: var(--c-accent-tint);
}
.check-item.card:has(input:disabled),
.radio-item.card:has(input:disabled) {
  background: var(--c-bej-1);
  border-color: var(--c-rule);
}
/* Eroare — aceeași citire ca varianta simplă (label + control roșii), pe fundal
   normal; cardul adaugă doar chenarul roșu. Fără fill roșu pe card: radio apare
   în grup, iar un grup întreg umplut cu roșu ar fi prea mult. Ultima regulă →
   învinge selectat/hover. Se activează ca lista: <input class="error">. */
.check-item.card:has(input.error),
.radio-item.card:has(input.error) {
  border-color: var(--c-error);
  background: var(--c-white);
}

/* ═══════════════════════════════════════════════════════════════
   .banner și .page-banner — DEPRECATE (v8), înlocuite de .alert.

   Erau două componente pentru o singură idee, și au driftat exact așa
   cum o fac două componente care ar trebui să fie una: markup canonic
   care își contrazicea propria regulă, borderul de eroare doar pe una,
   `fixed` în documentație și `relative` în cod. Acum:

     .banner            → .alert            (inline)
     .page-banner       → .alert.page       (full-bleed, sus)
     .banner-body/-text → .alert-body
     .banner-actions    → .alert-actions
     .banner-dismiss    → .alert-dismiss
     .info / .warn      → .neutral / .warning
     .page-banner.dark  → .alert.page.brand (suprafața dark iese din MVP)

   Blocul de mai jos e SELF-CONTAINED intenționat: nu se mai sprijină pe
   regulile comune ale familiei, ca să poată fi șters într-o bucată la
   îndepărtare — nu mai devreme de v10 (VERSIONING.md). Nu adăuga nimic
   aici și nu-l folosi în cod nou.
   ═══ */
.banner {
  width: 100%;
  border-radius: var(--r-md); padding: 14px 16px;
  display: flex; align-items: flex-start; gap: 12px;
  font-family: var(--font-sans); font-size: 14px; line-height: 1.5;
}
.banner .ic { flex-shrink: 0; width: 16px; height: 16px; margin-top: 1px; }
.banner-body {
  flex: 1; min-width: 0;
  display: flex; align-items: flex-start; gap: 12px;
}
.banner-text { flex: 1; min-width: 0; }
.banner strong { color: inherit; font-weight: 600; }
.banner a { color: inherit; text-decoration: underline; opacity: 0.85; }
@media (hover: hover) {
  .banner a:hover { opacity: 1; }
}
.banner .banner-actions {
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px;
}
.banner .banner-actions .btn { flex-shrink: 0; }
.banner-dismiss,
.page-banner-dismiss {
  flex-shrink: 0; box-sizing: border-box;
  width: 24px; height: 24px; border-radius: var(--r-xs);
  background: transparent; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  color: currentColor; opacity: 0.55; padding: 0;
  margin-top: -1px; margin-right: -4px;
  transition: opacity 120ms, background 120ms;
}
@media (hover: hover) {
  .banner-dismiss:hover,
  .page-banner-dismiss:hover {
    opacity: 1;
    background: color-mix(in oklab, currentColor 10%, transparent);
  }
}

.banner.info    { background: var(--c-bej-1);        color: var(--c-ink); }
.banner.success { background: var(--c-success-bg);   color: var(--c-success-text); }
.banner.warn    { background: var(--c-warn-bg);       color: var(--c-warn-text); }
.banner.error   { background: var(--c-error-bg);      color: var(--c-error-text); border: 1px solid var(--c-error); }
.banner.brand   {
  background: var(--c-white);
  color: var(--c-fog);
  border: 1px solid var(--c-rule);
  border-left: 3px solid var(--c-pink);
  padding: 13px 15px 13px 13px;
}
.banner.brand strong { color: var(--c-pink-deep); }
.banner.brand .ic    { color: var(--c-pink-deep); }

.page-banner {
  position: relative; width: 100%;
  padding: 12px 48px 12px 20px;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  font-family: var(--font-sans); font-size: 14px; font-weight: 500;
  line-height: 20px; text-align: center;
}
.page-banner .ic { flex-shrink: 0; width: 16px; height: 16px; }
.page-banner-body { min-width: 0; }
.page-banner-dismiss {
  position: absolute; right: 12px; top: 50%; transform: translateY(-50%);
  width: 28px; height: 28px; margin: 0;
}
.page-banner strong { color: inherit; font-weight: 700; }
.page-banner a { text-decoration: underline; opacity: 0.8; }
@media (hover: hover) {
  .page-banner a:hover { opacity: 1; }
}

.page-banner.info    { background: var(--c-bej-1);        color: var(--c-ink); }
.page-banner.success { background: var(--c-success-bg);   color: var(--c-success-text); }
.page-banner.warn    { background: var(--c-warn-bg);       color: var(--c-warn-text); }
.page-banner.error   { background: var(--c-error-bg);      color: var(--c-error-text); }
.page-banner.brand   { background: var(--c-pink-tint);     color: var(--c-pink-deep); }
.page-banner.dark    { background: var(--c-dark-grad);     color: #fff; }

/* ═══════════════════════════════════════════════════════════════
   Stepper — proces liniar compact, inline cu header.
   3-5 pași maxim, label scurt.
   Anatomie: DOUĂ clase. Fiecare pas e un indicator rotund
   (.stepper-ind) plus eticheta; numărul stă direct în chip.
   Indicatorul poartă starea, pasul poartă eticheta:
   .done (check --c-success pe tint --c-success-bg, label ink) →
   .active (număr alb pe --c-pink solid, label pink-deep bold) →
   upcoming (număr fog pe alb cu ring --c-rule, label fog).
   Aceleași clase și stări pe ambele platforme: lista de pași din
   .flow-sheet.steps e un .stepper.vertical, nu un vocabular paralel.
   ═══════════════════════════════════════════════════════════════ */
.stepper {
  display: inline-flex; align-items: center; gap: 28px;
  font-family: var(--font-sans);
}
.stepper-step {
  display: inline-flex; align-items: center; gap: 8px;
  background: transparent; border: none; padding: 0;
  font-family: inherit; font-size: 14px; line-height: 20px; font-weight: 500;
  color: var(--c-fog); cursor: pointer;
  transition: color 140ms;
}
@media (hover: hover) {
  .stepper-step:hover:not(:disabled) { color: var(--c-ink); }
}
.stepper-step:disabled { cursor: not-allowed; }
.stepper-step.done   { color: var(--c-ink); }
@media (hover: hover) {
  .stepper-step.done:hover { color: var(--c-ink); }
}
.stepper-step.active { color: var(--c-pink-deep); font-weight: 600; }
@media (hover: hover) {
  .stepper-step.active:hover { color: var(--c-pink-deep); }
}

/* Indicatorul de pas — chip rotund, aceleași valori în orice layout.
   Upcoming e alb cu ring, nu bej: suprafața gazdă e albă (bara de flux sau
   sheet-ul de pași), iar fog pe alb e singura pereche care trece AA. */
.stepper-ind {
  width: 28px; height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; line-height: 1;
  background: var(--c-white);
  box-shadow: inset 0 0 0 1px var(--c-rule);
  color: var(--c-fog);
  transition: background 140ms, box-shadow 140ms, color 140ms;
}
/* Done = tratament tonal: tint verde + check verde, fără contur. Fill-ul solid
   rămâne rezervat pasului curent, ca „ești aici" să fie singurul semnal plin
   din bară. --c-success e tokenul de fill/stroke al familiei, deci corect pe un
   glif (4.89:1 pe --c-success-bg, peste pragul de 3:1 pentru non-text).
   Ring-ul e doar pe upcoming: acolo chipul e alb pe bară albă și fără contur
   ar dispărea. Tint-ul verde se citește singur, deci nu-i trebuie. */
.stepper-step.done .stepper-ind {
  background: var(--c-success-bg); box-shadow: none; color: var(--c-success);
}
.stepper-step.active .stepper-ind {
  background: var(--c-pink); box-shadow: none; color: #fff;
}

/* ─── Vertical layout — aceiași pași, pe rânduri în loc de orizontal.
   Două gazde: .stepper.vertical (lista din .flow-sheet.steps, pe mobil) și
   .stepper-menu (dropdown-ul variantei .mobile). Un singur set de reguli,
   ca stările să nu poată deriva între layout-uri. ─── */
.stepper.vertical {
  display: flex; flex-direction: column; align-items: stretch; gap: 2px;
}
.stepper.vertical .stepper-step,
.stepper-menu .stepper-step {
  display: flex; width: 100%;
  justify-content: flex-start; text-align: left;
  transition: background 120ms, color 140ms;
}
/* Rânduri de listă — modelate după .sidebar a: radius r-lg, hover soft.
   Label 15px și chip 24px: e o listă de tap targets, nu o bară de chrome. */
.stepper.vertical .stepper-step {
  gap: 13px; padding: 11px 8px;
  border-radius: var(--r-lg); font-size: 15px;
}
@media (hover: hover) {
  .stepper.vertical .stepper-step:hover:not(:disabled) { background: var(--c-rule-soft); }
}
.stepper.vertical .stepper-ind { width: 24px; height: 24px; font-size: 12px; }

/* ─── Mobile variant — colapsează la trigger + dropdown
   Trigger-ul nu are chrome propriu — trăiește într-un container dedicat
   (page header) și-i moștenește suprafața. ─── */
.stepper.mobile { position: relative; display: inline-block; }
.stepper-trigger {
  display: inline-flex; align-items: center; gap: 8px;
  background: transparent; border: none; padding: 0;
  font-family: var(--font-sans); font-size: 13px; line-height: 18px; font-weight: 600;
  color: var(--c-pink-deep);
  cursor: pointer;
}
.stepper-trigger > svg { flex-shrink: 0; }
/* Trigger-ul arată mereu pasul curent, deci chipul lui e cel activ —
   aceleași valori ca .stepper-step.active .stepper-ind. */
.stepper-trigger .stepper-ind {
  width: 22px; height: 22px; font-size: 11px;
  background: var(--c-pink); box-shadow: none; color: #fff;
}
.stepper-trigger .stepper-count {
  font-weight: 500; color: var(--c-fog);
  font-variant-numeric: tabular-nums;
  padding-left: 8px;
  border-left: 1px solid var(--c-rule);
  margin-left: 2px;
}
.stepper-trigger .chev {
  color: var(--c-fog); transition: transform 160ms;
  transform: rotate(0deg);   /* REGULA CHEVRONULUI — vezi `.select-trigger > .chev` */
}
.stepper.mobile.open .stepper-trigger .chev { transform: rotate(180deg); }

/* ═══════════════════════════════════════════════════════════════
   Popover — SUPRAFAȚA CANONICĂ pentru orice overlay ancorat de un
   declanșator: dropdown menu, popover, lista de typeahead/combobox,
   dropdown-ul .stepper.mobile.

   Un singur set de reguli, două gazde de nume — la fel ca perechea
   .stepper.vertical / .stepper-menu de mai sus. .stepper-menu NU are
   suprafață proprie: dacă ar avea-o, ar deriva de popover exact cum
   .badge din aplicație a derivat de .badge din DS.

   Originea: .stepper-menu, care era singurul overlay ancorat desenat
   în sistem. Diferența față de el, decisă la 2026-08-26: radius cu o
   treaptă mai sus la ambele niveluri — suprafața md→lg, rândurile
   xs→md — ca meniul să nu citească mai ascuțit decât inputul care îl
   deschide (.input e deja --r-lg). ═══ */
.popover,
.stepper-menu {
  position: absolute; top: calc(100% + 8px); left: 0; z-index: 20;
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-lg);
  padding: 6px; min-width: 200px;
  box-shadow: var(--sh-popover);
  display: none; flex-direction: column; gap: 2px;
}
.popover.is-open { display: flex; }
.stepper.mobile.open .stepper-menu { display: flex; }

/* ─── .popover.popper — suprafața DS, poziționarea altcuiva ───
   Forma React montează lista prin Radix, care o portalează în <body> și o
   așază cu Floating UI. Radix pune stilurile de poziție pe WRAPPER-ul lui
   (`[data-radix-popper-content-wrapper]`), nu pe elementul de conținut — iar
   conținutul e chiar cel care poartă `.popover`. Fără modificatorul ăsta,
   `position: absolute; top: calc(100% + 8px); left: 0` din regula de bază s-ar
   aplica ÎNĂUNTRUL wrapper-ului deja poziționat și ar împinge meniul 8px în jos
   și la stânga față de unde l-a pus biblioteca — un offset care arată ca un bug
   de ancorare și se „repară" de obicei cu un `style` inline.
   `display: flex` e tot aici fiindcă montarea o decide Radix (montează și
   demontează), nu clasa `.is-open`. Suprafața, radius-ul, padding-ul, umbra și
   rândurile rămân ale DS-ului — se schimbă doar cine ține coordonatele. */
.popover.popper {
  position: relative;
  top: auto; left: auto; right: auto;
  display: flex;
}

/* Ancorare la dreapta declanșatorului. Baza e `left: 0`, ceea ce e corect când
   butonul e la începutul containerului lui — și greșit când e la capătul lui:
   meniul pleacă spre stânga și nu mai are nicio margine comună cu ce l-a
   deschis. Cazul canonic e un buton în `.input-group-addon.inline-end`
   (selectorul de monedă), dar apare la fel pe orice acțiune aliniată dreapta.

   `left: auto` e obligatoriu, nu decorativ: cu `left: 0` din bază plus un
   `right: 0` adăugat peste, poziționarea e supra-constrânsă și în LTR câștigă
   `left` — adică un `right: 0` scris inline NU face nimic și pare un bug al
   popoverului. De asta modificatorul există: ca alinierea să fie o clasă
   verificabilă, nu un style inline care tace când e ignorat. */
.popover.align-end,
.stepper-menu.align-end { left: auto; right: 0; }

/* Conținut liber (nu o listă de acțiuni) — respiră mai mult și nu are
   gap între copii, fiindcă nu sunt rânduri de meniu. */
.popover.content { padding: 16px; gap: 0; }
/* Într-un flex column, un buton s-ar întinde pe toată lățimea. Într-o listă de
   acțiuni asta e corect; într-un popover de conținut, nu — butonul e o acțiune,
   nu un rând. */
.popover.content > .btn { align-self: flex-start; }

/* Rândul de meniu. Aceleași metrici ca pasul din .stepper-menu, din
   același motiv ca suprafața: e un singur lucru. */
.popover-item,
.stepper-menu .stepper-step {
  padding: 8px 10px;
  border-radius: var(--r-md);
}
/* Tipul rândului de meniu: 14/20, și el CÂȘTIGĂ peste .check-item / .radio-item
   (15/22) pe un rând care le poartă pe amândouă — un meniu de filtre cu
   `class="popover-item check-item"`, sau markupul de monedă de dinainte de v8.2
   (`popover-item radio-item`), care se randează corect și azi. Aceeași specificitate
   (o clasă fiecare), deci decide ORDINEA: regula asta e declarată după cea a
   controalelor, ca în nota de la .popover-item.is-active. Dependența e
   intenționată, nu accidentală — un rând de meniu e un rând de meniu oriunde,
   iar un singur rând urcat la 15 lângă frații lui pe 14 ar fi exact neregula pe
   care declararea tipului pe .check-item/.radio-item o repară în rest. NU muta
   blocul ăsta deasupra secțiunii de checkbox/radio. */
.popover-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: transparent; border: none; cursor: pointer;
  font-family: var(--font-sans); font-size: 14px; line-height: 20px;
  color: var(--c-ink); text-align: left;
  transition: background 120ms, color 140ms;
}
/* Hover on a menu row is --c-bej-1, the single hover surface for menu-type
   surfaces (decided 2026-09-02, after the side-by-side with the production
   component set). --c-bej-0 (#FBFAF7) on a white popover is a 2% step — at
   that distance the row does not read as reachable, which is why the app
   picked bej-1 on its own. --c-rule-soft, the third variant in circulation,
   is transparent grey: inside a popover it inherits whatever sits underneath
   and reads as dirty grey, so it is not a candidate for a floating surface. */
@media (hover: hover) {
  .popover-item:hover:where(:not(:disabled)),
  .stepper-menu .stepper-step:hover:where(:not(:disabled)) { background: var(--c-bej-1); }
}
/* Press (2026-10-15): pe touch hoverul e închis în @media (hover: hover), deci
   :active e singurul răspuns la tap — același token ca hoverul, nu o culoare nouă. */
.popover-item:active:where(:not(:disabled)),
.stepper-menu .stepper-step:active:where(:not(:disabled)) { background: var(--c-bej-1); }
/* ═══ DOUĂ stări, DOUĂ nume — REVIZUIT 2026-09-21 ═══
   Până aici, `.is-active` purta două lucruri diferite pe același selector:
   VALOAREA ALEASĂ dintr-un select și RÂNDUL SUB CURSORUL DE TASTATURĂ dintr-un
   typeahead (aria-activedescendant). Nota din 2026-09-03 care a respins bifa
   spunea chiar asta — „it collided with .typeahead" — dar a tratat coliziunea ca
   pe un motiv să existe UN singur semnal, nu ca pe un nume suprasolicitat. Era a
   doua interpretare: două stări care se comportă diferit (una e a DATELOR și
   persistă, cealaltă e a TASTATURII și se mută la fiecare săgeată) nu pot împărți
   un selector. Aceeași reparație ca .label / .field-label și --c-error /
   --c-error-text: ce e diferit primește nume diferit.

     .popover-item.is-selected  valoarea aleasă — bifă de brand la capăt + weight 600
     .popover-item.is-active    cursorul de tastatură — suprafață neutră

   Odată despărțite, bifa nu mai e „a doua afirmație a aceluiași fapt": e SINGURA
   afirmație a unui fapt care înainte n-avea semnal propriu. ─── */

/* ─── Valoarea ALEASĂ — bifă de brand + weight 600, fără fundal și fără culoare ───
   Rândul ales nu-și ia fundal și nu-și schimbă culoarea textului. Semnalul de
   culoare e bifa de la capăt, ca în shadcn (`SelectPrimitive.ItemIndicator`);
   greutatea rămâne ca al doilea semnal, pe text.

   Ce se schimbă față de 2026-09-03: fundalul --c-pink-tint și textul
   --c-pink-deep dispar. Weight 500 RĂMÂNE. Motivul de atunci — „un fundal tonal
   ar fi al treilea bej de ordonat pentru ochi" — rămâne adevărat, și tocmai de
   asta rândul nu primește NICIUN fundal acum: problema nu se rezolvă mutând
   canalul pe brand, ci scoțând complet suprafața din joc. Brandul rămâne în
   sistem, dar concentrat într-un obiect de 16px în loc de o bandă pe toată
   lățimea rândului.

   De ce greutatea rămâne, deși bifa e deja un semnal: bifa trăiește la CAPĂTUL
   rândului, iar textul e la început. Într-un meniu lat ochiul citește eticheta și
   ar trebui să traverseze până în dreapta ca să afle dacă rândul ăla e cel ales.
   Greutatea marchează chiar cuvântul citit. Nu e „a doua enunțare a aceluiași
   fapt" în sensul din 2026-09-03 — aia era despre două semnale în ACELAȘI canal
   (fundal colorat plus glif colorat); astea sunt canale diferite, la capete
   diferite ale rândului. Și nu mișcă nimic: greutatea e singura proprietate de
   text care se schimbă, iar slotul bifei e deja rezervat pe toate rândurile.

   Consecință: hover-ul NU se mai retrage pe rândul ales. Regula de dinainte îl
   proteja fiindcă rândul avea o culoare de apărat; acum n-are, deci rândul ales
   se comportă normal sub cursor (--c-bej-1) și bifa rămâne vizibilă peste. Asta
   e și comportarea din shadcn.

   Culoarea bifei e --c-accent, tokenul de brand. E o iconiță, deci pragul e 3:1
   (WCAG 1.4.11, non-text), nu 4.5: măsoară 3.67:1 pe popover-ul alb, 3.09:1 pe
   rândul hover-at (--c-bej-1) și 3.13:1 pe tint, dacă rândul ales e și sub
   cursorul de tastatură. Toate trec, dar 3.09 e podeaua — dacă --c-accent sau
   --c-bej-1 se re-valorizează vreodată, perechea asta e prima care cade. */
/* Selectorul poartă și părintele DINADINS: `.popover-item > svg` (0-1-1) fixează
   --c-fog pe orice iconiță din rând și ar bate un `.popover-check` simplu (0-1-0),
   deci bifa ar ieși gri. Aici e 0-2-1 și câștigă. Singura regulă care trece peste
   ea e a rândului dezactivat. */
.popover-item > .popover-check {
  position: absolute;
  inset-inline-end: 10px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--c-accent);
  flex-shrink: 0;
}
/* Ancora bifei. Pe rând, nu pe popover: bifa se aliniază la rândul ei. */
.popover-item { position: relative; }
/* Greutatea rândului ales: 600. `[data-state="checked"]` e alias, nu redundanță:
   Radix pune atributul ăla pe `SelectPrimitive.Item`, iar componenta React nu
   adaugă și clasa — fără el, forma React ar rămâne pe 400 în timp ce HTML-ul
   scris de mână e îngroșat, adică exact felul de diferență între cele două forme
   pe care react/ există ca s-o închidă. Aceeași pereche de selectori ca la
   ascunderea bifei. */
.popover-item.is-selected,
.popover-item[data-state="checked"] { font-weight: 600; }
/* În HTML simplu bifa stă în markup pe TOATE rândurile listei și se aprinde doar
   pe cel ales — așa slotul e rezervat chiar și de motoarele fără :has(). În React
   Radix montează `ItemIndicator` numai pe rândul ales, deci regula asta n-are ce
   ascunde acolo; ambele forme rămân corecte. */
.popover-item:not(.is-selected):not([data-state="checked"]) > .popover-check { visibility: hidden; }
/* Slotul se rezervă pe TOATE rândurile unui meniu care poate purta o bifă, nu
   doar pe cel ales: altfel meniul își schimbă lățimea când selecția sare de pe
   rândul cel mai lung pe altul, iar textul rândului ales s-ar strânge sub bifă.
   Exact ce face `pr-8` în shadcn, din exact același motiv. */
.popover:has(.popover-check) .popover-item { padding-inline-end: 34px; }
.popover.compact:has(.popover-check) .popover-item { padding-inline-end: 30px; }

/* ─── Cursorul de TASTATURĂ — suprafață NEUTRĂ ───
   `.is-active` înseamnă UN singur lucru: rândul pe care stă
   aria-activedescendant într-un typeahead sau combobox. Nu e o valoare aleasă,
   e unde a ajuns săgeata — se mută la fiecare apăsare și nu supraviețuiește
   închiderii listei.

   REVIZUIT 2026-09-21 (a doua oară în aceeași zi): era --c-pink-tint +
   --c-pink-deep + weight 500. Argumentul pentru tint era că e singurul fel în
   care „unde e tastatura" se distinge de „unde e mouse-ul". Regula care îl bate
   e mai simplă și se aplică la tot meniul: CULOAREA ÎNTR-UN RÂND DE LISTĂ
   ÎNSEAMNĂ „ASTA E VALOAREA ALEASĂ", și nimic altceva. Un cursor de tastatură
   care poartă o bandă de brand spune același lucru ca o valoare aleasă, în alt
   loc — adică exact confuzia din care a ieșit despărțirea .is-selected /
   .is-active. Brandul rămâne concentrat în bifă.

   Deci cursorul ia --c-bej-1: ACEEAȘI suprafață ca hover-ul, care e și
   comportarea din shadcn (`data-[selected=true]:bg-accent` pe CommandItem e
   același accent ca hover-ul). Preț declarat: rândul de sub săgeată și rândul de
   sub mouse arată la fel. În practică nu concurează — cine navighează cu
   tastatura nu ține și mouse-ul pe listă — iar un ecran care chiar are nevoie să
   le despartă suprimă hover-ul cât timp se navighează cu tastatura (o clasă pe
   listă și `:hover` exclus), nu inventează o a treia culoare.

   Textul și greutatea revin la normal: pe o suprafață neutră n-au ce semnala, iar
   weight 600 e de acum semnalul valorii alese. Nu se declară nimic pentru ele —
   rândul moștenește ce are orice rând. */
/* `[data-selected="true"]` e atributul pe care cmdk îl pune pe rândul de sub
   cursor în react/command.tsx — alias pentru `.is-active`, ca
   `[data-state="checked"]` pentru `.is-selected`: fără el forma React a
   comenzii n-ar avea cursor de tastatură. */
.popover-item.is-active,
.popover-item[data-selected="true"] { background: var(--c-bej-1); }
/* A menu row that IS a choice — a native DS radio/checkbox inside .popover-item
   (a multi-choice filter; the pre-v8.2 currency markup, which still renders).
   A SINGLE choice from a closed list is not drawn this way any more: it is the
   select's own list — .is-selected + .popover-check at the end of the row
   (components/select.json). The currency menu moved to it in v8.2, at both of
   its sizes. The control comes from
   .radio-item / .check-item unchanged; the only thing the row cancels is the
   `margin-top: 2px` those carry to align with the FIRST LINE of a multi-line
   label. A menu row is one line and .popover-item centers it, so the offset is
   a 1px misalignment against the row's other children. Same cancellation, same
   reason as .radio-item.card input. */
.popover-item > input[type="radio"],
.popover-item > input[type="checkbox"] { margin: 0; }
/* `[data-disabled="true"]`: rândul cmdk e un <div role="option">, nu un <button>,
   deci `:disabled` nu-l atinge — atributul e cum spune cmdk „dezactivat". */
.popover-item:disabled,
.popover-item[data-disabled="true"] { color: var(--c-mist); cursor: not-allowed; }
.popover-item > svg { flex-shrink: 0; color: var(--c-fog); }

/* Etichetă de grup și separator — structura unui meniu lung. */
/* Mărimea (11px) vine din podeaua scalei — grupat cu .t11 în secțiunea TIPOGRAFIE.
   Aici doar ce e specific: greutate 600, tracking 0.10em, padding-ul de rând. */
/* `.command [cmdk-group-heading]` e alias, nu redundanță: cmdk randează antetul
   grupului cu atributul lui și fără className, deci forma React a comenzii
   n-ar putea purta clasa — același motiv pentru care `[data-state="checked"]`
   stă lângă `.is-selected`. */
.popover-label,
.command [cmdk-group-heading] {
  font-weight: 600;
  letter-spacing: 0.10em;
  padding: 8px 10px 4px;
}
.popover-sep { height: 1px; background: var(--c-rule-soft); margin: 4px 0; flex-shrink: 0; }

/* ═══════════════════════════════════════════════════════════════
   Typeahead & Combobox — .input + .popover, fără chrome propriu.

   Regula care ține componenta onestă: câmpul E .input, neschimbat.
   Nu are înălțime proprie, nici bordură proprie, nici radius propriu
   — altfel devine un al doilea input care derivă de primul. Tot ce
   adaugă componenta e ancorarea listei și starea „niciun rezultat".

   typeahead = text liber care filtrează  ·  combobox = alegere dintr-o
   listă închisă, deci arată ca .select (chevron) dar deschide .popover.
   ═══ */
.typeahead { position: relative; display: flex; flex-direction: column; gap: 4px; }
.typeahead > .popover {
  top: calc(100% + 6px); left: 0; right: 0;
  min-width: 0;
  max-height: 280px; overflow-y: auto;
}
/* Câmpul cu label deasupra: .field dă gap-ul, .typeahead ancorează. */
.field.typeahead > .popover { top: calc(100% + 6px); }

/* Substringul care a produs potrivirea. Accent, nu bold pe tot rândul —
   rândul rămâne citibil ca text, marcajul spune doar DE CE e acolo. */
.typeahead-match { background: transparent; color: var(--c-accent-deep); font-weight: 600; }

/* Rând secundar sub eticheta opțiunii (cod fiscal, email, categorie). */
.popover-item .typeahead-meta {
  display: block; font-size: 12px; line-height: 16px; color: var(--c-fog);
}
/* On a selected row the meta follows the row's colour. --c-fog hardcoded here
   does not inherit, and on --c-pink-tint it measures 3.88:1 — below AA at 12px
   (it was 4.37 on the old bej-0 fill, itself a known-baseline pair). Everything
   in a selected row is --c-pink-deep (4.58:1); the hierarchy between label and
   meta comes from size and weight, 14/500 vs 12/400, which is what separates
   them on every other row anyway. Colour was doing double duty. */
/* Meta pe rândul de sub săgeată: nicio excepție. Rândul e --c-bej-1, adică exact
   suprafața de hover, deci meta se comportă acolo cum se comportă pe orice rând
   hover-at — rămâne --c-fog. Regula care îl ducea pe --c-pink-deep a căzut odată
   cu tintul (2026-09-21). Contrast: 3.84:1 la 12px, sub AA — dar e perechea
   fog × bej-1 deja declarată în tests/error-tokens/baseline.json, nu una nouă,
   și e aceeași pe care o are orice rând de listă sub mouse. */
.typeahead-empty,
.command-empty {
  padding: 14px 10px; text-align: center;
  font-family: var(--font-sans); font-size: 14px; line-height: 20px;
  color: var(--c-fog);
}

/* Combobox — ACELAȘI .input, plus chevronul lui .select. SVG-ul e copiat
   literal din .select ca să nu existe două săgeți în sistem; dacă se
   schimbă una, se schimbă amândouă în același commit. */
.input.has-chevron {
  padding-right: 36px;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6L8 10L12 6' stroke='%237A7570' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
}
.typeahead.is-open .input.has-chevron { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M12 10L8 6L4 10' stroke='%237A7570' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }

/* ═══════════════════════════════════════════════════════════════
   Command — .input + rânduri de .popover, într-o listă care se filtrează.
   shadcn îl numește Command (pe cmdk): un câmp care filtrează o listă de
   comenzi sau destinații, inline sau într-un dialog (⌘K). Primul consumator
   e căutarea din acest catalog (components/_search.js).

   Nu are chrome propriu, deliberat — aceeași regulă ca la typeahead: câmpul
   E .input, rândul E .popover-item, eticheta de grup E .popover-label,
   separatorul E .popover-sep, starea goală E .command-empty (= .typeahead-empty).
   Ce adaugă componenta e doar CARCASA: rândul de căutare de sus
   (.command-input), lista derulabilă (.command-list) și subsolul cu
   scurtături (.command-footer).

   Față de typeahead: acolo lista PLUTEȘTE sub un câmp din pagină (.popover
   ancorat, --sh-popover); aici lista nu plutește — ori e o suprafață inline
   în flux, ori stă într-un .modal, care are deja scrim. Deci nici umbră, nici
   ancorare. Rândul activ e cursorul de tastatură (.is-active), nu o valoare:
   comenzile sunt verbe, n-au .is-selected și nici bifă.
   ═══ */
.command {
  display: flex; flex-direction: column;
  width: 100%;
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-lg);
  overflow: hidden;
  font-family: var(--font-sans);
}

/* Rândul de căutare — e un CÂMP, deci are geometria lui .input-group: padding
   lateral 16 (20 la .lg), iconița 16, gap 8 → textul începe la 40 (44), ca în
   orice căutare din sistem. Fără scurtătură la capăt: în dialog capătul e al X-ului,
   iar Esc stă în subsol (.command-footer).
   Carcasa ia bordura (doar linia de jos), controlul o pierde — aceeași regulă.
   Fără halo pe focus: suprafața care are focusul e comanda însăși — un inel în
   jurul câmpului ar desena o a doua cutie în prima, același motiv pentru care
   .select-trigger.bare din addon nu-și pune halo.
   Rândurile se aliniază pe COLOANA ICONIȚELOR: 6 (listă) + 10 (rând) = 16, sub
   iconița câmpului. Textul lor e la 42, nu la 40 — gap-ul rândului de meniu e 10,
   al câmpului 8; fiecare își ține regula, iconițele sunt cele care fac coloana. */
.command-input {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 16px;
  border-bottom: 1px solid var(--c-rule-soft);
  color: var(--c-fog);                      /* iconița de căutare */
}
.command-input > .ic { flex-shrink: 0; }
.command-input > .input {
  flex: 1; min-width: 0;
  padding: 0;
  border: 0; border-radius: 0;
  background: transparent; box-shadow: none;
}
.command-input > .input:focus,
.command-input > .input:focus-visible,
.command-input > .input.is-focused { border: 0; box-shadow: none; outline: none; }

/* Lista — aceeași geometrie ca .popover: padding 6, gap 2, deci rândul la
   --r-md (20 − 6 = 14). Derulează ea, nu comanda, ca rândul de căutare și
   subsolul să rămână pe loc. 300px = aceeași înălțime ca lista cmdk din shadcn. */
.command-list {
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px;
  max-height: 300px; overflow-y: auto;
  scroll-padding-block: 6px;
}
/* Grupul (etichetă + rândurile ei) e o coloană cu același gap. `[cmdk-list-sizer]`
   și `[cmdk-group-items]` sunt wrapperele pe care le pune cmdk — aceleași
   coloane, fără className. */
.command-group,
.command [cmdk-list-sizer],
.command [cmdk-group-items] { display: flex; flex-direction: column; gap: 2px; }
/* cmdk ascunde un grup rămas fără rânduri cu atributul `hidden` — pe care
   `display: flex` de mai sus l-ar bate. */
.command-group[hidden],
.command [hidden] { display: none; }

/* Scurtătura de la capătul rândului — TEXT, nu badge. Rândul are deja eticheta
   lui, iar o pastilă la capăt s-ar citi ca a doua etichetă (sau ca un status);
   aici e un indiciu. Badge-ul alb rămâne scurtătura dintr-un CÂMP (addon-ul unui
   .input-group, rândul de căutare). Mono 11 fog — ca CommandShortcut din shadcn. */
.command-shortcut {
  margin-left: auto; padding-left: 10px; flex-shrink: 0;
  font-family: var(--font-mono); font-size: 11px; line-height: 16px;
  letter-spacing: 0.06em; color: var(--c-fog);
  font-variant-numeric: tabular-nums;
}

/* Subsolul — legenda tastelor. Tastele în ink, cuvintele în fog; 12px = .t12,
   nu sub podea. Opțional: inline, fără; în dialog, de obicei cu. */
.command-footer {
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  padding: 8px 16px;
  border-top: 1px solid var(--c-rule-soft);
  font-family: var(--font-sans); font-size: 12px; line-height: 16px;
  color: var(--c-fog);
}
.command-footer kbd {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  color: var(--c-ink);
}

/* Declanșatorul (⌘K) — intrarea în paletă, în forma câmpului de căutare. Rolul
   lui e al unui CÂMP („aici cauți"), deci poartă câmpul: .input-group cu iconița
   `search` și scurtătura (badge alb, .badge.sm.outline) în addon-uri, exact căutarea din .input-group. Dar
   controlul e un <button class="input">, nu un <input>: aici nu se scrie — click
   (sau ⌘K) deschide dialogul, unde se scrie. Un <input readonly> ar anunța un câmp
   de text în care tastele nu fac nimic.
   Carcasa ține bordura, hover-ul și halo-ul (:focus-within) cu valorile lui
   .input, ca orice .input-group. Aici stă doar ce un buton n-are de la sine:
   textul la stânga, pe --c-mist (e placeholder-ul: un buton n-are ::placeholder),
   tăiat cu elipsă, și ținta de click pe toată înălțimea. cursor: text — promisiunea
   se ține: click-ul te lasă scriind, în câmpul din dialog. */
.input-group.command-trigger { cursor: text; }
.input-group.command-trigger > .input {
  align-self: stretch;
  text-align: left; color: var(--c-mist);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  cursor: text;
}

/* Treapta mare — perechea listei de 44px (.popover.lg): suprafață --r-xl +
   padding 10, rând 8/14 la 16/24, aceeași aritmetică (24 − 10 = 14). Se
   deduce din CÂMP, ca lista unui select din trigger: scrii .input.lg și lista
   îl urmează — o singură decizie. Într-un .modal câmpul e oricum .input.lg
   (modal.md), deci dialogul de comenzi e mare fără să mai spună nimeni. Rândul
   de căutare are geometria lui .input-group.lg: 20 lateral, iconița la 20.
   Rândurile, eticheta și separatorul sunt grupate cu `.popover.lg` mai jos, unde
   rândul își ia și padding-ul lateral de 10 (10 + 10 = 20, sub iconița câmpului). */
.command:has(> .command-input > .input.lg) { border-radius: var(--r-xl); }
.command:has(> .command-input > .input.lg) > .command-input { padding: 6px 20px; }
.command:has(> .command-input > .input.lg) > .command-list { padding: 10px; scroll-padding-block: 10px; }
.command:has(> .command-input > .input.lg) > .command-footer { padding: 10px 20px; }

/* Forma de dialog (⌘K) — comanda într-un .modal: scrimul canonic, X-ul din DS,
   Esc, focus trap — toate ale modalului, nimic propriu. Modalul își pierde
   padding-ul și fundalul bej: suprafața E comanda (alb), iar un padding ar
   pune aer între rama dialogului și rândul de căutare. Stă în treimea de sus,
   nu la centru: lista crește și scade cu fiecare literă, iar un dialog centrat
   ar sări pe verticală la fiecare tastă. Lățimea rămâne a modalului
   (.modal / .modal.lg) — nicio treaptă nouă. */
.modal.command-dialog {
  padding: 0; background: var(--c-white); overflow: hidden;
  align-self: flex-start; margin-top: 10vh;
}
.modal.command-dialog > .command { border: 0; border-radius: 0; }
/* X-ul: centrat pe rândul de căutare (56px la .lg → mijloc la 28; butonul .sm
   are 30 → top 13), iar rândul lasă loc în dreapta ca textul să nu intre sub el. */
.modal.command-dialog > .btn.icon-only { top: 13px; right: 13px; }
.modal.command-dialog > .command > .command-input { padding-right: 56px; }

/* ═══════════════════════════════════════════════════════════════
   App Footer — bară fixă jos pentru app-shell paginat
   Conține rândul de navigare (back outline + primary CTA) și un
   text de ajutor opțional care apare doar la scroll (.is-scrolled).
   Pereche cu .app-top — împreună formează shell-ul standard pentru
   flow-uri secvențiale (signing, onboarding, autoangajare).
   ═══════════════════════════════════════════════════════════════ */
.app-footer {
  position: fixed; bottom: 0; left: 0; right: 0;
  z-index: 30;
  background: var(--c-white);
  border-top: 1px solid var(--c-rule-soft);
}
.app-footer[hidden] { display: none; }
.app-footer-in {
  width: 100%; max-width: 640px;
  margin: 0 auto;
  padding: 12px 16px 14px;
  display: flex; flex-direction: column;
  gap: 0;
  box-sizing: border-box;
}
/* Variantă pentru pagini cu conținut wider (ex: signer cu signer-layout 1096).
   Consumatorul măsoară conținutul cu getBoundingClientRect, setează --app-foot-w
   pe :root și aplică .matches-content pe footer. Footer-ul renunță la padding-ul
   orizontal ca butoanele să fie flush cu marginile conținutului. */
.app-footer.matches-content .app-footer-in {
  max-width: var(--app-foot-w, 640px);
  padding-left: 0; padding-right: 0;
}
.app-foot-nav {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px;
}
/* Rezervă spațiu pentru back-button pe primul pas (visibility, nu display) */
.app-foot-nav .invisible { visibility: hidden; }
/* Text ajutor — ascuns by default, revealed pe .is-scrolled */
.app-foot-help {
  text-align: center;
  font-size: 13px; color: var(--c-fog);
  max-height: 0; opacity: 0; margin: 0;
  overflow: hidden; pointer-events: none;
  transition: max-height 180ms ease, opacity 180ms ease, margin-top 180ms ease;
}
.app-foot-help a { color: var(--c-pink-deep); text-decoration: underline; }
.app-footer.is-scrolled .app-foot-help {
  max-height: 48px; opacity: 1; margin-top: 4px;
  pointer-events: auto;
}
@media (min-width: 1024px) {
  .app-footer-in { padding: 12px 20px 16px; }
}

/* ═══════════════════════════════════════════════════════════════
   APP SHELL — FLOW CHROME DESKTOP · DESKTOP ONLY (≥1024px)
   ──────────────────────────────────────────────────────────────
   Perechea desktop a lui .flow-chrome: .app-top (header fix cu
   stepper aliniat stânga, close dreapta și progress line) +
   .app-footer (bara de navigare jos, definită mai sus).
   .app-footer rămâne valid și singur (alias istoric) — orice pagină
   existentă merge nemodificată.

   Gazda e .app-shell, shell-ul de produs din secțiunea LAYOUT. NU-l
   redefinim aici: adăugăm doar rezerva de spațiu pentru cele două
   bare fixe, și doar când headerul de flux chiar există
   (:has(> .app-top)). Un dashboard cu sidebar folosește același
   .app-shell și rămâne neatins.

   Alinierea: ambele bare citesc --app-foot-w (default 640px), deci
   close-ul din header cade flush cu CTA-ul din footer. Pe conținut
   mai lat (signer, 1096px) consumatorul pune .matches-content pe
   AMBELE bare — pe una singură alinierea se rupe.

   Breakpoint: DS-ul are un singur prag, --bp-desktop: 1024px.
   Vezi docs/components/flow-chrome.md → „Desktop".
   ═══════════════════════════════════════════════════════════════ */
.app-shell:has(> .app-top) {
  --app-top-h: 60px;
  background: var(--c-bej-0);
  padding-top: var(--app-top-h);
  padding-bottom: 96px;
  box-sizing: border-box;
}

.app-top {
  --app-top-h: 60px;
  position: fixed; top: 0; left: 0; right: 0;
  z-index: 31;
  background: var(--c-white);
  border-bottom: 1px solid var(--c-rule-soft);
}
.app-top[hidden] { display: none; }
.app-top-in {
  position: relative;
  width: 100%; max-width: var(--app-foot-w, 640px);
  margin: 0 auto;
  padding: 0 16px;
  min-height: var(--app-top-h, 60px);
  display: flex; align-items: center; gap: 16px;
  box-sizing: border-box;
}
/* Simetric cu .app-footer.matches-content — close flush cu marginea conținutului */
.app-top.matches-content .app-top-in { padding-left: 0; padding-right: 0; }
@media (min-width: 1024px) { .app-top-in { padding: 0 20px; } }

/* Contextul e aliniat la stânga, close-ul împins la marginea dreaptă */
.app-top-center {
  flex: 1 1 auto; min-width: 0;
  display: flex; justify-content: flex-start;
  overflow-x: auto; scrollbar-width: none;
}
.app-top-center::-webkit-scrollbar { display: none; }
.app-top-right { flex-shrink: 0; margin-left: auto; display: flex; align-items: center; gap: 4px; }

/* Stepper în header — o treaptă mai compact decât cel din conținut
   (28px indicator / 14px label / gap 28px devin 26 / 13 / 22). */
.app-top .stepper { gap: 22px; flex-wrap: nowrap; }
.app-top .stepper-step { font-size: 13px; white-space: nowrap; }
.app-top .stepper-ind { width: 26px; height: 26px; font-size: 12px; }

/* Close — singura acțiune de chrome din header. Tratament outline (ca
   .btn.outline), simetric cu back-ul din footer. Ieșirea din flux se
   confirmă cu modal (vezi docs/components/modal.md). */
/* Migrat 2026-09-01 de la clasa proprie .app-top-close la
   .btn.outline.icon-only. La 36px era în afara scării și rescria
   .btn.outline literal — alb + inset 1px --c-rule. Comentariul ei spunea
   chiar „tratament outline (ca .btn.outline)": imitația era declarată,
   doar nefolosită. Nu mai e nevoie de nicio regulă aici. */

/* Progress line — bandă de 3px pe toată lățimea, jos în header.
   Consumatorul setează --app-progress (procent) pe .app-progress-bar,
   derivat din indexul pasului: (step + 1) / total.
   NU apare pe ecranele fără pași — nici la 0%, nici la 100%. */
.app-progress {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 3px;
  background: transparent;
}
.app-progress-bar {
  height: 100%; width: var(--app-progress, 0%);
  background: var(--c-bej-1);
  transition: width 280ms cubic-bezier(.2, .8, .2, 1);
}
@media (prefers-reduced-motion: reduce) { .app-progress-bar { transition: none; } }

/* ═══════════════════════════════════════════════════════════════
   FLOW CHROME — SHELL MOBIL PENTRU FLUXURI · MOBILE ONLY (<1024px)
   ──────────────────────────────────────────────────────────────
   ⚠️  EXCLUSIV PE MOBIL — banda de sub breakpoint-ul unic
       (--bp-desktop: 1024px), tablete incluse. De la 1024px în
       sus folosește layout-ul desktop (.app-shell + .layout-12)
       și .app-footer pentru flow-uri.
       Nu importa .flow-chrome (sau orice clasă .flow-*) într-o
       pagină desktop — regula e bidirecțională, fără excepție.
       Vezi SKILL.md → „Hard rules → DON'T",
       docs/components/foundation-layout.md → „Breakpoint unic" și
       docs/navigation.md → „Flow chrome".
   ──────────────────────────────────────────────────────────────
   Shell-ul pentru ecranele SECVENȚIALE de pe mobil: wizard, onboarding,
   declarații, semnare — orice ecran cu un singur „înainte". Header bar
   (back stânga · titlu opțional centru · meniu opțional dreapta) + footer
   bar (un singur CTA primar) ca overlay-uri peste conținut scrollabil.
   Ecranele de PRODUS (acasă, liste, setări, detalii — răsfoiești, citești,
   acționezi) NU stau aici: ele trăiesc în .app-mobile + .tabbar (secțiunile
   LAYOUT și APP NAVIGATION). Două shell-uri pe mobil, ca pe desktop
   (.app-shell + .sidebar vs .app-top + .app-footer): felul ecranului alege
   shell-ul, platforma alege forma. Până pe 2026-09-16 .flow-chrome era
   „shell-ul oricărui ecran de mobil" — asta punea un CTA de flux pe ecrane
   care n-aveau niciun „înainte".
   Tratament canonic: veil + blur (bej-0 cu backdrop-filter), pe care
   conținutul îl traversează gradual mascat.
   Foile de jos (meniu, pași) sunt .sheet (secțiunea SHEET), cu rânduri
   .list-row; .flow-sheet* și .flow-menu-row au rămas aliasuri deprecate.
   Pereche desktop: .app-top + .app-footer (flow-uri multi-page desktop).
   Consumatorul aduce conținutul propriu per ecran și starea
   (step / open-sheets) — DS oferă doar layout-ul și tratamentul.
   ═══════════════════════════════════════════════════════════════ */

.flow-chrome {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: var(--c-bej-0);
  height: 100%;
}

/* Conținut scrollabil — sub overlay-uri, mask fade pe ambele capete
   ca să se estompeze pe sub header/footer (specific tratamentului 1). */
.flow-chrome-scroll {
  position: absolute; inset: 0;
  z-index: 1;
  overflow-y: auto; overflow-x: hidden;
  padding-top: 128px;
  padding-bottom: 108px;
  -webkit-mask-image: linear-gradient(to bottom,
    transparent 0, transparent 58px, #000 124px,
    #000 calc(100% - 104px), transparent calc(100% - 54px), transparent 100%);
          mask-image: linear-gradient(to bottom,
    transparent 0, transparent 58px, #000 124px,
    #000 calc(100% - 104px), transparent calc(100% - 54px), transparent 100%);
}

/* Bar overlay — header sus, footer jos */
.flow-bar {
  position: absolute; left: 0; right: 0;
  z-index: 3;
}
.flow-bar.top    { top: 0; }
.flow-bar.bottom { bottom: 0; }

/* Voalul — gradient bej-0 + backdrop-blur, mascat să se estompeze
   spre conținut. Top și bottom oglindesc gradient + mask. */
.flow-bar-veil {
  position: absolute; inset: 0;
  pointer-events: none;
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
}
.flow-bar.top .flow-bar-veil {
  background: linear-gradient(180deg,
    rgba(251,250,247,0.85) 0%, rgba(251,250,247,0.85) 46%, rgba(251,250,247,0) 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 46%, transparent 100%);
          mask-image: linear-gradient(180deg, #000 46%, transparent 100%);
}
.flow-bar.bottom .flow-bar-veil {
  background: linear-gradient(0deg,
    rgba(251,250,247,0.85) 0%, rgba(251,250,247,0.85) 46%, rgba(251,250,247,0) 100%);
  -webkit-mask-image: linear-gradient(0deg, #000 46%, transparent 100%);
          mask-image: linear-gradient(0deg, #000 46%, transparent 100%);
}
/* Fallback fără backdrop-filter — voalul singur la 90% e încă citibil
   (background bej la 90% peste content); nu mai are blur dar nu se sparge */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .flow-bar-veil { backdrop-filter: none; -webkit-backdrop-filter: none; }
}
@media (prefers-reduced-transparency: reduce) {
  .flow-bar-veil { backdrop-filter: none; -webkit-backdrop-filter: none; }
  .flow-bar.top .flow-bar-veil {
    background: linear-gradient(180deg,
      var(--c-bej-0) 0%, var(--c-bej-0) 46%, rgba(251,250,247,0) 100%);
  }
  .flow-bar.bottom .flow-bar-veil {
    background: linear-gradient(0deg,
      var(--c-bej-0) 0%, var(--c-bej-0) 46%, rgba(251,250,247,0) 100%);
  }
}

/* Rândul de header — 3 zone (back · titlu · meniu) */
.flow-header {
  position: relative;
  display: flex; align-items: center;
  padding: 2px 14px 12px;
}
.flow-header-left,
.flow-header-right { flex: 1; display: flex; }
.flow-header-left  { justify-content: flex-start; }
.flow-header-right { justify-content: flex-end; }
.flow-header-center { flex: 0 0 auto; }

/* Butoanele de chrome (back & meniu) sunt .btn.secondary.icon-only din DS.
   Specul cerea „fundal solid bej-1, opac peste voal și peste conținut
   scrollat, niciodată translucent" — iar .btn.secondary ESTE bej-1 + ink,
   deci nu mai e nevoie de nicio regulă de suprafață aici.

   Migrat 2026-09-01 de la clasa proprie .flow-bar-btn: 38px nu era pe scară,
   iar culorile erau deja o copie literală a lui .btn.secondary.
   aria-disabled rămâne — .btn stinge doar :disabled, iar chrome-ul mobil
   folosește atributul pe primul pas. */
.flow-bar [aria-disabled="true"] { opacity: 0.4; cursor: not-allowed; }

/* Titlu pas — buton transparent cu chevron, deschide steps sheet */
.flow-step-title {
  display: inline-flex; align-items: center; gap: 5px;
  background: none; border: none; padding: 0;
  cursor: pointer;
  font-family: inherit;
  font-size: 16px; line-height: 24px; font-weight: 700; letter-spacing: -0.02em;
  color: var(--c-ink);
}

/* Rândul de footer — un singur CTA full-width */
.flow-footer {
  position: relative;
  padding: 12px 18px 16px;
}
.flow-footer > .btn { width: 100%; }

/* Banda mobile merge până la 1024px, deci include tabletele. Shell-ul rămâne
   o SINGURĂ coloană — nu devine layout desktop la 700px: conținutul
   scrollabil și cele două bare se centrează la --flow-max, iar voalul
   (.flow-bar-veil) continuă să facă bleed pe toată lățimea. */
@media (min-width: 600px) and (max-width: 1023.98px) {
  .flow-chrome-scroll {
    padding-inline: max(0px, calc((100% - var(--flow-max)) / 2));
  }
  .flow-header,
  .flow-footer {
    max-width: var(--flow-max);
    margin-inline: auto;
  }
}

/* ── Foile de jos au ieșit din namespace-ul flow- (2026-09-16) ──
   .sheet-scrim / .sheet.attached / .sheet.floating / .sheet.form trăiesc în
   secțiunea SHEET (după Modal), iar rândurile lor sunt .list-row (secțiunea
   List row). Aliasurile deprecate .flow-sheet-scrim, .flow-sheet,
   .flow-menu-row, .flow-menu-ic poartă aceleași reguli prin selectori grupați
   acolo, ca o copie ștampilată să se poată re-copia fără să-și mute
   apelurile în același pas. Ce rămâne aici e doar ce n-are pereche nouă: */
.flow-sheet.steps { padding: 16px 16px 14px; }           /* DEPRECAT → .sheet.attached + .stepper.vertical */
.flow-menu-gap { height: 4px; }                          /* DEPRECAT → al doilea .list-group */
.flow-steps-label {                                      /* DEPRECAT → .t12 */
  padding: 0 6px 10px;
  font-size: 12px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--c-fog);
}

/* ═══════════════════════════════════════════════════════════════
   MONEY INPUT — sumă hero (Inter 700 48px tabular) + selector monedă
   Organism custom adăugat în v6.1. Folosit pe facturare și
   ecrane cu input mare de sumă (one-field-per-screen).
   Mărimea normală (formulare cu 5+ câmpuri) nu are reguli aici: e un
   .input-group cu moneda într-un addon — vezi „O VALOARE în addon” la Carcasă.
   ═══════════════════════════════════════════════════════════════ */

/* Container row — input + selector aliniate */
.amount-row {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding-left: 16px;
}

/* Valoarea — extensie hero a familiei .tn (Inter 700 tabular) */
.amount-value {
  font-family: var(--font-sans); font-weight: 700;
  font-size: 48px; line-height: 1; letter-spacing: -0.04em;
  color: var(--c-ink); font-variant-numeric: tabular-nums;
  white-space: nowrap; flex-shrink: 0;
  outline: none;
}
.amount-value .amount-dec { font-size: 0.44em; font-weight: 700; color: var(--c-mist); }
.amount-value.placeholder { color: var(--c-mist); }

/* Caret pentru starea activă — neutru, niciodată brand */
.amount-value.is-active { position: relative; }
.amount-value.is-active::after {
  content: ''; display: inline-block; width: 2px; height: 0.82em;
  margin-left: 4px; background: var(--c-ink); vertical-align: -0.04em;
  animation: amt-caret 1s step-end infinite;
}
@keyframes amt-caret { 0%,50% { opacity: 1; } 50.01%,100% { opacity: 0; } }

/* Stare eroare pe valoare */
.amount-field.is-error .amount-value,
.amount-field.is-error .amount-value .amount-dec { color: var(--c-error); }

/* Selector monedă — NU mai are un corp de reguli propriu.

   `.cur-select` și `.cur-select-trigger` sunt alias-uri pentru `.select-anchor`
   și `.select-trigger.lg`: fiecare regulă a primitivei le numește explicit, în
   secțiunea Select trigger. Motivul e același pentru care `.cur-select-menu` a
   fost eliminat pe 2026-09-02 — un al doilea corp de declarații pentru același
   lucru derivă, iar driftul se vede abia după ce a ajuns în consumatori.

   Rămâne aici DOAR ce ține de monedă, nu de control: cele două abateri de
   dimensiune de mai jos și drapelul.

   Markup canonic nou: <div class="select-anchor"> + <button class="select-trigger lg">.
   Numele vechi rămân valide și se scot cel mai devreme la N+2 (VERSIONING.md). */

/* Lângă suma hero (Inter 700 48px) trigger-ul urcă o treaptă de greutate și se
   strânge la padding-ul implicit: e însoțitorul unei valori, nu un câmp
   într-un formular. `line-height: 1` fiindcă se aliniază pe linia de bază a
   sumei, nu pe grila de 20px a formularelor. */
:is(.amount-row .select-trigger, .cur-select-trigger) { padding: 0 14px; font-weight: 600; line-height: 1; }
/* Codul monedei ține o lățime fixă ca trigger-ul să nu-și schimbe lățimea când
   treci de la „Lei" la „USD" — altfel suma de lângă el sare. */
:is(.select-trigger, .cur-select-trigger) .cur-code { display: inline-block; min-width: 36px; text-align: center; }

/* Drapelul — 20×20 circular */
.cur-flag {
  width: 20px; height: 20px; border-radius: 50%;
  overflow: hidden; display: inline-block; flex-shrink: 0;
}
.cur-flag svg { display: block; width: 100%; height: 100%; }

/* RETIRED 2026-09-02 — .cur-select-menu / .currency-option are gone. They were
   a parallel class tree: the surface reimplemented .popover field for field and
   the row reimplemented .popover-item with drift (15px vs 14px, 10px 12px vs
   8px 10px, a hand-drawn 18px circle instead of the DS radio, a bej-1
   "selected" fill). That drift is how the menu hover token diverged in the
   first place. The currency menu is now plain .popover + .popover-item +
   .cur-flag — see components/money-input.html. Breaking for any consumer that
   copied the old demo markup.
   v8.2: the native .radio-item control left the row too. The menu is the
   select's own list — role=listbox, .is-selected + .popover-check at the END
   of the row, weight 600 — at both sizes (hero and regular). Not breaking:
   a copied radio row still renders, it is just no longer the canonical form. */

/* ═══════════════════════════════════════════════════════════════
   Signer — ecran de citire/semnare documente (Rex QES/SES)
   Six pieces: doc-list, doc-bar + pdf-switch, pdf-page (mobile/desktop),
   html-doc-view, doc-sheet, signer-layout (sidebar + doc column).

   Sub-14px fonts on .pdf-*, .doc-item.compact .doc-item-num and
   .doc-item-meta are document-essential (Times typography for "official
   document feel", 22×22 sidebar bubble, and the 12px secondary line that
   .popover-item .typeahead-meta already establishes for list rows).
   Whitelisted in the 14px-baseline rule.
   ═══════════════════════════════════════════════════════════════ */

/* — Doc list — */
.doc-list { display: flex; flex-direction: column; gap: 8px; }
.doc-item {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex; align-items: center; gap: 12px;
  cursor: pointer; text-align: left; font-family: inherit; width: 100%;
}
@media (hover: hover) {
  .doc-item:hover { border-color: var(--c-fog); }
}
.doc-item-num {
  width: 30px; height: 30px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; line-height: 20px; font-weight: 600;
  font-family: var(--font-mono);
  flex-shrink: 0;
}
.doc-item-num.unread  { background: var(--c-white); border: 1px solid var(--c-rule-card); color: var(--c-fog); }
.doc-item-num.read    { background: var(--c-read-bg); color: var(--c-read-text); }
.doc-item-num.current { background: var(--c-white); border: 1.5px solid var(--c-pink); color: var(--c-pink-deep); }
.doc-item-num.signed  { background: var(--c-success-bg); color: var(--c-success-text); }
.doc-item-body { flex: 1; min-width: 0; }
.doc-item-name {
  font-size: 14px; font-weight: 500; color: var(--c-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Rândul secundar. Aceleași metrici ca `.popover-item .typeahead-meta` — 12/16
   pe --c-fog — fiindcă e același lucru: a doua linie a unui rând dintr-o listă.
   Era 14px, adică EXACT mărimea lui .doc-item-name (14/500), deci între etichetă
   și meta nu exista nicio ierarhie de mărime și culoarea ducea singură toată
   distincția. La 12/400 ierarhia vine din mărime și greutate, ca peste tot
   altundeva, iar culoarea nu mai face muncă dublă — exact argumentul scris deja
   la .popover-item .typeahead-meta.
   Contrast: --c-fog dă 4.19:1 pe --c-bej-05 (rândul hover-at) și 4.37:1 pe
   --c-bej-0. Sub AA la 12px, ca și 3.88:1-ul pe --c-pink-tint din popover —
   aceeași clasă de pereche, aceeași baseline cunoscută, nu una nouă. Mărimea nu
   mută raportul: WCAG cere 4.5:1 sub 18.66px, deci 12px și 14px au același prag. */
.doc-item-meta { font-size: 12px; line-height: 16px; color: var(--c-fog); margin-top: 2px; }
/* Pe rândul curent meta urmează culoarea rândului. (.popover-item.is-active a
   trecut pe neutru în 2026-09-21; .doc-item.current NU e un rând de meniu — e
   documentul deschis într-o listă de navigare, deci își păstrează tintul.) */
.doc-item-meta.current { color: var(--c-pink-deep); }
.doc-item.current { background: var(--c-pink-tint); border-color: var(--c-pink); }
/* Compact density — for desktop sidebar */
.doc-item.compact {
  padding: 8px 10px; border-radius: var(--r-sm);
  background: transparent; border-color: transparent;
  gap: 10px; margin-bottom: 2px;
}
/* Hover — --c-bej-05. Gazda e .signer-sidebar, care e --c-white, deci treapta
   se măsoară de la alb. Până pe 2026-09-09 era --c-bej-0: ΔL* 1.73 de la alb,
   exact valoarea respinsă pe 2026-09-02 pentru rândurile de popover („rândul nu
   se citește ca accesibil"). bej-05 dublează treapta la 3.39 fără să schimbe
   mecanismul — tot un fill tonal plin, doar o treaptă mai jos. */
@media (hover: hover) {
  .doc-item.compact:hover { background: var(--c-bej-05); border-color: transparent; }
}
.doc-item.compact.current { background: var(--c-pink-tint); border-color: var(--c-pink); }
.doc-item.compact .doc-item-num { width: 22px; height: 22px; font-size: 10px; }
.doc-item.compact .doc-item-name { font-size: 14px; }
.doc-item.compact .doc-item-meta { font-size: 14px; }

/* — Doc bar + PDF switch — */
.doc-bar {
  display: flex; align-items: center; gap: 12px;
  padding: 8px 12px 8px 16px;
  background: var(--c-white);
  border-bottom: 1px solid var(--c-rule-soft);
  flex-shrink: 0;
}
.doc-bar.desktop { padding: 10px 20px 10px 28px; }
.doc-bar-title {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 6px;
  background: transparent; border: none; padding: 8px 4px;
  font-family: inherit; cursor: pointer; min-height: 44px;
  font-size: 16px; line-height: 24px; font-weight: 600; color: var(--c-ink);
  text-align: left;
}
.doc-bar-title.static { cursor: default; }
.doc-bar-title .doc-bar-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 1; min-width: 0; }
.doc-bar-title .doc-bar-chevron { flex-shrink: 0; color: var(--c-fog); transition: transform 140ms; }
@media (hover: hover) {
  .doc-bar-title:hover { color: var(--c-ink); }
  .doc-bar-title:hover .doc-bar-chevron { color: var(--c-ink); }
}
.pdf-switch {
  display: inline-flex; align-items: center; gap: 8px;
  background: transparent; border: none; padding: 10px 8px;
  font-family: inherit; cursor: pointer;
  min-height: 44px; min-width: 44px;
  font-size: 16px; line-height: 24px; font-weight: 500; color: var(--c-fog);
  flex-shrink: 0;
}
.pdf-switch .pdf-switch-label { user-select: none; }
.pdf-switch.on { color: var(--c-ink); }
.pdf-switch .switch { pointer-events: none; }

/* — PDF viewer — */
.pdf-page {
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  padding: 24px 18px;
  font-family: 'Times New Roman', serif;
  font-size: 10px; line-height: 1.55;
  color: var(--c-ink);
}
.pdf-page.desktop {
  width: 100%; max-width: 800px;
  padding: 56px 72px;
  font-size: 14px; line-height: 1.6;
  border-radius: var(--r-xl);
}
.pdf-page.desktop + .pdf-page.desktop { margin-top: 14px; }
.pdf-subtitle {
  text-align: center;
  font-size: 10px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-fog);
  margin: 0 0 14px;
}
.pdf-page.desktop .pdf-subtitle { font-size: 12px; margin-bottom: 24px; }
.pdf-section { font-size: 10px; font-weight: 700; margin: 12px 0 4px; }
.pdf-page.desktop .pdf-section { font-size: 13px; margin: 18px 0 8px; }
.pdf-p { margin: 0 0 8px; text-align: justify; }
.pdf-page.desktop .pdf-p { margin: 0 0 12px; }
.pdf-hl { background: var(--c-pdf-hl); padding: 0 2px; }
.pdf-foot {
  text-align: center;
  font-size: 10px;
  color: var(--c-fog);
  font-family: var(--font-mono);
  margin-top: 18px; padding-top: 12px;
  border-top: 1px solid var(--c-rule-soft);
}
.pdf-page-sep {
  margin: 40px -72px;
  border-top: 2px solid var(--c-rule-card);
  border-bottom: 2px solid var(--c-rule-card);
  height: 18px;
  background: var(--c-bej-1);
}
.pdf-stack { display: flex; flex-direction: column; gap: 12px; }

/* — HTML doc view (mobile alternative) — */
.html-doc-eyebrow {
  font-size: 14px; line-height: 20px; font-weight: 600;
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--c-fog);
  margin-bottom: 8px;
  display: flex; align-items: center; gap: 6px;
}
.html-doc-eyebrow::before {
  content: '';
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--c-pink);
}
.html-doc-intro { font-size: 14px; color: var(--c-ink); line-height: 1.65; margin: 0 0 18px; }
.html-doc-section {
  font-size: 14px; font-weight: 600;
  letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--c-fog);
  margin: 18px 0 6px;
  padding-top: 12px;
  border-top: 1px solid var(--c-rule-soft);
}
.html-doc-section:first-of-type { border-top: none; padding-top: 0; margin-top: 16px; }
.html-doc-p { font-size: 14px; color: var(--c-ink); line-height: 1.65; margin: 0 0 14px; }
.html-doc-hl {
  background: var(--c-pink-tint);
  color: var(--c-pink-deep);
  padding: 1px 5px;
  border-radius: var(--r-xs);
  font-weight: 500;
}
.html-doc-meta {
  margin-top: 18px; padding-top: 12px;
  border-top: 1px solid var(--c-rule-soft);
  font-size: 14px; color: var(--c-fog);
  display: flex; justify-content: space-between;
  font-family: var(--font-mono);
}

/* — Doc sheet (bottom modal mobile) — DEPRECAT 2026-09-16 (doc-sheet.json →
   replaced_by .sheet.attached): aceeași foaie ca .sheet.attached, cu un
   .doc-list înăuntru. Regulile rămân pentru copiile ștampilate; se scot la
   N+2 (VERSIONING.md). Grabber-ul (.doc-sheet-handle) nu se mai folosește
   nicăieri nou — se citește ca drag și declanșează pull-to-refresh. — */
.doc-sheet-backdrop {
  position: fixed; inset: 0; z-index: 200;
  background: var(--c-backdrop);
  backdrop-filter: blur(var(--scrim-blur)); -webkit-backdrop-filter: blur(var(--scrim-blur));
  display: flex; flex-direction: column; justify-content: flex-end;
}
.doc-sheet-backdrop[hidden] { display: none !important; }
.doc-sheet {
  background: var(--c-bej-0);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  padding: 8px 18px 24px;
  max-height: 80%;
  display: flex; flex-direction: column;
}
.doc-sheet-handle {
  width: 40px; height: 4px;
  background: var(--c-rule-card);
  border-radius: 2px;
  margin: 8px auto 14px;
}
.doc-sheet-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px;
}
.doc-sheet-title { font-size: 18px; font-weight: 600; color: var(--c-ink); }
.doc-sheet-list { overflow-y: auto; flex: 1; }
/* Închiderea sheet-ului — același buton ca pe .modal:
   .btn.outline.sm.icon-only. Headerul dă doar așezarea (e un rând flex cu
   titlul la stânga), nu tratamentul butonului.

   Migrat 2026-09-02 de la clasa proprie .doc-sheet-close: transparent +
   padding 8px + fog, adică un al doilea buton de închidere cu alt contur și
   altă țintă de tap decât cel din .modal, pe o suprafață care e tot un
   dialog modal (.doc-sheet-backdrop poartă scrimul canonic). */
.doc-sheet-header > .btn.icon-only { flex-shrink: 0; }

/* — Signer layout (desktop composition) — */
.signer-layout {
  max-width: 1096px;
  margin: 0 auto;
  width: 100%;
  display: flex; gap: var(--sp-16);
  align-items: flex-start;
  padding: 24px 0 40px;
}
.signer-sidebar {
  width: 280px;
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-xl);
  display: flex; flex-direction: column;
  flex-shrink: 0;
  align-self: flex-start;
  overflow: hidden;
}
.signer-sidebar.sticky { position: sticky; top: 24px; }
.signer-sidebar-header { padding: 18px 18px 14px; border-bottom: 1px solid var(--c-rule-soft); }
.signer-sidebar-title { font-size: 16px; font-weight: 600; letter-spacing: -0.015em; color: var(--c-ink); }
.signer-sidebar-list { flex: 1; overflow-y: auto; padding: 10px; }
.signer-doc { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; }
.signer-doc-bar {
  width: 100%; max-width: 800px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.signer-doc-title {
  font-size: 16px; font-weight: 600; color: var(--c-ink);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════
   UPLOADER — .uploader*
   Zonă de încărcare + listă de fișiere. Un singur slot de înălțime
   fixă (--up-h): zona goală și containerul plin ocupă același spațiu,
   ca elementul să nu împingă layoutul când apar fișiere.
   Conturul punctat e SVG, nu border: CSS-ul nu permite dash și gap
   separat (dash-urile scalează cu grosimea). 1px · dash 8 · gap 6.
   ═══════════════════════════════════════════════════════════════ */
.uploader { --up-h: 218px; --up-h-compact: 64px; display: flex; flex-direction: column; gap: 14px; }
.uploader.is-compact { --up-h: var(--up-h-compact); }
.uploader-slot { min-height: var(--up-h); display: flex; flex-direction: column; gap: 14px; }

/* ── Zona de drop — fundal moștenit de la suprafața de sub ea ── */
.uploader-zone {
  height: var(--up-h); box-sizing: border-box; position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 12px; text-align: center; padding: 30px 24px;
  background: transparent; border-radius: var(--r-xl);
  cursor: pointer; transition: background 140ms;
}
.uploader-outline {
  position: absolute; inset: .5px; width: calc(100% - 1px); height: calc(100% - 1px);
  overflow: visible; pointer-events: none; opacity: .6; transition: opacity 140ms;
}
.uploader-zone:focus-visible .uploader-outline,
.uploader-zone.is-over .uploader-outline { opacity: 1; }
.uploader-zone.is-over { background: var(--c-accent-ultra); }
@media (hover: hover) {
  .uploader-zone:hover:not(.is-disabled) .uploader-outline { opacity: 1; }
  .uploader-zone:hover:not(.is-disabled) { background: var(--c-accent-ultra); }
}
/* Disabled = citirea de la .input:disabled: bej-1 + mist, contur neutru. */
.uploader-zone.is-disabled { cursor: not-allowed; background: var(--c-bej-1); color: var(--c-mist); }
.uploader-zone.is-disabled .uploader-outline { opacity: 1; }
.uploader-zone.is-disabled .uploader-title,
.uploader-zone.is-disabled .t14 { color: var(--c-mist); }
.uploader-ic {
  width: 56px; height: 56px; border-radius: var(--r-full);
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--c-accent); color: var(--c-white);
}
.uploader-zone.is-disabled .uploader-ic { background: var(--c-white); color: var(--c-mist); }
.uploader-zone-body { display: flex; flex-direction: column; gap: 2px; }
.uploader-zone .btn.sm { margin-top: 2px; }
.uploader-title { font-weight: 600; color: var(--c-ink); }

/* ── Compact — zona devine un rând de exact înălțimea unui .uploader-item ── */
.is-compact .uploader-zone {
  border-radius: var(--r-md); flex-direction: row; align-items: center;
  justify-content: flex-start; text-align: left; gap: 11px; padding: 9px 12px 9px 13px;
}
.is-compact .uploader-ic { display: none; }
.is-compact .uploader-zone-body { flex: 1; min-width: 0; }
.is-compact .uploader-zone .btn.sm { margin-top: 0; flex-shrink: 0; }
.is-compact .uploader-title { font-size: 14px; }
.is-compact .uploader-zone-body .t14 { font-size: 13px; }

/* ── Containerul plin — aceeași cutie ca zona, contur neutru ── */
.uploader-filled {
  flex: 1; box-sizing: border-box; display: flex; flex-direction: column;
  justify-content: flex-start; padding: 11px;
  background: transparent; border: 1px solid var(--c-rule-card); border-radius: var(--r-xl);
}
.is-compact .uploader-filled { flex: 0 0 auto; padding: 0; border: 0; border-radius: 0; }

/* ── Gazdă albă — cardul cu uploader are voie să fie alb ──
   Excepția de la „card cu inputs → tonal": zona nu cere bej-1 ca să se
   citească drept input host, conturul ei accent face deja treaba. Ce nu
   merge pe alb e conturul containerului plin: peste conturul cardului și
   al rândurilor dă trei cutii concentrice. Pe gazdă albă containerul se
   retrage la un simplu grup — rândurile albe cu hairline poartă singure
   structura. Înălțimea nu se mișcă: .uploader-filled rămâne flex: 1. */
:is(.card:not(.tonal):not(.dark), .card-hairline) .uploader-filled {
  padding: 0; border-color: transparent;
}

/* ── Rând de fișier ── */
.uploader-list { display: flex; flex-direction: column; gap: 8px; }
.uploader-item {
  display: flex; align-items: center; gap: 12px; padding: 11px 12px 11px 13px;
  background: var(--c-white); border: 1px solid var(--c-rule-card); border-radius: var(--r-md);
}
.uploader-item.is-error { border-color: var(--c-error); background: var(--c-error-bg); }
.uploader-thumb {
  width: 36px; height: 36px; border-radius: var(--r-sm);
  display: grid; place-items: center; flex-shrink: 0;
  background: var(--c-bej-1); color: var(--c-fog);
}
.uploader-thumb.is-done { background: var(--c-success-bg); color: var(--c-success); }
.uploader-thumb.is-error { background: var(--c-error-bg); color: var(--c-error); }
.uploader-body { flex: 1; min-width: 0; }
.uploader-name { display: flex; align-items: center; gap: 8px; color: var(--c-ink); font-weight: 500; }
.uploader-name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uploader-meta { color: var(--c-fog); margin-top: 2px; }
.uploader-meta.is-error { color: var(--c-error-text); }
.uploader-track { height: 4px; border-radius: var(--r-full); background: var(--c-bej-1); overflow: hidden; margin-top: 8px; }
.uploader-fill { height: 100%; border-radius: var(--r-full); background: var(--c-accent); transition: width 220ms linear; }
.uploader-row-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; }
.uploader-add { display: flex; }

/* ── Inputul real de fișiere ──
   Ascuns VIZUAL, nu `display: none`: rămâne în arborele de accesibilitate și
   păstrează comportamentul nativ de picker. Zona (role="button"), butonul din
   ea și Enter/Space îl declanșează prin .click(). */
.uploader-input,
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}
/* `.sr-only` e aceeași rețetă, cu numele pe care îl are peste tot altundeva
   (shadcn, Tailwind, Bootstrap): text doar pentru cititorul de ecran. Primul
   consumator e titlul dialogului de comenzi (react/command.tsx) — Radix cere un
   Title pe orice Dialog, iar paleta n-are unul vizibil. */

/* ── Focus ──
   Cutia e mare, deci outline-ul urmează raza ei, nu --r-sm din regula globală.
   Compact are altă rază, deci și alt override. */
.uploader-zone:focus-visible { border-radius: var(--r-xl); outline-offset: 3px; }
.is-compact .uploader-zone:focus-visible { border-radius: var(--r-md); }

/* ═══════════════════════════════════════════════════════════════
   LAYOUT — breakpoint unic (1024px) + grilă de 12 coloane
   ──────────────────────────────────────────────────────────────
   Un singur breakpoint în tot DS-ul: --bp-desktop = 1024px.

   - sub 1024px  → MOBILE: o coloană, sidebar-ul ascuns. DOUĂ shell-uri,
     după felul ecranului (2026-09-16): .app-mobile + .tabbar pentru
     ecranele de produs (acasă, liste, setări, detalii), .flow-chrome
     pentru fluxuri (wizard, onboarding, semnare). Vezi .app-mobile mai
     jos și secțiunea FLOW CHROME.
   - de la 1024px → DESKTOP: .app-shell (sidebar OPȚIONAL, rail fix
     în afara grilei) + .layout-12 (12 coloane, gutter --grid-gutter,
     capat la --layout-max și centrat). Fundalul curge la edge,
     conținutul funcțional nu.

   Sidebar-ul e opțional și NU consumă coloane: e un rail de
   --sidebar-w (sau --sidebar-w-icon pentru .icon-only) pe care
   grid-ul părinte îl măsoară cu :has(). Cele 12 coloane trăiesc în
   zona de conținut, indiferent dacă sidebar-ul e prezent sau nu —
   se schimbă doar lățimea disponibilă.

   Spanurile .col-1….col-12 se aplică DOAR peste breakpoint; sub el
   grila are o singură coloană și fiecare copil ocupă toată lățimea,
   fără clase suplimentare.

   .ds-shell (mai jos) e varianta de chrome pentru site-ul de docs;
   pentru pagini de produs folosește .app-shell.

   .app-shell găzduiește și chrome-ul de flux pe desktop: cu un
   .app-top ca prim copil primește rezerva de spațiu pentru cele două
   bare fixe (vezi „APP SHELL — FLOW CHROME DESKTOP" mai sus). Regula
   e scopată pe :has(> .app-top), deci un shell de produs fără header
   de flux rămâne exact ce era.
   ═══════════════════════════════════════════════════════════════ */

.app-shell {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 100vh;
}
/* Mobile: sidebar-ul nu există ca layout — navigarea trăiește în shell-ul mobil */
@media (max-width: 1023.98px) {
  .app-shell > .sidebar { display: none; }
}
@media (min-width: 1024px) {
  .app-shell:has(> .sidebar)           { grid-template-columns: var(--sidebar-w) 1fr; }
  .app-shell:has(> .sidebar.icon-only) { grid-template-columns: var(--sidebar-w-icon) 1fr; }
}

/* Grila de conținut — 12 coloane pe desktop, o coloană pe mobile.
   Padding egal stânga/dreapta (--layout-pad): aceeași respirație spre
   sidebar și spre marginea opusă. Dacă ecranul are un panou lateral
   dreapta (ex: ticker-ul Dublin), panoul NU își adaugă padding stânga
   propriu — padding-ul grilei e întregul gol, ca golurile să rămână egale
   pe ambele părți.

   Peste cap (--layout-max) aerul rămas are o singură regulă: FĂRĂ rail
   grila stă centrată în viewport; CU rail se lipește de rail și tot aerul
   cade în dreapta. Railul stă lipit de marginea ecranului, deci și
   conținutul stă lipit de rail. Măsurat 2026-09-16: centrată, grila lăsa
   la 1920px 120px de gol lângă railul de 240 (trecea drept centrare) dar
   208px lângă railul icon-only de 64 (528px la 2560) — se citea ca layout
   stricat, nu ca aer. */
.layout-12 {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--grid-gutter);
  width: 100%;
  max-width: var(--layout-max);
  margin-inline: auto;
  padding-inline: var(--layout-pad-mob);
}
.layout-12 > * { min-width: 0; }
/* Cu sidebar (full sau icon-only) grila se lipește de rail. Descendent, nu
   copil direct: un topbar deasupra grilei o pune de obicei într-un wrapper. */
.app-shell:has(> .sidebar) .layout-12 { margin-inline: 0 auto; }

@media (min-width: 1024px) {
  .layout-12 {
    grid-template-columns: repeat(var(--grid-cols), minmax(0, 1fr));
    padding-inline: var(--layout-pad);
  }
  .col-1  { grid-column: span 1;  }
  .col-2  { grid-column: span 2;  }
  .col-3  { grid-column: span 3;  }
  .col-4  { grid-column: span 4;  }
  .col-5  { grid-column: span 5;  }
  .col-6  { grid-column: span 6;  }
  .col-7  { grid-column: span 7;  }
  .col-8  { grid-column: span 8;  }
  .col-9  { grid-column: span 9;  }
  .col-10 { grid-column: span 10; }
  .col-11 { grid-column: span 11; }
  .col-12 { grid-column: span 12; }
}


/* ── Shell mobil de PRODUS — .app-mobile (< 1024px) ──────────────────────────
   Perechea mobilă a lui .app-shell: rama fixă în care trăiește un ecran de
   produs pe telefon — header voalat sus (.app-mobile-top), conținut care
   scrollează dedesubt (.app-mobile-scroll), slot de footer pentru bara de
   navigare (.tabbar, secțiunea APP NAVIGATION). Două shell-uri pe mobil, ca pe
   desktop (.app-shell + .sidebar vs .app-top + .app-footer): FELUL ecranului
   alege shell-ul, platforma alege forma.
     ecran de produs (răsfoiești, citești, acționezi)   → .app-mobile + .tabbar
     ecran de flux (secvențial, un singur „înainte")    → .flow-chrome
   Un ecran de produs nu primește niciodată un CTA în footer — acolo stă bara;
   un flux nu primește niciodată bara — acolo stă CTA-ul. Adăugat 2026-09-16
   (primitivele mobile; contractul e foundation-layout). */
.app-mobile, .flow-chrome {
  /* Insets de sistem — notch sus, home indicator jos. 0 în browser; paginile de
     referință le simulează cu valori fixe pe rama de telefon. Declarate pe
     shell, nu în :root — sunt o proprietate a ramei, nu un token. */
  --safe-top:    env(safe-area-inset-top, 0px);
  --safe-bottom: env(safe-area-inset-bottom, 0px);
}
.app-mobile {
  position: relative; isolation: isolate;
  height: 100dvh; overflow: hidden;
  background: var(--c-bej-0);
  --app-mobile-top: 60px;   /* înălțimea headerului, fără safe-top */
  --app-mobile-bot: 56px;   /* înălțimea barei .tabbar, fără safe-bottom */
}
/* Ecran de detaliu fără bară (navigarea globală e în urmă, nu jos) */
.app-mobile.no-bar { --app-mobile-bot: 0px; }
.app-mobile-scroll {
  position: absolute; inset: 0;
  overflow-y: auto; scrollbar-width: none; -webkit-overflow-scrolling: touch;
  padding: calc(var(--safe-top) + var(--app-mobile-top) + 12px) var(--layout-pad-mob)
           calc(var(--safe-bottom) + var(--app-mobile-bot) + 24px);
  display: flex; flex-direction: column; gap: 16px;
}
.app-mobile-scroll::-webkit-scrollbar { display: none; }

/* Header — același voal ca .flow-bar: bej-0 @ 85% + blur(7px). Trei configurări,
   după ce spune ecranul: root (logo), secțiune (titlu · acțiune),
   detaliu (înapoi · titlu centrat · spacer). Titlul e chrome, nu heading —
   un div, ca .flow-header-center; h1-ul paginii, dacă există, stă în conținut.
   Contractul e `header` (perechea desktop: topbar-ul Dublin). */
.app-mobile-top {
  position: absolute; top: 0; left: 0; right: 0; z-index: 3;
  padding: calc(var(--safe-top) + 12px) 14px 12px;
  background: rgba(251, 250, 247, 0.85);
  -webkit-backdrop-filter: blur(7px); backdrop-filter: blur(7px);
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: calc(var(--safe-top) + var(--app-mobile-top));
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .app-mobile-top { background: var(--c-bej-0); }
}
@media (prefers-reduced-transparency: reduce) {
  .app-mobile-top { -webkit-backdrop-filter: none; backdrop-filter: none; background: var(--c-bej-0); }
}
/* root: doar logo, stânga — meniul e itemul Meniu din .tabbar */
.app-mobile-top > .logo { height: 22px; width: auto; margin-left: 6px; }
/* secțiune: titlu stânga (18/600), acțiune dreapta */
.app-mobile-top > .title {
  margin: 0 0 0 6px; flex: 1; min-width: 0;
  font-size: 18px; line-height: 24px; font-weight: 600; letter-spacing: -0.01em; color: var(--c-ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* detaliu: înapoi · titlu centrat (16/700, ca .flow-step-title) · spacer = lățimea butonului */
.app-mobile-top.detail > .title {
  margin: 0; text-align: center;
  font-size: 16px; font-weight: 700; letter-spacing: -0.02em;
}
.app-mobile-top > .spacer { width: 40px; flex: 0 0 auto; }

/* Desktop: .topbar — perechea de sus a headerului de produs (contractul header).
   Un singur rând pe bej-0, deasupra coloanei de conținut, la aceeași lățime
   ca .layout-12: selectorul firmei (.select-trigger.sm) · căutarea
   (.input-group) · badge-ul live. Înlocuiește flex-ul inline din vechiul
   templates/header.html (2026-09-16). Sub 1024px headerul e .app-mobile-top;
   consumatorul face swap, ca la flow chrome. */
.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  width: 100%; max-width: var(--layout-max); margin-inline: auto;
  padding: 16px var(--layout-pad) 8px;
}
.topbar > .input-group { flex: 1 1 320px; max-width: 460px; }

/* ═══════════════════════════════════════════════════════════════
   APP NAVIGATION — .sidebar (desktop ≥1024) · .tabbar + .mobile-menu (mobile <1024)
   Contractul e app-nav.json: UN job, două forme, după platformă.
   Desktop: rail-ul .sidebar — are loc să arate totul deodată.
   Mobile: rail-ul se desparte după FRECVENȚĂ — destinațiile de nivel 1
   (cel mult cinci) intră în .tabbar, mereu vizibilă; identitatea, grupurile,
   rândurile de cont, linkurile de footer și ieșirea intră în .mobile-menu, deschis
   de itemul Meniu din bară. .tabbar-action e perechea lui .sidebar-cta.
   Numele claselor NU se schimbă — .sidebar rămâne .sidebar (2026-09-16).

   Sidebar — componentă canonică (desktop)
   Folosită ca chrome pe paginile DS și ca product sidebar pe Dublin.
   Tipografie aliniată la scala: .t12 (group labels) / .t14 (links).

   Variante:
   - .sidebar             — default 240px full (DS docs, Dublin full mode)
   - .sidebar.icon-only   — 64px icon-only (Dublin compact mode)

   Sub-elemente:
   - .sidebar-brand       — logo top + separator (non-clickabil)
   - .sidebar-grp         — eyebrow descriptor opțional, non-actionable (.t12)
   - .sidebar-section     — wrapper colapsabil (data-state open|closed)
   - .sidebar-trigger     — categorie-părinte expandabilă (.t14 + icon + caret)
   - .sidebar-section-list— lista de copii (.t14 inset, fără icon)
   - .sidebar a           — nav link (.t14 style) — suportă <svg> icon
   - .sidebar-footer      — footer optional (links secundare; icon-only → icon-buttons)
   - .sidebar-cta         — CTA primary opțional, stă sub footer (icon-only → icon-button 40×40)

   Layout host:
   - .app-shell  — shell de produs (sidebar opțional + .layout-12). Vezi
     secțiunea LAYOUT mai sus.
   - .ds-shell   — wrap sidebar + main pe paginile DS docs. Sub breakpoint
     colapsează la o coloană și ascunde sidebar-ul (regula stă în
     components/_chrome.css, alături de restul stilurilor de doc-site).
   ═══════════════════════════════════════════════════════════════ */

.ds-shell { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

.sidebar {
  position: sticky; top: 0; height: 100vh;
  /* Stratul „sticky surfaces" (50) din scara canonică — foundation-grid.html.
     Sticky face oricum un stacking context; fără z-index railul se picta în
     ordinea DOM-ului, deci un card poziționat de lângă el (.kpi e
     position: relative) acoperea eticheta de hover din .icon-only. */
  z-index: 50;
  background: var(--c-bej-1);
  border-right: 1px solid var(--c-rule-soft);
  padding: 20px 12px 24px;
  overflow-y: auto;
  /* 1px de aer între rânduri: pilula albă a activului și hover-ul vecinului
     nu trebuie să se atingă. Gap, nu margin pe rânduri — footer-ul și CTA-ul
     se pinează jos cu margin-top:auto, care ar fi călcat de o margine fixă. */
  display: flex; flex-direction: column; gap: 1px;
}

.sidebar-brand {
  display: block;
  padding: 0 8px 16px;
  border-bottom: 1px solid var(--c-rule-soft);
  margin-bottom: 8px;
}
.sidebar-brand img { height: 22px; width: auto; display: block; }

/* Group label — .t12 styling */
.sidebar-grp {
  font-family: var(--font-sans);
  font-size: 12px; line-height: 16px; font-weight: 600;
  letter-spacing: 0.10em; text-transform: uppercase;
  color: var(--c-fog);
  padding: 0 8px; margin: 18px 0 6px;
  display: flex; align-items: center; gap: 6px;
}
/* Nav link — .t14 styling, cu icon support */
.sidebar a {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  font-family: var(--font-sans);
  font-size: 14px; line-height: 20px; font-weight: 500;
  color: var(--c-fog);
  border-radius: var(--r-lg); /* forma rândului — hover și active împart același radius */
  transition: background 120ms, color 120ms;
  text-decoration: none;
}
/* TODO(hover-sidebar): the sidebar is the one menu-type surface still on
   --c-rule-soft. The 2026-09-02 decision made --c-bej-1 the single hover
   surface for menu-type surfaces (see the .popover-item hover comment), but
   .sidebar is ITSELF --c-bej-1, so bej-1-on-bej-1 would be invisible. Needs a
   design call — a tonal step above bej-1, white like .sidebar a.active, or a
   deliberate exception. Left unchanged on purpose; do not "fix" it by copying
   the popover token. */
@media (hover: hover) {
  .sidebar a:hover { background: var(--c-rule-soft); color: var(--c-ink); }
}
/* Active — white highlight: link lifted off tonal bg (--c-bej-1) cu subtle shadow.
   Aliniat cu pattern-ul .tabs button.active (white bg + ink + lift). */
.sidebar a.active {
  background: var(--c-white); color: var(--c-ink);
  box-shadow: var(--sh-xs);
}
.sidebar a svg { flex-shrink: 0; opacity: 0.6; }
.sidebar a.active svg { opacity: 1; }

/* Contor pe un item de meniu — componenta `.badge`, cu `.sm` obligatoriu aici
   (badge.json → sizes.scale[".badge.sm"].required_in include `.sidebar`).
   Varianta urmează rândul, fiindcă rândul își schimbă suprafața sub contor:
     - rând implicit  → suprafața e --c-bej-1 (railul) → contorul e `.outline`
                        (fill --c-white + contur neutru) = badge ALB;
     - rând `.active` → suprafața e pilula albă        → contorul e `.neutral`
                        (fill --c-bej-1) = badge NEUTRU.
   Inversate, ambele dispar în propriul rând: alb pe alb, bej pe bej. Nu e o
   alegere de gust — e aceeași inversiune tonală pe care o face deja `.tabs.dark`
   pentru contoarele ei. Pink NU, în niciun caz: un număr nu e beneficiu de brand.
   Perechea se scrie în MARKUP și se schimbă odată cu `.active` (și în JS, când
   activul se mută la runtime). DS-ul n-o rescrie din CSS intenționat: dacă
   `.outline` s-ar randa ca `.neutral` pe rândul activ, clasa din markup ar minți
   și lintul n-ar mai avea ce verifica. Pereche greșită = drift, prins de
   scripts/check-aligned-lint.sh (regula 19).
   DS-ul definește aici doar PLASAREA, în cele două geometrii ale componentei:
   în rail-ul full există un rând, deci contorul se duce la capătul lui; în
   `.icon-only` nu mai există rând (linkul e un pătrat de 40px cu label ascuns),
   deci se agață de colț. Fără regulile astea un `.badge` pus în `.icon-only`
   împinge iconul din centru.
   EXCEPȚIE, și vine tot din plasare: în `.icon-only` contorul e agățat de colțul
   de sus-dreapta, adică peste colțul tăiat al pilulei și peste rail — suprafața
   de sub el e --c-bej-1 chiar și pe rândul activ. Deci acolo contorul rămâne
   `.outline` în ambele stări; `.neutral` pe activ ar tăia o gaură bej în pilula
   albă și n-ar avea niciun contur față de rail (verificat în browser, 2026-09-21).
   Inversiunea e regula rail-ului FULL. */
.sidebar a > .badge { margin-left: auto; }
.sidebar.icon-only a { position: relative; }
.sidebar.icon-only a > .badge {
  /* top:0, nu negativ: `gap` pe `.sidebar` e 1px, deci un contor ridicat peste
     marginea linkului ar călca itemul de deasupra (și pilula albă a activului). */
  position: absolute; top: 0; right: -2px;
  margin-left: 0;
}
/* Top-level items — un increment de padding lateral în plus (10→12px) față de copiii inset */
.sidebar > a { padding-inline: 12px; }

/* Footer — .t14 weight 400 + color mist pentru distincție */
.sidebar-footer {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--c-rule-soft);
}
.sidebar-footer a {
  font-size: 14px; font-weight: 400;
  color: var(--c-mist);
  padding: 5px 10px;
  border-radius: var(--r-xs);
}
@media (hover: hover) {
  .sidebar-footer a:hover { color: var(--c-ink); background: transparent; }
}
/* Footer-ul e un bloc simplu, nu flex — cei 1px vin din marginea rândului. */
.sidebar-footer a + a { margin-top: 1px; }

/* CTA opțional — buton primary full-width care stă cel mai jos, sub footer.
   margin-top:auto îl pinează jos; când urmează unui footer (care deja pinează
   grupul), resetăm marginea ca să stea lipit sub el. */
.sidebar-cta { margin-top: auto; padding-top: 12px; }
.sidebar-footer + .sidebar-cta { margin-top: 0; }
.sidebar-cta .btn { width: 100%; }

/* Icon-only variant — 64px, doar iconuri; eticheta apare la hover (mai jos) */
.sidebar.icon-only {
  width: var(--sidebar-w-icon);
  padding: 16px 8px;
  align-items: center;
  /* Bula etichetei iese din rail, deci railul nu poate clipa: overflow
     visible, nu auto. Consecința, asumată: railul icon-only NU scrollează.
     La 41px pe item încap ~15 destinații în 768px de înălțime; mai multe
     înseamnă sidebar full cu secțiuni, nu un rail mai lung. */
  overflow: visible;
}
.sidebar.icon-only .sidebar-grp { display: none; }
.sidebar.icon-only .sidebar-brand {
  padding: 0 0 16px; margin-bottom: 12px;
  border-bottom: 1px solid var(--c-rule-soft);
  width: 100%; display: flex; justify-content: center;
}
.sidebar.icon-only .sidebar-brand img { height: 28px; }
.sidebar.icon-only a {
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  gap: 0; /* fără gap — altfel label-ul (0px, dar prezent) decentrează iconul */
  font-size: 0; /* hide text label */
}
.sidebar.icon-only a svg { opacity: 1; }

/* ── Eticheta la hover — rail-ul icon-only ───────────────────────────────
   Eticheta stă în DOM ca <span class="sidebar-label"> (e numele accesibil
   al linkului) și în .icon-only devine tooltip: aceeași bulă ca .tooltip —
   culoare, tipografie, rază, umbră — selectorul e grupat în secțiunea
   Tooltip; aici e doar plasarea: la dreapta railului, centrată pe item, la
   hover ȘI la :focus-visible. Fără `title` pe link: ar dubla bula cu
   tooltipul nativ al browserului. Un label bare (text, fără span) rămâne
   ascuns de font-size: 0 și n-are ce arăta la hover.
   20px = 12.5px până la marginea railului (item de 40 centrat în 47) + 8px
   aer, ca vârful săgeții să cadă la 2px de bordura railului. */
.sidebar.icon-only :is(.sidebar-trigger, .sidebar-cta .btn) { position: relative; }
.sidebar.icon-only .sidebar-label {
  left: calc(100% + 20px); top: 50%;
  transform: translateY(-50%);
  max-width: none; white-space: nowrap;
  opacity: 0;
  transition: opacity 100ms ease;
}
/* Săgeata — același pătrat de 8px rotit ca .tooltip-arrow, spre rail. */
.sidebar.icon-only .sidebar-label::before {
  content: ""; position: absolute;
  left: -4px; top: 50%;
  width: 8px; height: 8px;
  background: var(--c-accent);
  transform: translateY(-50%) rotate(45deg);
}
/* Hover cu 120ms întârziere, ca trecerea mouse-ului peste rail să nu aprindă
   toate etichetele; la ieșire dispare imediat. Focus: fără întârziere —
   tastatura a ales deja itemul. */
@media (hover: hover) {
  .sidebar.icon-only :is(a, .sidebar-trigger, .sidebar-cta .btn):hover > .sidebar-label { opacity: 1; transition-delay: 120ms; }
}
.sidebar.icon-only :is(a, .sidebar-trigger, .sidebar-cta .btn):focus-visible > .sidebar-label { opacity: 1; }

/* ───────────────────────────────────────────────────────────────
   Secțiuni colapsabile (model shadcn) — opt-in prin .sidebar-section.
   Categoria-părinte e un <button class="sidebar-trigger"> cu stil nav-link
   .t14 + icon + caret; lista de copii animă pe grid-template-rows (0fr↔1fr).
   Starea trăiește pe wrapper: data-state="open" | "closed".

   Convenții:
   - Icon DOAR pe categoria-părinte (.sidebar-trigger), nu pe copii.
   - Copiii (.sidebar-section-list a) = .t14 cu inset (aliniat sub label-ul părinte).
   - .sidebar-grp = eyebrow descriptor opțional, non-actionable.
   ─────────────────────────────────────────────────────────────── */
.sidebar-section { display: flex; flex-direction: column; }
.sidebar-section.is-end {
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid var(--c-rule-soft);
}

.sidebar-trigger {
  display: flex; align-items: center; gap: 8px;
  width: 100%;
  padding: 7px 12px; /* top-level parent — un increment lateral peste copiii inset */
  font-family: var(--font-sans);
  font-size: 14px; line-height: 20px; font-weight: 500;
  color: var(--c-fog);
  background: none; border: 0;
  border-radius: var(--r-lg);
  cursor: pointer; text-align: left;
  -webkit-appearance: none; appearance: none;
  transition: background 120ms, color 120ms;
}
/* TODO(hover-sidebar): same open question as .sidebar a:hover above. */
@media (hover: hover) {
  .sidebar-trigger:hover { background: var(--c-rule-soft); color: var(--c-ink); }
}
.sidebar-trigger:focus-visible { outline: 2px solid var(--c-pink); outline-offset: 1px; }
.sidebar-trigger svg:not(.sidebar-grp-caret) { flex-shrink: 0; opacity: 0.6; }

.sidebar-grp-caret {
  margin-left: auto; flex-shrink: 0;
  opacity: 0.5;
  transition: transform 200ms ease, opacity 120ms;
  /* Repaus explicit (REGULA CHEVRONULUI). Aici starea rotită e cea ÎNCHISĂ
     (-90°), deci deschiderea merge -90 → 0: tot în sensul acelor. Unghiul nu e
     180, deci nu era ambiguu — dar se declară din același motiv, ca sensul să nu
     depindă de cum decompune motorul o matrice. */
  transform: rotate(0deg);
}
@media (hover: hover) {
  .sidebar-trigger:hover .sidebar-grp-caret { opacity: 0.85; }
}
.sidebar-section[data-state="closed"] .sidebar-grp-caret { transform: rotate(-90deg); }

.sidebar-section-list {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 220ms cubic-bezier(0.4, 0, 0.2, 1);
}
.sidebar-section[data-state="closed"] .sidebar-section-list { grid-template-rows: 0fr; }
.sidebar-section-list > div {
  min-height: 0;
  overflow: hidden;
  display: flex; flex-direction: column;
}
/* Copiii inset — 1px și între ei, și față de trigger-ul de deasupra. Marginea
   stă pe rând (nu gap pe wrapper) ca să nu adauge înălțime când lista e închisă. */
.sidebar-section-list a { padding-left: 34px; margin-top: 1px; }

/* Icon-only: trigger devine icon-button; caret + copii ascunși */
.sidebar.icon-only .sidebar-grp-caret { display: none; }
.sidebar.icon-only .sidebar-section-list { display: none; }
.sidebar.icon-only .sidebar-trigger {
  width: 40px; height: 40px;
  justify-content: center;
  padding: 0; font-size: 0; gap: 0;
}
.sidebar.icon-only .sidebar-trigger svg:not(.sidebar-grp-caret) { opacity: 1; }
/* Secțiunea pinată jos își întinde separatorul pe toată lățimea railului, ca
   footerul — altfel linia avea 40px, cât trigger-ul. */
.sidebar.icon-only .sidebar-section.is-end { width: 100%; align-items: center; }

/* Footer în icon-only — link-urile devin icon-buttons 40×40 (icon obligatoriu în markup). */
.sidebar.icon-only .sidebar-footer {
  width: 100%; margin-top: auto;
  padding-top: 12px;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.sidebar.icon-only .sidebar-footer a {
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  padding: 0; font-size: 0; gap: 0;
}
.sidebar.icon-only .sidebar-footer a svg { opacity: 0.6; }

/* Icon-only CTA — buton primary devine icon-button 40×40 (doar iconul). */
.sidebar.icon-only .sidebar-cta {
  width: 100%; display: flex; justify-content: center;
  padding-top: 12px;
}
.sidebar.icon-only .sidebar-cta .btn {
  width: 40px; height: 40px; padding: 0;
}

@media (prefers-reduced-motion: reduce) {
  .sidebar-section-list, .sidebar-grp-caret, .sidebar.icon-only .sidebar-label { transition: none; }
}

.ds-main {
  padding: 48px 56px 120px;
  max-width: 920px;
}
.ds-page-hd {
  margin-bottom: 48px;
  padding-bottom: 24px;
  border-bottom: 1px solid var(--c-rule-soft);
}



/* ── Mobile: .tabbar — bara de navigare (< 1024px) ──────────────────────────
   Destinațiile de nivel 1 din .sidebar, cel mult cinci, mereu vizibile. Alb
   solid + hairline sus — singura bară opacă din shell. Activ: icon
   --c-accent-deep + etichetă 700, fără fill (aceeași pereche „ales" ca
   .stepper-step.active / .toggle.is-active; pink pe alb dă 3.4:1 și pică la
   11px). Inactiv: fog 500 (4.61:1 pe alb). Brandul plin apare doar pe acțiunea
   ridicată și pe contor. Al șaselea item merge în .mobile-menu. Dublin:
   Acasă · De făcut · [Cameră] · Banii tăi · Meniu — camera e acțiunea ridicată,
   Meniu deschide .mobile-menu. Fără FAB sub 1024px (vox-fab.json). */
.tabbar {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 3;
  background: var(--c-white);
  border-top: 1px solid var(--c-rule-soft);
  padding: 0 8px var(--safe-bottom, 0px);
  display: flex;
}
.tabbar-item {
  flex: 1 1 0; min-width: 0; height: 56px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  border: none; padding: 0; background: transparent;
  font-family: var(--font-sans); color: var(--c-fog);
  text-decoration: none; -webkit-appearance: none; appearance: none;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
@media (hover: hover) {
  .tabbar-item:hover { color: var(--c-ink); }
}
/* Press (2026-10-15): tab barul e doar pe telefon, unde nu există hover. Tapul
   închide culoarea (itemul curent își păstrează pinkul — regula lui vine după)
   și strânge iconul — singurul semnal și pe tabul deja curent. */
.tabbar-item:active { color: var(--c-ink); }
.tabbar-item:active .tabbar-ic { transform: scale(0.92); }
.tabbar-item.active, .tabbar-item[aria-current="page"] { color: var(--c-accent-deep); }
.tabbar-item.active .tabbar-label, .tabbar-item[aria-current="page"] .tabbar-label { font-weight: 700; }
.tabbar-item:focus-visible { outline: 2px solid var(--c-fog); outline-offset: -4px; border-radius: var(--r-md); }
.tabbar-ic {
  position: relative; width: 32px; height: 32px;
  display: flex; align-items: center; justify-content: center;
  transition: transform 100ms ease;
}
/* Glifa Lucide poartă doar .ic — bara îi dă mărimea (24) și linia (2), ca .btn. */
.tabbar-ic > .ic { width: 24px; height: 24px; stroke-width: 2; }
/* 11px pe podeaua scalei, dar NU uppercase — e eticheta unui tab, nu un .t11:
   cinci sloturi de ~60px nu țin 12px; e singurul 11 ne-uppercase din DS. */
.tabbar-label { font-size: 11px; line-height: 12px; letter-spacing: 0; font-weight: 500; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Contor — .badge.sm.filled.pink, tabular, doar numere, niciodată un punct gol.
   Inel alb de 2px îl ridică de pe glifă. Singurul loc din navigare unde contorul
   rămâne brand: aici nu e un contor la capăt de rând, ci un pip așezat PESTE
   glifă, care trebuie citit dintr-o privire de la distanța telefonului. Rândurile
   din .mobile-menu au trecut pe .badge.sm.neutral (2026-09-21) — acolo contorul
   stă la capătul unui rând alb, ca pe orice .list-row. */
.tabbar-ic > .badge {
  position: absolute; top: -2px; left: 22px;
  padding: 1px 5px; font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--c-white);
}
/* Acțiunea ridicată — perechea mobilă a lui .sidebar-cta: .btn.primary.lg.icon-only,
   44 pe scară (un al cincilea pas de mărime e interzis de button.json), ridicată
   8px peste bară. D5 din revizia 2026-09-16 a rămas deschisă pe mărime; 44 e
   implicitul până la o decizie contrară. */
.tabbar-action {
  flex: 0 0 auto; width: 72px;
  display: flex; justify-content: center; align-items: flex-start;
}
.tabbar-action > .btn { margin-top: -8px; box-shadow: var(--sh-pink); }

/* ── Mobile: .mobile-menu — restul rail-ului, deschis de itemul Meniu ────────
   Identitate, destinații grupate cu contor, rânduri de cont, rândul distructiv
   pinat jos. Overlay pe toată rama (bej-0); rândurile sunt .list-row în
   .list-group pe carduri albe — iconul fog pică la contrast pe fundalul paginii.
   Contorul e .badge.sm.neutral, aceeași regulă ca pe orice .list-row de pagină
   (list-row.json → variants.link): rândul e ALB, deci fill-ul --c-bej-1 se vede
   pe el — ΔL* 6.82, dublu față de treapta de hover pe care DS-ul o acceptă deja
   ca vizibilă (3.39, --c-bej-05); la hover rândul urcă pe --c-bej-05 și rămân
   3.43. E aceeași logică ca pe rândul .active din rail: contorul ia varianta
   care contrastează cu suprafața de sub el, iar aici suprafața e albă în ambele
   stări. Până pe 2026-09-21 era .filled.pink — o excepție de la regula lui
   .list-row, moștenită din decizia de bară albă; pink rămâne DOAR în .tabbar,
   unde contorul e un pip pe glifă, nu un contor la capăt de rând.
   Închiderea e același X (.btn.secondary.icon-only).
   Stare: [hidden] pentru închis — clasele sunt fără stare, gazda schimbă. */
.mobile-menu {
  position: absolute; inset: 0; z-index: 50;
  background: var(--c-bej-0);
  padding: calc(var(--safe-top, 0px) + 12px) var(--layout-pad-mob) calc(var(--safe-bottom, 0px) + 24px);
  display: flex; flex-direction: column; gap: 20px;
  overflow-y: auto; scrollbar-width: none;
}
.mobile-menu::-webkit-scrollbar { display: none; }
.mobile-menu[hidden] { display: none !important; }
.mobile-menu-top { display: flex; align-items: center; justify-content: space-between; height: 44px; margin: 0 -6px; flex: 0 0 auto; }
.mobile-menu-top > .logo { height: 22px; width: auto; margin-left: 6px; }
/* Rândul de identitate stă direct pe fundal, fără card și fără hairline —
   containerul dă așezarea, rândul rămâne .list-row (avatar.lg + titlu + meta) */
.mobile-menu > .list-row { border-bottom: none; padding: 4px 0; min-height: 52px; }
/* Ultimul grup (ieșirea din cont) se pinează jos */
.mobile-menu > .footer { margin-top: auto; }

/* ═══════════════════════════════════════════════════════════════
   Separator — linie de despărțire în flux.
   --c-rule (ink @ 10%), aceeași treaptă ca hairline-ul secundar din
   restul sistemului. .soft coboară la --c-rule-soft pentru separatoare
   INTERNE unei suprafețe deja delimitate (rânduri de card, meniu).
   ═══════════════════════════════════════════════════════════════ */
.separator {
  border: none; margin: 0;
  height: 1px; width: 100%;
  background: var(--c-rule);
  flex-shrink: 0;
}
.separator.soft { background: var(--c-rule-soft); }
.separator.vertical {
  height: auto; width: 1px;
  align-self: stretch; min-height: 1em;
}

/* ═══════════════════════════════════════════════════════════════
   Button group — rândul de acțiuni. UN SINGUR motor: .modal-actions
   e alias-ul lui, nu o a doua implementare (vezi secțiunea Modal).
   Desktop: orizontal, aliniat dreapta. Mobil: vertical și full-width,
   fiindcă un CTA înghesuit pe jumătate de ecran nu e un tap target.

   Ordinea e [secundar][primar] LA ORICE LĂȚIME — secundarul primul în
   DOM, primarul al doilea: la dreapta pe desktop, dedesubt pe mobil.
   REVIZUIT 2026-09-07: mobilul era `column-reverse`, ca „primarul sus,
   unde ajunge degetul primul". Efectul real: două confirmări din
   aceeași aplicație, pe același telefon, în ordini inverse — cea
   nedistructivă pe .modal-actions urca primarul deasupra dismiss-ului,
   cea distructivă (suprapusă, .stacked) îl ținea dedesubt. Ordinea unui
   rând de acțiuni nu poate depinde de lățimea ecranului: acolo se
   apasă greșit. Un singur sens, pe mobil și pe desktop.

   .stacked = acțiuni suprapuse la orice lățime (dialog de dismiss,
   dialog de ștergere): dismiss deasupra, primarul/distructivul dedesubt.
   .compact = excepția: două acțiuni CONTEXTUALE (nu CTA-uri) rămân una
   lângă alta pe mobil — cere .btn.sm, altfel nu încap.
   ═══════════════════════════════════════════════════════════════ */
.btn-group,
.modal-actions { display: flex; gap: 10px; align-items: center; }
.btn-group.end,
.modal-actions { justify-content: flex-end; }
.btn-group.full > .btn,
.modal-actions.full > .btn { flex: 1; }

/* Suprapuse — aceeași ordine, pe o coloană, la orice lățime. */
.btn-group.stacked,
.modal-actions.stacked { flex-direction: column; align-items: stretch; gap: var(--sp-8); }
.btn-group.stacked > .btn,
.modal-actions.stacked > .btn { width: 100%; }

@media (max-width: 1023.98px) {
  .btn-group,
  .modal-actions { flex-direction: column; align-items: stretch; }
  .btn-group > .btn,
  .modal-actions > .btn { width: 100%; }
  .btn-group.compact { flex-direction: row; align-items: center; }
  .btn-group.compact > .btn { width: auto; flex: 1; }
  /* .split e UN obiect, nu un rând de acțiuni — nu se desface pe coloană la nicio
     lățime. Într-un .btn-group care S-A desfăcut, splitul primește lățimea rândului
     și eticheta absoarbe surplusul, ca partea-icon să rămână pătrată. */
  .btn-group.split { flex-direction: row; align-items: center; }
  .btn-group.split > .btn { width: auto; }
  .btn-group.split > .btn:first-child { flex: 1; }
  .btn-group > .btn-group.split > .btn:first-child { flex: 1; }
}

/* ═══════════════════════════════════════════════════════════════
   .btn-group.split — DOUĂ acțiuni lipite într-un singur obiect.

   Pattern-ul „Export": o acțiune implicită scrisă (Export) plus deschiderea
   setului ei (chevron, sau o a doua acțiune strâns înrudită — Export / Setări de
   export). Restul modificatorilor lui .btn-group aranjează acțiuni SEPARATE cu
   spațiu între ele; .split e singurul unde spațiul dispare, fiindcă cele două
   jumătăți sunt același lucru. Dacă între ele ar încăpea un „și", e .btn-group
   normal, nu .split.

   Forma: pilula rămâne pe capetele EXTERIOARE, cusătura din mijloc se aplatizează.
   Două pilule alipite ar lăsa o lentilă de fundal între capetele rotunde — obiectul
   s-ar citi ca două butoane care se ating, care e exact opusul intenției.

   ─── Cusătura: un fir, nu două ───
   .outline și .outline-pink își poartă conturul ca `box-shadow: inset 0 0 0 1px`,
   nu ca `border`. Inelul e desenat ÎN interiorul cutiei, deci două butoane alăturate
   pun două fire de 1px unul lângă altul și cusătura iese de 2px — dublă exact pe
   linia care trebuie să spună „un singur obiect", și de două ori mai închisă decât
   conturul din jur. Un -1px de margine trage al doilea buton peste inelul primului:
   cele două inele cad pe același pixel și cusătura măsoară 1px, ca tot conturul.
   NU rezolva asta scoțând conturul de pe o parte — inelul e box-shadow, nu border,
   deci nu are „o parte" de scos fără să rescrii umbra pe patru laturi.

   .secondary nu are contur deloc: două fill-uri de bej-1 lipite sunt o singură pată
   fără cusătură, iar ținta de click devine invizibilă. Acolo firul trebuie DESENAT,
   nu dedublat — inset 1px doar pe muchia din stânga a jumătății a doua — și
   suprapunerea de -1px se anulează, altfel firul ar mânca un pixel din fill.

   ─── Fără altitudine ───
   .secondary se ridică 1px la hover (și .primary, dacă ajunge aici). O jumătate de
   obiect care se ridică rupe obiectul: cusătura se desface și cele două capete nu mai
   sunt pe aceeași linie de bază. Aceeași anulare, din același motiv, ca în
   .input-group. Semnalul de hover rămâne suprafața — care e oricum tot ce au
   variantele plate.
   ═══════════════════════════════════════════════════════════════ */
.btn-group.split { display: inline-flex; flex-direction: row; align-items: stretch; gap: 0; }

/* Colțurile: rotunde doar în exterior. Logice (start/end), nu stânga/dreapta —
   sistemul rulează și pe .flow-chrome, iar o cusătură nu are voie să depindă de
   direcția textului. */
.btn-group.split > .btn { border-radius: 0; }
.btn-group.split > .btn:first-child {
  border-start-start-radius: var(--r-full);
  border-end-start-radius: var(--r-full);
}
.btn-group.split > .btn:last-child {
  border-start-end-radius: var(--r-full);
  border-end-end-radius: var(--r-full);
}

/* Inelele celor două variante cu contur cad pe același pixel. */
.btn-group.split > .btn + .btn { margin-inline-start: -1px; }

/* Butonul de sub cursor trece deasupra vecinului, ca inelul lui de hover
   (--c-rule-hover, o treaptă mai închis) să nu fie tăiat de cel care stă peste el.
   :focus-visible la fel — outline-ul e desenat în afara cutiei și s-ar tăia. */
.btn-group.split > .btn:focus-visible { position: relative; z-index: 1; }
@media (hover: hover) {
  .btn-group.split > .btn:hover:not(:disabled) { position: relative; z-index: 1; }
}

/* Altitudinea se anulează — vezi nota de mai sus. */
@media (hover: hover) {
  .btn-group.split > .btn:hover:not(:disabled) { transform: none; }
  .btn-group.split > .btn.primary:hover:not(:disabled),
  .btn-group.split > .btn.secondary:hover:not(:disabled) { box-shadow: none; }
}

/* .secondary: firul se desenează, fiindcă varianta nu aduce niciun contur.
   Declarat și pe :hover ca să supraviețuiască lui `box-shadow: none` de mai sus —
   la hover cade umbra de altitudine, nu cusătura. */
.btn-group.split > .btn.secondary + .btn.secondary,
.btn-group.split > .btn.secondary + .btn.secondary:active:not(:disabled) {
  margin-inline-start: 0;
  box-shadow: inset 1px 0 0 var(--c-rule);
}
@media (hover: hover) {
  .btn-group.split > .btn.secondary + .btn.secondary:hover:not(:disabled) {
    margin-inline-start: 0;
    box-shadow: inset 1px 0 0 var(--c-rule);
  }
}

/* ═══════════════════════════════════════════════════════════════
   Context menu — .popover, compact.

   REVISED 2026-09-02, superseding the 2026-08-26 rationale ("the menu that
   appears UNDER the cursor, over dense content, so it must take up less than
   one opened from a button"). That reading stepped DOWN EVERY AXIS, type
   included: 14→13 on the row, 11→10 on the label. The type step-down does not
   hold. Popover, dropdown menu, select list and context menu are one container
   with one kind of row; what differs is the trigger — an input, a button, a
   right-click. A menu row is a menu-item at page type wherever it is opened
   from, and 13px was a second reading size for the same object.

   What survives is the density that is STRUCTURAL, not typographic: the menu
   is anchored to a row of dense content rather than to a field, so it holds a
   narrower column (min-width 168px vs 200px) and hugs its rows more closely
   (container padding 4 vs 6, row padding 6/8 vs 8/10). The radius step
   (surface --r-md, row --r-sm) is that same geometry: 14 − 4 = 10, exactly
   --r-sm on the row, the way 20 − 6 = 14 gives --r-md in the default popover.
   ═══════════════════════════════════════════════════════════════ */
/* ─── .popover.lg — lista perechii de 44px ───
   Treapta `.lg` există la trigger (44px / 16px) și la `.input`, dar lista rămăsese
   pe metricile implicite: un meniu de rânduri de 36px la 14px, deschis de sub un
   control de 44px la 16px. Valoarea se citea la o mărime în buton și la alta în
   listă, adică exact în momentul în care o compari cu celelalte.

   Metricile urmează relațiile pe care sistemul le are deja, nu numere noi:
     · rândul e cu 4px mai scund decât trigger-ul care l-a deschis, ca la implicit
       (trigger 40 → rând 36; trigger 44 → rând 40)
     · textul rândului = textul trigger-ului (16px), ca la implicit (14 = 14)
     · raza suprafeței MINUS padding-ul containerului = raza rândului, regula
       scrisă la `.popover.compact`: 24 − 10 = 14, exact --r-md, cum 20 − 6 = 14
       la implicit și 14 − 4 = 10 la compact
   Deci: suprafață --r-xl + padding 10, rând --r-md + padding 8/14, min-width 240.

   Se aplică singură pe forma din DOM — `.select-anchor:has(> .select-trigger.lg)`
   mai jos — ca să nu existe două locuri unde scrie „e mare". Forma React n-are
   ancoră (Radix portalează lista în <body>), deci acolo clasa vine din componentă:
   `<Select size="lg">` o pune pe amândouă. ─── */
.popover.lg { border-radius: var(--r-xl); padding: 10px; min-width: 240px; gap: 2px; }
.popover.lg .popover-item,
.command:has(> .command-input > .input.lg) .popover-item {
  padding: 8px 14px;
  border-radius: var(--r-md);
  font-size: 16px; line-height: 24px;
}
.popover.lg .popover-label,
.command:has(> .command-input > .input.lg) :is(.popover-label, [cmdk-group-heading]) { padding: 10px 14px 6px; }
/* În comanda mare rândul și eticheta țin padding-ul lateral al rândului implicit
   (10), nu pe cel al lui .popover.lg (14): coloana iconițelor cade sub iconița
   câmpului, care stă la 20 — regula lui .input-group.lg — deci 10 (listă) + 10.
   Raza rămâne concentrică: 24 − 10 = 14. Treapta (16/24, 8 sus/jos) nu se mișcă. */
.command:has(> .command-input > .input.lg) .popover-item,
.command:has(> .command-input > .input.lg) :is(.popover-label, [cmdk-group-heading]) { padding-inline: 10px; }
.popover.lg .popover-sep,
.command:has(> .command-input > .input.lg) .popover-sep { margin: 6px 0; }
.popover.lg:has(.popover-check) .popover-item { padding-inline-end: 42px; }
.popover.lg > .popover-item > .popover-check { inset-inline-end: 14px; }
/* Lista nu poate fi mai îngustă decât controlul care a deschis-o. Un meniu de
   240px sub un trigger full-width arată ca al altcuiva — și e chiar problema pe
   care Radix o rezolvă cu --radix-select-trigger-width, setat în react/select.tsx.
   `max()` păstrează și podeaua proprie: lista unui trigger îngust (un filtru de
   90px) rămâne lizibilă la 200px, nu se strânge după buton. */
.select-anchor > .popover { min-width: max(200px, 100%); }

/* Forma scrisă de mână: lista mare se deduce din trigger, nu se scrie a doua oară. */
.select-anchor:has(> .select-trigger.lg) > .popover { border-radius: var(--r-xl); padding: 10px; min-width: max(240px, 100%); }
.select-anchor:has(> .select-trigger.lg) > .popover > .popover-item {
  padding: 8px 14px;
  border-radius: var(--r-md);
  font-size: 16px; line-height: 24px;
}
.select-anchor:has(> .select-trigger.lg) > .popover > .popover-label { padding: 10px 14px 6px; }
.select-anchor:has(> .select-trigger.lg) > .popover > .popover-sep { margin: 6px 0; }
.select-anchor:has(> .select-trigger.lg) > .popover:has(.popover-check) > .popover-item { padding-inline-end: 42px; }
.select-anchor:has(> .select-trigger.lg) > .popover > .popover-item > .popover-check { inset-inline-end: 14px; }

.popover.compact { border-radius: var(--r-md); padding: 4px; min-width: 168px; }
.popover.compact .popover-item {
  padding: 6px 8px;
  border-radius: var(--r-sm);
}
.popover.compact .popover-label { padding: 6px 8px 3px; }
.popover.compact .popover-sep { margin: 3px 0; }

/* ═══════════════════════════════════════════════════════════════
   Modal — părțile formalizate. Suprafața și scrimul sunt mai sus;
   astea sunt sloturile pe care orice modal le are oricum, ca să nu fie
   reinventate per ecran.
   .modal.confirm = varianta „alert dialog" din shadcn: conținut simplificat
   (titlu + o propoziție), fără X, fiindcă o întrebare blocantă cere un
   răspuns explicit — nu o ieșire laterală.

   NU se numește .modal.alert, deși ăsta era numele evident: `.alert` e deja
   o COMPONENTĂ, iar `.modal.alert` prinde și regula ei — care e
   `display: flex`, deci titlul, textul și butoanele se așează pe un rând.
   Un modificator nu poate purta numele unei componente existente.
   ═══════════════════════════════════════════════════════════════ */
/* Tipografia sloturilor NU stă aici: .modal-title e .t28 și .modal-body e .t14,
   grupate în regulile lor din scală (secțiunea Tipografie). Slotul dă doar
   spațierea — 8px sub titlu, 40px la dreapta ca titlul să nu intre sub X,
   24px sub descriere.
   Până la 2026-09-25 sloturile aveau mărimi proprii, în afara scalei (titlu
   20/600, descriere 15/22/400), în timp ce modal.md și markup-ul din modal.json
   prescriau .t28 + .t14 pentru aceleași două roluri: două rețete pentru un slot,
   iar aplicațiile le foloseau pe amândouă. */
.modal-title { margin: 0 0 8px; padding-right: 40px; }
.modal-body { margin: 0 0 24px; }
.modal-body:last-child { margin-bottom: 0; }

/* .modal-actions nu are reguli proprii — e alias-ul lui .btn-group, declarat
   în secțiunea Button group. Un modal nu primește alt rând de acțiuni decât
   restul aplicației, așa că nici nu are alt CSS. Suprapuse: .modal-actions.stacked. */

.modal.confirm { max-width: 400px; padding: 28px 28px 24px; }
.modal.confirm > .btn.icon-only { display: none; }
.modal.confirm .modal-title { padding-right: 0; }
.modal.confirm .modal-body { margin-bottom: 22px; }

/* ═══════════════════════════════════════════════════════════════
   Modal în pași — .modal.stepped (2026-10-13)
   Un flux scurt (editare → … → confirmare) într-UN SINGUR modal. Înlocuiește
   confirmarea deschisă PESTE modalul de editare („Nu modal peste modal").
   Ordinea în container: X · .modal-title · .stepper · .modal-step (descrierea
   și câmpurile pasului) · .modal-actions.
   Fereastra are ACEEAȘI înălțime la toți pașii: --modal-stepped-h, pusă de
   flux la înălțimea celui mai înalt pas (ex. 700px pe .modal.lg cu 4 pași).
   Între pași se schimbă doar conținutul din .modal-step; rândul de acțiuni
   stă jos, pe loc, deci butoanele nu sar. Un pas mai înalt decât fereastra
   se derulează în .modal-step, nu împinge butoanele.
   ═══════════════════════════════════════════════════════════════ */
.modal.stepped {
  display: flex; flex-direction: column;
  height: var(--modal-stepped-h, auto);
  max-height: calc(100dvh - 64px);            /* 2 × padding-ul .modal-backdrop */
}
.modal.stepped > .modal-title { margin-top: 4px; }   /* fără eyebrow: +4px sus, ca la orice modal */
/* Pașii stau SUB titlu: 8px din slotul titlului + 16 = 24 deasupra, 28 dedesubt. */
.modal.stepped > .stepper { margin: var(--sp-16) 0 var(--sp-28); }
/* Corpul pasului. Marginea negativă + padding-ul de 4px țin halo-ul de focus
   al câmpurilor (3px) în interiorul zonei derulabile, ca să nu fie tăiat. */
.modal.stepped > .modal-step {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  margin: 0 calc(-1 * var(--sp-4)); padding: 0 var(--sp-4) var(--sp-4);
}
.modal.stepped > .modal-actions { margin-top: auto; padding-top: var(--sp-24); }
/* 5 pași: stepperul ia treapta compactă din .app-top (chip 26, label 13, gap 22),
   ca să încapă pe un rând în .modal.lg. Fără clasă nouă: o numără CSS-ul. */
.modal.stepped > .stepper:has(> .stepper-step:nth-child(5)) { gap: 22px; flex-wrap: nowrap; }
.modal.stepped > .stepper:has(> .stepper-step:nth-child(5)) .stepper-step { font-size: 13px; white-space: nowrap; }
.modal.stepped > .stepper:has(> .stepper-step:nth-child(5)) .stepper-ind { width: 26px; height: 26px; font-size: 12px; }

/* ═══ Record view — o înregistrare citită într-un modal (PATTERN, 2026-10-07) ═══
   Compoziția lui RecordViewDialog din Dublin: eticheta felului (.t-eyebrow),
   numele înregistrării (.modal-title), o descriere opțională (.modal-body)
   și corpul — un .quantum-stack. Doar citire: fără rând de acțiuni, iar un
   click în afară închide. Antetul apare imediat, din rândul din care s-a
   deschis; doar corpul se încarcă, iar starea de încărcare sau de eroare îi
   ține locul (.record-view-state).
   Măsurile sunt ale lui Dublin: 440px (40 sub cei 480 ai modalului — perechile
   etichetă–valoare au nevoie de mai puțină lățime decât un formular, iar un
   card mai îngust ține eticheta aproape de valoare), 8px sub etichetă, 6px
   între titlu și descriere, 16px până la corp, cu sau fără descriere, iar
   starea are înălțimea a trei grupuri de un rând (192px), ca modalul să nu
   sară când sosește corpul. Suprafața, padding-ul și X-ul sunt ale .modal. */
.modal.record-view { max-width: 440px; }
.record-view > .t-eyebrow { display: flex; margin-bottom: 8px; }
/* Numărul înregistrării după felul ei — „● FACTURĂ YB-0142" — în cifre
   tabulare. Opțional: o cheltuială n-are. Dublin pune 6px de margine peste
   gap-ul de 8px al .t-eyebrow, deci numărul stă la 14px de fel (măsurat). */
.record-view-ref { margin-left: 6px; font-variant-numeric: tabular-nums; }
.record-view > .modal-title { margin-bottom: 0; }
.record-view > .modal-body { margin: 6px 0 0; }
.record-view > .quantum-stack,
.record-view > .record-view-state { margin-top: 16px; }
.record-view-state { display: grid; place-items: center; min-height: 192px; }

/* ═══════════════════════════════════════════════════════════════
   Fieldset — GRUPUL de câmpuri. .field e un câmp (label + control);
   .fieldset e colecția. Numele diferite fac diferența vizibilă în
   markup, care e exact locul unde se confundau.
   ═══════════════════════════════════════════════════════════════ */
.fieldset { display: flex; flex-direction: column; gap: 18px; border: none; padding: 0; margin: 0; }
.fieldset-legend {
  font-family: var(--font-sans);
  font-size: 13px; font-weight: 600; line-height: 18px;
  color: var(--c-ink); padding: 0; margin: 0 0 2px;
}
/* Cei -12px anulează parțial gap-ul de 18px al grupului, ca descrierea să stea
   ancorată de titlu (6px) și nu la distanță de câmp. Funcționează DOAR când
   titlul e un <div class="fieldset-legend"> — adică un item flex, deci cu gap
   înaintea descrierii. Un <legend> REAL nu e item flex: browserul îl scoate din
   fluxul de flex și îl pune deasupra content box-ului, deci nu există gap de
   anulat și cei -12px trag descrierea PESTE titlu (măsurat: 10px suprapunere).
   De asta al doilea selector există — și de asta markupul canonic folosește
   <legend>, care e ce anunță cititorul de ecran la fiecare câmp din grup. */
.fieldset-help {
  font-family: var(--font-sans);
  font-size: 13px; line-height: 18px; color: var(--c-fog); margin: -12px 0 0;
}
.fieldset > legend + .fieldset-help { margin-top: 0; }

/* Separatorul dintre două câmpuri ale aceluiași grup (shadcn `FieldSeparator`)
   NU e o clasă nouă: e <hr class="separator soft"> ca direct copil de .fieldset.
   Gap-ul de 18px cade de ambele părți, deci linia stă centrată fără margini
   proprii. Nu se folosește .separator (--c-rule) în interiorul unui grup —
   hairline-ul tare separă secțiuni, nu câmpuri înrudite. */

/* Field row — câmpuri ÎNRUDITE pe același rând, lățimi egale (shadcn: un
   FieldGroup orizontal). Gap-ul e 12px, nu 18px ca la .fieldset: pe orizontală
   câmpurile sunt mai apropiate decât pe verticală, altfel rândul se rupe vizual.

   REDENUMIT în v8 din .input-group. Motivul e o coliziune de nume, nu estetica:
   în shadcn, `InputGroup` e un input cu addon-uri ÎN interiorul bordurii, iar
   rândul de câmpuri e treaba lui `FieldGroup`. Numele nostru vechi spunea un
   lucru și arăta altul, deci .input-group a trecut la sensul din shadcn (mai jos,
   secțiunea Input group) și rândul a primit numele care îl descrie. Vechiul sens
   NU supraviețuiește ca alias — un alias pe un nume reutilizat ar face exact
   greșeala pe care redenumirea o repară. Era status: wip, deci nimeni nu se
   sprijinea pe el prin contract. */
.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); gap: 12px; }
.field-row.cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.field-row.cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.field-row.grow-last { grid-template-columns: auto minmax(0, 1fr); }
@media (max-width: 1023.98px) {
  .field-row.cols-3, .field-row.grow-last { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ═══════════════════════════════════════════════════════════════
   OTP — celule pe .input, cu MĂRIMEA și FORMA schimbate. Atât.
   Suprafața NU e o decizie a OTP-ului: celula e un .input ca oricare
   altul, deci regula de context e cea din docs/components/input.md —
   alb pe card bej-1, `.tonal` pe card alb. De aceea blocul ăsta nu
   declară nici `background`, nici `border-color`: dacă le-ar declara,
   OTP-ul ar fi singurul câmp din DS care ignoră cardul de sub el.

   Mărime: o treaptă peste .input.lg pe X, trei pe Y — celula 48 lățime
   (44→48) x 56 înălțime (44→48→52→56), cifra 16→18→20. Motivul e cel
   de dinainte: câmpul de OTP e de obicei singurul din pagină, nu
   concurează cu nimic și poate respira. Treptele merg aproape toate pe Y
   fiindcă axele nu au același buget: lățimea e plătită de rând (șase
   celule + separator trebuie să încapă pe un telefon), înălțimea nu e
   plătită de nimic. Celula NU e pătrată — e un dreptunghi vertical, care
   ține și cifra mai bine centrată optic.
   ═══════════════════════════════════════════════════════════════ */
.otp { display: flex; gap: 8px; }
.otp .input {
  /* 48 e lățimea, nu un minim: `flex: 0 1 48px` nu crește niciodată peste ea,
     dar cedează dacă rândul nu încape — șase celule de 48 + gap + separator
     cer ~355px, peste lățimea utilă a unui telefon. `min-width: 0` e obligatoriu:
     fără el un <input> refuză să scadă sub lățimea lui intrinsecă (~20 caractere)
     și rândul iese din container în loc să se strângă. Înălțimea NU cedează:
     56 e fix pe orice lățime, altfel celulele s-ar turti pe telefon exact unde
     tap target-ul contează mai mult. O singură regulă pentru toate lățimile —
     pe mobil se schimbă doar gap-ul. */
  flex: 0 1 48px; min-width: 0;
  width: 48px; height: 56px;
  padding: 0; text-align: center;
  /* .input e --r-lg (20px); pe o celulă atât de mică asta dă aproape o pastilă.
     O celulă de cod e un dreptunghi rotunjit, nu o pastilă — o treaptă în jos,
     la --r-md. Radius-ul rămâne --r-md la orice mărime a celulei: forma e o
     decizie separată de mărime, nu una care scalează cu ea. */
  border-radius: var(--r-md);
  font-size: 20px; font-weight: 500;
  font-variant-numeric: tabular-nums;
}
.otp-sep { align-self: center; color: var(--c-mist); padding: 0 2px; }
/* Doar gap-ul. Înălțimea rămâne 56 și lățimea se strânge singură, din
   `flex: 0 1 48px` de mai sus — nu e nevoie de mărimi separate pe mobil. */
@media (max-width: 1023.98px) {
  .otp { gap: 6px; }
}

/* ═══════════════════════════════════════════════════════════════
   Toggle — buton-icon care ține o stare (v8, 2026-09-08).

   REGULA FORMEI, singura care trebuie ținută minte:
     forma se schimbă DOAR acolo unde nu există un container care să
     declare setul. Solo n-are șină, deci forma ține locul ei: cercul
     se strânge și se umple. În grup, carcasa declară deja setul, deci
     itemul rămâne buton rotund și doar culoarea se schimbă.

   Consecința care contează: `plin de brand = selectat` are UN singur
   înțeles, în ambele contexte. Varianta cu șina plină de roz (respinsă)
   făcea alb-pe-roz să însemne „oprit" în grup și „pornit" la solo.

   De ce contur în repaus: până aici starea de repaus era transparentă,
   deci vasul nu exista și controlul nu se citea ca fiind clicabil.
   Pragul non-text pentru conturul unui control e 3:1 (WCAG 1.4.11), pe
   fundalul REAL de pagină (--c-bej-0). Măsurat acolo: --c-rule (0.10) dă
   1.24:1 și --c-input-border (0.30) dă 2.00:1 — amândouă pică.
   --c-input-border-tonal (0.46) dă 3.13:1 și trece. De asta conturul
   toggle-ului NU e cel al lui .btn.outline.

   Culorile stării selectate: alb pe --c-accent = 3.67:1. Trece, fiindcă
   un icon e non-text (prag 3:1) — spre deosebire de tooltip, care poartă
   TEXT de 13px și de asta stă la 3.67 ca abatere declarată, nu ca normă.
   Hoverul urcă pe --c-accent-deep (alb = 5.39:1), exact ca .switch.on.
   ═══════════════════════════════════════════════════════════════ */
.toggle {
  --tg-size: 30px;
  --tg-icon: 14px;
  --tg-stroke: 1.5;  /* linia iconului, pe scara optică din secțiunea Icons */
  /* Raza stării selectate. Urcă o treaptă odată cu cutia — vezi scara. */
  --tg-r-on: var(--r-sm);
  /* Raza de repaus e jumătatea cutiei SCRISĂ CA LUNGIME, nu --r-full. Cercul
     iese identic (15 pe 30, 13 pe 26), dar morph-ul devine animabil, și asta
     e tot ce ținea animația în loc: border-radius interpolează numeric, iar
     browserul TAIE valoarea la jumătatea cutiei. De la 9999px drumul până sub
     15px se face în ultima jumătate de procent din durată — restul animației e
     invizibil, deci forma sărea la final în loc să curgă. De la 15px drumul e
     15 → 10 și fiecare cadru se vede. Docul spunea deja „morph de 3px la .xs",
     adică jumătatea cutiei; aici codul ajunge din urmă intenția. */
  --tg-r-off: calc(var(--tg-size) / 2);
  /* O durată și o curbă pentru TOATE proprietățile stării. Înainte forma mergea
     pe 180ms și culoarea pe 140ms, cu `ease` implicit pe fiecare: umplerea se
     termina înaintea formei, deci tranziția se citea în doi timpi.

     Curba e cea standard, aceeași cu a accordionului — NU cubic-bezier(.2,.8,.2,1)
     de la modal/toast. Aia e o curbă de INTRARE: ceva care apare vrea să frâneze
     tare la final. Măsurat pe morph-ul de aici, consumă 88% din drum în prima
     treime din durată și pe urmă plutește — mișcarea se strânge la început și
     restul cadrelor stau pe loc, exact senzația de „nu curge". Standardul
     consumă 55% în prima treime, deci se mișcă pe toată durata. O tranziție
     între două stări ținute nu intră în scenă, ci trece dintr-una în alta. */
  --tg-dur: 180ms;
  --tg-ease: cubic-bezier(0.4, 0, 0.2, 1);

  width: var(--tg-size); height: var(--tg-size); padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  background: transparent; color: var(--c-fog);
  border: none; border-radius: var(--tg-r-off);
  box-shadow: inset 0 0 0 1px var(--c-input-border-tonal);
  cursor: pointer;
  /* `filter` e în listă fiindcă :active îl folosește; fără el, apăsarea și
     ridicarea degetului erau singurele salturi rămase. */
  transition: background var(--tg-dur) var(--tg-ease),
              color var(--tg-dur) var(--tg-ease),
              border-radius var(--tg-dur) var(--tg-ease),
              box-shadow var(--tg-dur) var(--tg-ease),
              filter var(--tg-dur) var(--tg-ease);
}
.toggle > svg { width: var(--tg-icon); height: var(--tg-icon); stroke-width: var(--tg-stroke); display: block; }
.toggle > svg > * { vector-effect: non-scaling-stroke; }

/* Scara e a butonului-icon: 26 / 30 / 40 / 44, cu iconul 14 pe treptele
   dense și 18 pe cele mari (aceeași decizie ca .btn.icon-only).

   Raza selectatului urcă o singură treaptă, la 40. Motivul e proporția, nu
   gustul: morph-ul se citește ca raport între rază și JUMĂTATEA cutiei. Cu o
   rază unică (--r-sm peste tot) raportul ar aluneca 0.77 → 0.67 → 0.50 → 0.45,
   adică de două ori mai agresiv sus decât jos. Cu treapta la 40 stă în
   0.77 / 0.67 / 0.70 / 0.64 — aceeași senzație pe toată scara. E aproximarea
   în tokeni a unei raze procentuale, fără să iasă din sistem.

   --r-md NU poate fi raza selectatului pe treptele dense: la 30px jumătatea
   cutiei e 15, deci 14 lasă 1px de diferență față de cerc, iar la 26px
   14 > 13 și browserul o taie înapoi la cerc — starea selectată n-ar mai
   schimba nimic în formă exact pe treptele care ajung pe bare. */
.toggle.xs { --tg-size: 26px; }
.toggle.md { --tg-size: 40px; --tg-icon: 18px; --tg-stroke: 1.75; --tg-r-on: var(--r-md); }
.toggle.lg { --tg-size: 44px; --tg-icon: 18px; --tg-stroke: 1.75; --tg-r-on: var(--r-md); }

@media (hover: hover) {
  .toggle:hover:not(:disabled) { background: var(--c-bej-1); color: var(--c-ink); }
}
/* Pressul se citește pe contur și pe suprafață, ca la .btn.outline: variantele
   plate n-au altitudine de anulat. */
.toggle:active:not(:disabled) {
  background: var(--c-bej-1);
  box-shadow: inset 0 0 0 1px var(--c-input-border-hover);
  filter: brightness(0.96);
}
.toggle:disabled { opacity: 0.4; cursor: not-allowed; }

/* Selectat — forma se strânge, fundalul devine brand, conturul dispare
   (umplerea ia toată forma; un inel peste ea ar face butonul să pară mai mic).

   FĂRĂ LIFT, deși e o suprafață plină de brand ca .btn.primary. O stare ȚINUTĂ
   care levitează citește ca „e pe cale să pornească", iar într-un grup un item
   ridicat rupe șina. Luăm culorile primarului, nu și altitudinea lui. */
.toggle[aria-pressed="true"] {
  border-radius: var(--tg-r-on);
  background: var(--c-accent); color: var(--c-white);
  /* `transparent`, nu `none`: aceeași geometrie, doar culoarea inelului se
     stinge. Spre `none` se interpolează și spread-ul 1px → 0, adică inelul se
     strânge spre centru în timp ce fundalul se umple — două mișcări în locul
     uneia. Rezultatul final e identic, un inel transparent peste umplere. */
  box-shadow: inset 0 0 0 1px transparent;
}
@media (hover: hover) {
  .toggle[aria-pressed="true"]:hover:not(:disabled) { background: var(--c-accent-deep); color: var(--c-white); }
}
.toggle[aria-pressed="true"]:active:not(:disabled) { background: var(--c-accent-deep); filter: brightness(0.94); }

/* Grup segmentat — carcasa poartă conturul O SINGURĂ dată și itemii i-l predau,
   exact modelul lui .input-group. Rămâne pilulă: itemul neselectat e un buton în
   repaus care așteaptă să fie apăsat, iar șina e ce spune că formează un set.
   Selecția e exclusivă, deci membrii sunt role="radio" + aria-checked, NU
   aria-pressed — vezi anti_patterns din components/toggle.json. */
.toggle-group {
  display: inline-flex; gap: 2px;
  padding: 3px; background: transparent;
  border-radius: var(--r-full);
  box-shadow: inset 0 0 0 1px var(--c-input-border-tonal);
}
.toggle-group .toggle { box-shadow: none; }
/* O SINGURĂ treaptă în grup, deocamdată — pentru icon și pentru text: cea implicită,
   30px (decizia ownerului DS, 2026-10-14). Scara 26/30/40/44 rămâne a toggle-ului
   SOLO. Un .xs / .md / .lg pus pe un item de grup e ignorat: variabilele revin la
   valorile implicite ale lui .toggle, deci grupul randează 30 orice ar scrie markup-ul. */
.toggle-group .toggle:is(.xs, .md, .lg) {
  --tg-size: 30px; --tg-icon: 14px; --tg-stroke: 1.5; --tg-r-on: var(--r-sm);
}
.toggle-group .toggle[aria-checked="true"] {
  /* Aceeași rază ca în repaus, ca să nu rămână nimic de interpolat: în grup
     șina declară setul, deci doar culoarea se schimbă. */
  border-radius: var(--tg-r-off);
  background: var(--c-accent); color: var(--c-white);
}
@media (hover: hover) {
  .toggle-group .toggle[aria-checked="true"]:hover:not(:disabled) { background: var(--c-accent-deep); color: var(--c-white); }
}

/* ── .toggle-group.text — itemi cu text scurt în loc de icon (2026-10-14) ──
   Coduri și acronime de câteva caractere: D300, D394, TVA, CAS. Același grup —
   radiogroup, radio + aria-checked, roving tabindex, carcasa cu contur o dată —
   și aceeași regulă a formei: în grup forma nu face morph, se schimbă doar
   culoarea. O singură treaptă, ca orice grup: 30px înălțime, 10px aer lateral;
   lățimea îmbracă textul.

   Selectatul e IDENTIC cu al grupului cu iconițe: --c-accent + text alb, hover
   --c-accent-deep — nu are regulă proprie. Alb pe accent dă 3.67:1, sub pragul
   de 4.5:1 pentru text de 13px: ABATERE DECLARATĂ, decizia ownerului DS
   (Octavian, 2026-10-14) — alb pe brand pink e suficient perceptual, deci niciun
   control nu trece pe pink-deep ca să urce contrastul. Aceeași familie de
   excepție ca tooltipul (3.67 la 13px) și eticheta .btn.primary.

   Repausul e text --c-ink, nu --c-fog ca iconul: fog trece ca text doar pe alb
   (4.56:1) și pică pe bej-0 (4.37) și pe bej-1 (3.84), iar grupul e transparent,
   deci stă pe orice gazdă. Ink: 18.48 / 19.29 / 16.23.

   Cifre tabulare (Inter, nu mono): D300 și D301 au aceeași lățime, deci un cod
   nu-și mișcă vecinii când se schimbă o cifră și coloana de coduri se citește
   aliniată; mono ar aduce o a doua familie într-un rând de controale Inter.

   Numele complet („D300 — Decont de TVA") e aria-label pe item — un acronim nu
   e un nume accesibil — și tot el apare la hover / focus, în bula tooltipului
   (selectorul e grupat în secțiunea Tooltip). O singură sursă: bula citește
   attr(aria-label), deci numele auzit și cel văzut nu pot diverge. Fără săgeată:
   ::before e stratul de atins de sub 1024px. */
.toggle-group.text .toggle {
  position: relative;
  width: auto; min-width: var(--tg-size); padding: 0 10px;
  color: var(--c-ink);
  font-family: var(--font-sans); font-size: 13px; font-weight: 500;
  line-height: 1; letter-spacing: 0; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
/* Doar culoarea textului: regula de sus (--c-ink) ar bate altfel albul selectatului,
   la aceeași specificitate și mai jos în fișier. Fundalul rămâne al grupului. */
.toggle-group.text .toggle[aria-checked="true"] { color: var(--c-white); }
/* Bula — plasarea .tooltip.top; primul și ultimul item o lipesc de marginea lor
   (ca .start / .end), ca bula unui grup aliniat la stânga să nu iasă din ecran. */
.toggle-group.text .toggle[aria-label]::after {
  content: attr(aria-label);
  bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  width: max-content; white-space: normal; /* itemul e nowrap; bula se rupe la 260, ca .tooltip */
  opacity: 0; transition: opacity 100ms ease;
}
.toggle-group.text .toggle[aria-label]:first-child::after { left: 0; transform: none; }
.toggle-group.text .toggle[aria-label]:last-child::after { left: auto; right: 0; transform: none; }
@media (hover: hover) {
  .toggle-group.text .toggle[aria-label]:hover::after { opacity: 1; transition-delay: 120ms; }
}
.toggle-group.text .toggle[aria-label]:focus-visible::after { opacity: 1; }

/* ═══════════════════════════════════════════════════════════════
   Tooltip — ajutor scurt la hover/focus, pe fundal de brand.
   Fundalul e --c-accent. Alb pe el dă 3.67:1, sub AA la 13px — decizie luată
   în cunoștință de cauză (2026-08-27): momentul în care explici ceva e un
   moment de brand, și tooltipul poartă pinkul. Nu e o scăpare; dacă apare
   într-un audit, ăsta e răspunsul. Alternativa măsurată, dacă se răzgândește
   cineva: --c-accent-deep, 5.39:1.
   ═══════════════════════════════════════════════════════════════ */
/* Aceeași bulă, a doua plasare: eticheta din rail-ul icon-only —
   .sidebar.icon-only .sidebar-label (secțiunea Sidebar dă doar poziția).
   A treia: numele complet al unui item din .toggle-group.text, citit din
   aria-label (secțiunea Toggle dă poziția). */
.tooltip,
.sidebar.icon-only .sidebar-label,
.toggle-group.text .toggle[aria-label]::after {
  position: absolute; z-index: 40;
  max-width: 260px; padding: 7px 11px;
  background: var(--c-accent); color: #fff;
  border-radius: var(--r-sm);
  font-family: var(--font-sans);
  font-size: 13px; line-height: 18px; font-weight: 400;
  box-shadow: var(--sh-md);
  pointer-events: none;
}
.tooltip strong { color: #fff; font-weight: 600; }
/* Bula stă în .tooltip-wrap (relative) și e ASCUNSĂ până la hover / focus — ca
   eticheta din rail: 120ms întârziere la hover (mouse-ul care trece nu aprinde
   bule), imediat la focus (tastatura a ales deja). Rămâne în DOM, deci
   aria-describedby o citește oricând. width: max-content — un absolut într-un
   wrap de 80px și-ar rupe altfel textul la lățimea declanșatorului.
   .is-open o ține deschisă fără hover / focus: documentație, teste, JS.

   TOUCH (2026-10-15). Hoverul e închis în @media (hover: hover): pe telefon,
   un tap nu mai lasă bula aprinsă până la următorul tap. Focusul aprinde bula
   doar când e :focus-visible (tastatura), nu :focus-within — Android Chrome dă
   focus butonului la tap, deci :focus-within ar fi readus exact bula lipită.
   Pe telefon conținutul se atinge așa:
   - toggletip — un declanșator care DOAR explică (ⓘ, „Ce e CUI-ul?"). Tapul /
     clickul comută .is-open; tap în afară sau Esc închide. Nu e o clasă nouă, e
     comportament: react/tooltip.tsx (`toggle`). Merge la fel cu mouse-ul: hover
     arată, click fixează.
   - un buton cu acțiune (icon-only, „Descarcă PDF") NU comută: tapul îi face
     acțiunea, iar numele stă în aria-label. Iconul trebuie să se înțeleagă
     singur pe telefon — dacă nu, butonul primește text. */
.tooltip-wrap { position: relative; display: inline-flex; }
.tooltip { width: max-content; opacity: 0; transition: opacity 100ms ease; }
@media (hover: hover) {
  .tooltip-wrap:hover > .tooltip { opacity: 1; transition-delay: 120ms; }
}
.tooltip-wrap:has(:focus-visible) > .tooltip, .tooltip-wrap.is-open > .tooltip { opacity: 1; }
/* Latura — o clasă, nu left/top inline: 8px aer între declanșator și bulă,
   bula centrată pe el. .top e implicita documentației (cel mai des e loc sus);
   .bottom când sus nu e; .left / .right pentru iconuri lipite de o margine. */
.tooltip.top    { bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); }
.tooltip.bottom { top: calc(100% + 8px);    left: 50%; transform: translateX(-50%); }
.tooltip.left   { right: calc(100% + 8px);  top: 50%;  transform: translateY(-50%); }
.tooltip.right  { left: calc(100% + 8px);   top: 50%;  transform: translateY(-50%); }
/* Alinierea pe sus / jos: implicit centrată; .start / .end lipesc bula de
   marginea stângă / dreaptă a declanșatorului — pentru declanșatoare la
   marginea ecranului, unde bula centrată ar ieși din viewport. */
.tooltip.top.start, .tooltip.bottom.start { left: 0;    transform: none; }
.tooltip.top.end,   .tooltip.bottom.end   { left: auto; right: 0; transform: none; }
/* Săgeata — același pătrat de 8px rotit ca la eticheta din rail; latura o
   așază la mijlocul muchiei dinspre declanșator, .start / .end la 16px. */
.tooltip-arrow {
  position: absolute; width: 8px; height: 8px;
  background: var(--c-accent); transform: rotate(45deg);
}
.tooltip.top > .tooltip-arrow    { bottom: -4px; left: calc(50% - 4px); }
.tooltip.bottom > .tooltip-arrow { top: -4px;    left: calc(50% - 4px); }
.tooltip.left > .tooltip-arrow   { right: -4px;  top: calc(50% - 4px); }
.tooltip.right > .tooltip-arrow  { left: -4px;   top: calc(50% - 4px); }
.tooltip.start > .tooltip-arrow  { left: 16px; }
.tooltip.end > .tooltip-arrow    { left: auto; right: 16px; }
@media (prefers-reduced-motion: reduce) { .tooltip, .toggle-group.text .toggle[aria-label]::after { transition: none; } }

/* ═══════════════════════════════════════════════════════════════
   Spinner — indicator de așteptare.
   ≥24px: loader-ul Bono (assets/loader-bono.json, Lottie), fiindcă la
   mărimea aia logo-ul se citește și momentul merită brandul.
   <24px: inel CSS în brand — un logo la 16px e o pată, nu un logo.
   ═══════════════════════════════════════════════════════════════ */
.spinner {
  display: inline-block; flex-shrink: 0;
  width: 20px; height: 20px;
  border: 2px solid var(--c-accent-tint);
  border-top-color: var(--c-accent);
  border-radius: 50%;
  animation: spinner-rot 700ms linear infinite;
}
.spinner.xs { width: 16px; height: 16px; border-width: 2px; }
.spinner.sm { width: 20px; height: 20px; }
.spinner.on-fill { border-color: rgba(255,255,255,0.35); border-top-color: #fff; }
@keyframes spinner-rot { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2s; }
}
/* Gazda loader-ului Lottie — dimensiunile la care logo-ul e lizibil. */
.spinner-brand { display: inline-block; flex-shrink: 0; width: 32px; height: 32px; }
.spinner-brand.size-6 { width: 24px; height: 24px; }
.spinner-brand.size-8 { width: 32px; height: 32px; }

/* ═══════════════════════════════════════════════════════════════
   Skeleton — locul unui conținut care încă nu a sosit.
   Nu e un spinner: spinner-ul spune „așteaptă", skeleton-ul arată CE vine
   și unde, ca pagina să nu sară când sosește. Venit din Dublin (cardurile de
   pe dashboard, 2026-10-15), unde a fost Skeleton-ul din shadcn.

   Bara stă pe linia de bază și e înaltă cât cifrele (0.75em): acoperă exact
   locul numărului care vine. E inline-block gol, deci rândul care o ține își
   păstrează înălțimea (strut-ul lui) și textul o înlocuiește fără să miște
   nimic. Cere un rând de text în jur: într-un părinte flex devine element
   flex, pierde rândul și linia de bază, iar părintele se strânge — acolo
   părintele trece pe inline-block cât ține bara (vezi .kpi .delta).

   Sub reduced motion stă pe loc. Nu încetinește ca .spinner: o bară care
   nu pulsează se citește tot ca „se încarcă", un inel oprit se citește ca
   pagină înghețată. animation: none, nu durată — regula globală de reduced
   motion pune 0.01ms pe orice animație, iar una infinită la 0.01ms pâlpâie.
   ═══════════════════════════════════════════════════════════════ */
.skeleton {
  display: inline-block; vertical-align: baseline;
  width: 100%; height: 0.75em;
  border-radius: var(--r-xs);
  background: var(--c-bej-1);
  animation: skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes skeleton-pulse { 50% { opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) {
  .skeleton { animation: none; }
}

/* ═══════════════════════════════════════════════════════════════
   Card — sloturile. Suprafața e mai sus; astea sunt părțile pe care
   orice card le are, aliniate cu ce a construit deja aplicația.
   ═══════════════════════════════════════════════════════════════ */
.card-title {
  font-family: var(--font-sans);
  font-size: 16px; font-weight: 600; line-height: 22px;
  letter-spacing: -0.01em; color: var(--c-ink); margin: 0;
}
.card-desc {
  font-family: var(--font-sans);
  font-size: 14px; line-height: 20px; color: var(--c-fog); margin: 4px 0 0;
}
.card-body { margin-top: 16px; }

/* Respirația antet → conținut nu depinde de memoria autorului. Orice element
   care urmează direct titlului sau descrierii primește --sp-16, chiar dacă cel
   care a scris markup-ul a uitat .card-body (cazul cardului cu uploader, unde
   componenta se lipea de titlu). .card-desc își păstrează cei 4px de sub titlu,
   .card-ft cei 20px de deasupra subsolului — amândouă sunt sărite aici. */
.card > :is(.card-title, h2, h3, h4) + *:not(.card-desc, .card-ft),
.card > .card-desc + *:not(.card-ft) { margin-top: var(--sp-16); }
.card-ft {
  margin-top: 20px; padding-top: 16px;
  border-top: 1px solid var(--c-rule-soft);
  display: flex; align-items: center; gap: 10px;
}
.card-ft.end { justify-content: flex-end; }

/* ═══════════════════════════════════════════════════════════════
   Action card — .card.cta cu sloturile cardului (2026-10-07)
   Formalizat din Dublin (shared/dublin-ui/action-card.tsx, rândul de
   acțiuni de pe „Acasă"), la geometria lui măsurată: antet → conținut →
   acțiune. Geometria e a componentei, deci într-un rând de carduri
   butoanele stau pe aceeași linie orice ar ține corpurile:
   - padding-ul .card (24px), niciodată suprascris;
   - titlul o treaptă sub .card-title, la 14/20 — mărimea etichetei de
     buton, ca să nu cântărească mai mult decât acțiunea;
   - titlu → corp 16px, din regula de frați a sloturilor (mai sus);
   - corpul crește (flex: 1) și își așază conținutul: sus (implicit),
     .center sau .end;
   - corp → acțiune 16px, ai corpului: cu corpul crescând, margin-top:auto
     de pe buton se rezolvă la zero;
   - acțiunea e un .btn.full de 40px, ultimul copil. Cu o acțiune
     secundară, cele două stau în .card-ft: butonul plin + un
     .btn.outline.icon-only de 40px, 8px între ele, pe aceeași linie de
     bază — fără linia de sus a subsolului, care aici ar tăia cardul.
   Cardurile dintr-un rând de grilă se întind la cel mai înalt, deci
   butoanele stau la aceeași distanță de baza rândului.
   Clasele astea dau exact ce scria Dublin în utilitare (text-sm, flex-1
   min-h-0 flex-col mb-4, inline-flex … ring-card-outline): la re-ștampilare
   Dublin nu se mișcă, iar utilitarele devin redundante. */
.card.cta > .card-title { font-size: 14px; line-height: 20px; }
.card.cta > .card-body {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  margin-bottom: 16px;
}
.card.cta > .card-body.center { justify-content: center; }
.card.cta > .card-body.end    { justify-content: flex-end; }
.card.cta > .card-ft {
  margin-top: auto; padding-top: 0; border-top: none;
  gap: var(--sp-8);
}
.card.cta > .card-ft > .btn.full { flex: 1; width: auto; }
.card.cta > .card-ft > .btn.icon-only { flex: none; }

/* Titlul conturat — titlul într-o pilulă cu un punct roz de 5px (punctul
   .dot al DS-ului, nu cel de 7px cu halo din prototip). Hairline-ul e cel al
   cardului (--c-rule-card) pe suprafețele deschise și alb @ 14% pe .card.dark,
   unde DS-ul nu are token. align-self ține pilula la lățimea textului în
   coloana flex a .card.cta. */
.card-title.outlined {
  display: inline-flex; align-items: center; gap: 8px; align-self: flex-start;
  padding: 4px 12px; border-radius: var(--r-full);
  box-shadow: inset 0 0 0 1px var(--c-rule-card);
}
.card-title.outlined > .dot {
  width: 5px; height: 5px; flex-shrink: 0;
  border-radius: 50%; background: var(--c-pink);
}
.card.dark .card-title.outlined { box-shadow: inset 0 0 0 1px rgba(255,255,255,0.14); }

/* ═══════════════════════════════════════════════════════════════
   Table — părțile lipsă: containerul care derulează, subsolul și
   varianta densă. Fără .tbl-wrap, un tabel lat împinge pagina pe
   orizontală în loc să deruleze în el însuși.
   ═══════════════════════════════════════════════════════════════ */
/* Raza tabelului e --r-sm (10px), fixată pe 2026-09-21 după ce producția a ieșit
   și pe --r-md, și pe --r-lg: nu exista o singură specificație de citit, fiindcă
   `.tbl` nu declară rază DELOC — o poartă carcasa. De aici mai vine o consecință
   care trebuie spusă o dată: un `.tbl` pus direct în pagină n-are colțuri rotunde,
   pentru că n-are ce să i le dea. Tabelul VINE cu rază; carcasa e cea care o duce,
   deci `.tbl-wrap` nu e opțional. Vezi anti-patternul din table.json.

   De ce în jos, și nu la treapta de card: un tabel nu e un card. --r-xl (24px) e
   raza suprafețelor de CONȚINUT (card content, KPI hero) — obiecte cu padding lat,
   unde colțul are loc să respire. Corpul unui tabel e o grilă lipită de marginea
   carcasei: primul `th` începe la 1px de contur, deci un colț de 24px taie vizibil
   în banda de antet și lasă un gol care nu e aliniat cu nicio coloană. 10px e
   treapta care rotunjește carcasa fără să muște din grilă.

   overflow-x: auto NU e doar pentru scroll — el e și ce CLIPUIE colțurile: fundalul
   bej-1 al lui `thead th` și hairline-urile rândurilor sunt dreptunghiulare, iar
   fără clip ar ieși peste raza carcasei în cele patru colțuri. Nu-l scoate ca să
   "repari" un dropdown tăiat dintr-o celulă — ancorează meniul într-un `.popover`
   poziționat, cum face context-menu. */
.tbl-wrap {
  width: 100%; overflow-x: auto;
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-sm);
  background: var(--c-white);
}
.tbl-wrap .tbl { min-width: 560px; }
.tbl caption {
  caption-side: top; text-align: left;
  padding: 14px 22px 0;
  font-family: var(--font-sans);
  font-size: 13px; color: var(--c-fog);
}
.tbl tfoot td {
  padding: 12px 22px;
  background: var(--c-bej-1);
  font-size: 14px; font-weight: 500; color: var(--c-ink);
  border-top: 1px solid var(--c-rule-soft);
  font-variant-numeric: tabular-nums;
}
.tbl.compact thead th { padding: 8px 14px; }
.tbl.compact tbody td { padding: 9px 14px; font-size: 13px; }
/* Subsolul și titlul pe gutter-ul densității lor. La 22px, totalul unui tabel
   compact nu mai stătea sub coloana lui, ci cu 8px mai la dreapta. */
.tbl.compact tfoot td,
.tbl.compact caption { padding-inline: 14px; }
.tbl-empty {
  padding: 40px 22px; text-align: center;
  font-family: var(--font-sans);
  font-size: 14px; color: var(--c-fog);
}

/* ─── Tabelul filtrat: filtrele stau ÎN carcasă ───
   Contract: table.json → variants.filtered · doc: docs/components/table.md

   O listă pe care o filtrezi e UN obiect: .tbl-wrap > .tbl-filters (antetul
   listei + bara, filter-bar.json) + .tbl-scroll > .tbl.compact. Nu un card cu
   o carcasă de tabel în el: două contururi unul într-altul dau listei două
   margini, iar fiecare piesă se aliniază la a ei — în templates/table-view.html
   titlul și filtrele stăteau la 25px, coloanele la 48px.

   Alinierea e regula: UN gutter de 14px, padding-ul inline al lui .tbl.compact.
   Titlul, primul filtru și textul primei coloane încep pe aceeași linie; ✕-ul
   general și textul ultimei coloane (suma, la dreapta) se termină pe aceeași
   linie. De aceea tabelul filtrat e compact: un .tbl implicit ar pune coloanele
   la 22px. Cele două numere se schimbă împreună.

   Fără fundal și fără linie sub filtre: banda bej-1 a lui thead e despărțitorul.
   O bandă sau o linie în plus ar fi un al doilea antet deasupra antetului.

   Overflow: carcasa filtrată NU mai e cutia care derulează. Lista unui filtru
   (select, perioadă, sumă) e un .popover absolut în .select-anchor, iar un
   overflow pe carcasă l-ar tăia chiar sub bară. Derularea și clipul de colțuri
   trec pe .tbl-scroll, care învelește doar lista: colțurile de jos (hover-ul
   ultimului rând, tfoot) rămân tăiate la rază, iar sus n-are ce să iasă —
   .tbl-filters stă pe albul carcasei. Clipul nu se scoate, se mută. */
.tbl-wrap:has(> .tbl-filters) { overflow: visible; }
.tbl-filters { padding: 14px; }
.tbl-scroll {
  overflow-x: auto;
  /* Raza interioară: a carcasei minus conturul ei de 1px. */
  border-radius: 0 0 calc(var(--r-sm) - 1px) calc(var(--r-sm) - 1px);
}

/* ═══════════════════════════════════════════════════════════════
   Calendar — selector de dată, o zi sau interval.

   Două trepte, diferite prin antet:
   .calendar (implicit)  antet fără chrome (.calendar-select, luni abreviate),
                         navigare .btn.ghost.sm.icon-only. Trăiește în .popover.
   .calendar.lg          antet din controale obișnuite: .select pentru lună și an,
                         navigare .btn.outline.icon-only. De sine stătător.
   Treapta = --cal-cell · --cal-gap · --cal-pad. Restul se derivă.
   ═══════════════════════════════════════════════════════════════ */
.calendar {
  --cal-cell: 34px;
  --cal-gap: 2px;
  --cal-pad: 14px;
  display: inline-block; padding: var(--cal-pad);
  /* Lățimea vine din grilă. Fără ea, un <select> nativ (măsurat după cea mai lungă
     opțiune) ar dicta lățimea componentei. */
  width: calc(7 * var(--cal-cell) + 6 * var(--cal-gap) + 2 * var(--cal-pad) + 2px);
  background: var(--c-white);
  border: 1px solid var(--c-rule-card);
  border-radius: var(--r-lg);
}
/* În popover, chrome-ul e al popoverului. */
.popover .calendar { --cal-pad: 0px; border: none; background: transparent; border-radius: 0; width: calc(7 * var(--cal-cell) + 6 * var(--cal-gap)); }
.calendar-hd {
  display: flex; align-items: center; justify-content: space-between;
  gap: 4px; margin-bottom: 10px;
}
/* Grupul lună+an ține mijlocul; săgețile rămân la capete. */
.calendar-picker {
  display: flex; align-items: center; justify-content: center;
  gap: 12px; flex: 1; min-width: 0;
}
.calendar-month {
  font-family: var(--font-sans);
  font-size: 14px; font-weight: 600; color: var(--c-ink);
}
/* Select fără bordură și fără fundal — doar pe treapta implicită. Săgeata e cea
   de la .select. Luna e abreviată (Ian, Feb, …): antetul are 178px pentru lună+an. */
.calendar-select {
  appearance: none; -webkit-appearance: none;
  height: 30px; padding: 0 18px 0 6px;
  max-width: 100%;
  border: none; border-radius: var(--r-md);
  background-color: transparent;
  font-family: var(--font-sans);
  font-size: 14px; font-weight: 600; line-height: 20px;
  color: var(--c-ink); cursor: pointer;
  transition: background-color 120ms;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M4 6L8 10L12 6' stroke='%237A7570' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 2px center;
  min-width: 0;   /* altfel <select> nu se strânge sub cea mai lungă opțiune */
}
/* Anul nu se strânge (toate opțiunile au aceeași lățime, deci s-ar tăia); luna da. */
.calendar-picker > .calendar-select:last-child { flex: none; }
@media (hover: hover) {
  .calendar-select:hover:not(:disabled) { background-color: var(--c-bej-1); }
}
.calendar-select:disabled { color: var(--c-mist); cursor: not-allowed; }
.calendar-grid { display: grid; grid-template-columns: repeat(7, var(--cal-cell)); gap: var(--cal-gap); }
/* Mărimea (11px) vine din podeaua scalei — grupat cu .t11 în secțiunea TIPOGRAFIE.
   Aici doar tracking-ul și celula. */
.calendar-dow {
  letter-spacing: 0.06em;
  height: 26px; display: flex; align-items: center; justify-content: center;
}
.calendar-day {
  height: var(--cal-cell); width: var(--cal-cell);
  display: flex; align-items: center; justify-content: center;
  border: none; background: transparent; cursor: pointer;
  /* Celula e pătrată (--cal-cell pe ambele axe), deci 50% e cerc, nu elipsă.
     Cercul e forma zilei pe toate stările: hover, azi (inel), selectat (plin),
     capetele de interval. Singura excepție e banda intervalului, dreptunghiulară. */
  border-radius: 50%;
  font-family: var(--font-sans);
  font-size: 13px; line-height: 18px; color: var(--c-ink);
  font-variant-numeric: tabular-nums;
  transition: background 120ms, color 120ms;
  /* isolation: obligatoriu — face din celulă un stacking context, deci z-index:-1 de
     pe pseudo-elementele de interval înseamnă „sub cifra asta". Fără el, banda și
     pastila capătului pleacă sub fundalul alb al lui .calendar și dispar. */
  position: relative; isolation: isolate;
}
/* Was five :not()s deep — is-selected, in-range and both range ends all had to
   be named back out. They are declared below and win by order now; a SIXTH day
   state needs no edit here, which is exactly what the old shape could not
   promise. */
@media (hover: hover) {
  .calendar-day:hover:where(:not(:disabled)) { background: var(--c-bej-1); }
}
/* Press (2026-10-15): pe touch hoverul e închis în @media (hover: hover), deci
   :active e singurul răspuns la tap — același token ca hoverul, nu o culoare nouă. */
.calendar-day:active:where(:not(:disabled)) { background: var(--c-bej-1); }
.calendar-day.is-muted { color: var(--c-mist); }
.calendar-day.is-today { box-shadow: inset 0 0 0 1px var(--c-input-border); font-weight: 600; }
.calendar-day.is-selected { background: var(--c-accent); color: #fff; font-weight: 600; }
.calendar-day:disabled { color: var(--c-mist); cursor: not-allowed; }

/* ─── Interval — trei stări pe .calendar-day, aceeași grilă ───
   Banda stă pe ::before și se întinde cu jumătate de gap în ambele direcții: două
   jumătăți acoperă gapul grilei, fără aritmetică pe :nth-child.
   Pictare: fundal buton → ::before (bandă) → ::after (pastila capătului) → cifră. */
.calendar-day.in-range::before,
.calendar-day.is-range-start::before,
.calendar-day.is-range-end::before {
  content: ""; position: absolute; z-index: -1;
  top: 0; bottom: 0;
  background: var(--c-accent-tint);
}
.calendar-day.in-range::before {
  left: calc(var(--cal-gap) / -2); right: calc(var(--cal-gap) / -2);
}
/* Capetele: banda pleacă doar spre interiorul intervalului. */
.calendar-day.is-range-start::before { left: 50%; right: calc(var(--cal-gap) / -2); }
.calendar-day.is-range-end::before { left: calc(var(--cal-gap) / -2); right: 50%; }
/* Fill-ul capătului e declarat de două ori: pe element ca auditul de contrast să
   citească perechea reală (alb pe accent), pe ::after ca să treacă peste bandă. */
.calendar-day.is-range-start,
.calendar-day.is-range-end {
  background: var(--c-accent);
  color: #fff; font-weight: 600;
}
.calendar-day.is-range-start::after,
.calendar-day.is-range-end::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: 50%; background: var(--c-accent);
}
/* Pe --c-accent-tint: mist 2.08, fog 3.88, accent-deep 4.58. */
.calendar-day.is-muted.in-range { color: var(--c-accent-deep); }
/* Inelul lui „azi" e inset box-shadow, deci s-ar picta sub bandă. Rămâne greutatea. */
.calendar-day.is-today.in-range { box-shadow: none; font-weight: 600; }
/* Banda ocupă fundalul, deci hoverul din interior e un inel. */
@media (hover: hover) {
  .calendar-day.in-range:hover:not(:disabled) {
    background: transparent;
    box-shadow: inset 0 0 0 1px var(--c-accent);
  }
}
.calendar-day.in-range:active:not(:disabled) {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--c-accent);
}

/* Picior opțional: text de ajutor + acțiune de golire. */
.calendar-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--c-rule-soft);
}
.calendar-foot .t13 { max-width: 62%; }

/* ─── Scurtături (opțional) ───
   Model shadcn: rând sub grilă, despărțit de o linie, butoane outline pe cea mai
   mică treaptă (.btn.xs, 26px), flex:1 — umplu rândul și trec pe următorul. */
.calendar-presets {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--c-rule-soft);
}
.calendar-presets > .btn { flex: 1 1 auto; }   /* flex-basis auto: rupere pe text */

/* ─── Treapta lg ─── celula de 44px e și ținta de atins pe touch. */
.calendar.lg { --cal-cell: 44px; --cal-gap: 4px; --cal-pad: 20px; border-radius: var(--r-xl); }
.calendar.lg .calendar-hd { gap: 8px; margin-bottom: 14px; }
.calendar.lg .calendar-picker { gap: 8px; }
/* width:auto e obligatoriu: .select vine cu width:100%, iar flex-basis:auto se ia
   după width — cu 100% ambele select-uri ar porni de la lățimea containerului și
   s-ar strânge egal, tăind numele lunii. */
.calendar.lg .calendar-picker > .select { flex: 1 1 auto; width: auto; min-width: 0; }
.calendar.lg .calendar-month { font-size: 16px; }
.calendar.lg .calendar-dow { font-size: 12px; height: 32px; }
.calendar.lg .calendar-day { height: var(--cal-cell); width: var(--cal-cell); font-size: 15px; }
.calendar.lg .calendar-presets { gap: 8px; margin-top: 16px; padding-top: 16px; }
.calendar.lg .calendar-foot { margin-top: 16px; padding-top: 16px; }

/* ═══════════════════════════════════════════════════════════════
   Filter bar — rândul de filtre de deasupra unei liste.
   Contract: components/filter-bar.json · doc: docs/components/filter-bar.md

   NU e o familie nouă de controale. Fiecare filtru e un control care exista
   deja: .input-group (căutare, entitate), .select-anchor + .select-trigger (o
   valoare, mai multe, perioadă, sumă), .tabs (segment), .check-item (da/nu).
   Bara adaugă doar ce controalele nu pot ști singure:
     1. așezarea pe rând — lățimi fixe, căutarea ultima și elastică, ruperea
        pe mobil;
     2. ✕-ul de golire pe un select (.select-clear), care ia locul săgeții;
     3. ✕-ul general de la capătul barei, care le golește pe toate
        (.filter-reset), și antetul listei cu numărul (.filter-hd);
     4. rândul de/până la din popoverul de sumă (.filter-range).
   ═══════════════════════════════════════════════════════════════ */
.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: 8px;
}
/* Filtrele UMPLU rândul, în părți egale — ca niște coloane, nu ca niște
   etichete. Lățimea unui filtru depinde doar de lățimea barei și de câte
   filtre are, NICIODATĂ de valoare: „Neplătite" și „Plătite și neplătite" stau
   în aceeași cutie, deci alegerea unei valori nu mută nimic pe rând. Bara se
   lărgește, filtrele cresc toate odată; se îngustează, se strâng toate odată.
   `flex: 1 1 120px` e ce le face egale: toate pornesc de la aceeași bază, iar
   restul rândului se împarte în părți egale, fără să conteze conținutul. Baza
   e 120px, nu 0: cu 0, padding-ul căutării (16px pe fiecare parte) s-ar adăuga
   peste partea ei și ar ieși mai lată decât celelalte. O valoare mai lungă
   decât cutia se taie cu elipsă (select.json → variants.width).
   Minimul e pragul de rupere, nu o lățime: 120px, lățimea minimă a unui select
   în Material (meniul M3 pornește de la 112dp). Când rândul nu le mai poate da
   fiecăruia 120px, ultimul trece pe rândul următor, unde împart iar rândul.
   Căutarea ia aceeași parte, plus locul ✕-ului general cât acesta lipsește:
   umple MEREU rândul până la capătul tabelului, iar când apare ✕-ul se strânge
   doar ea ca să-i facă loc (mai jos). Are același prag de 120px ca celelalte,
   nu unul mai mare: un prag propriu de 200px o oprea, pe o bară îngustă,
   înainte să cedeze tot locul ✕-ului, iar restul îl dădeau celelalte filtre —
   care se mișcau. Segmentul (.tabs) și da/nu (.check-item) își țin lățimea
   lor: nu sunt câmpuri cu valori. */
.filter-bar > :is(.select-anchor, .input-group, .typeahead) {
  flex: 1 1 120px; width: auto; min-width: 120px;
}
/* Locul ✕-ului îl ține căutarea (recunoscută după input[type=search], nu
   printr-o clasă în plus). Fără ✕ — ascuns cu `hidden` sau nerandat — baza ei
   crește cu exact cât ar ocupa el, 40px + golul de 8px, deci căutarea ajunge
   la capătul rândului. Când ✕-ul apare, baza ei scade cu aceiași 48px,
   iar el îi ia locul. Spațiul de împărțit rămâne identic pentru celelalte
   filtre, deci ele nu se mișcă nici cu un pixel: se strânge doar căutarea.
   Pragul de rupere rămâne și el același, cu ✕ sau fără: 3 × 120 + 168 =
   4 × 120 + 48. */
@media (min-width: 1024px) {
  .filter-bar:not(:has(> .filter-reset:not([hidden]))) > .input-group:has(> input[type="search"]) { flex-basis: 168px; }
}
/* Căutarea nu trece de 480px. Pe o bară lată, o căutare care umple tot restul
   rândului se citește ca un câmp de formular, nu ca un filtru; peste 480px
   restul rândului îl iau celelalte filtre, tot în părți egale. Cât căutarea e
   sub plafon, regula de mai sus ține: ✕-ul general o strânge doar pe ea. Când
   e deja la 480px cu și fără ✕, cei 48px ai ✕-ului vin din celelalte filtre,
   în părți egale. O bară care n-are decât căutarea o lasă la 480px, iar ✕-ul
   (dacă e) rămâne la capătul rândului. */
.filter-bar > .input-group:has(> input[type="search"]) { max-width: 480px; }
/* Segmentul are 2–7 opțiuni, aceeași limită ca .tabs oriunde altundeva
   (tabs.json); peste 7 devine filtrul „o valoare" (.select). Cât încape, își
   ține lățimea; când opțiunile nu încap pe rând (7 pe o bară îngustă), nu iese din bară și nu împinge
   pagina: se strânge la lățimea rândului și derulează orizontal, fără bară de
   derulare vizibilă. Opțiunile nu se rup pe două rânduri și nu se taie.
   Tabul ales trebuie adus în vedere (scrollIntoView({ inline: "nearest" })).
   Inelul de focus intră în pastilă: în carcasa cu derulare, outline-ul de
   afară ar fi tăiat. */
.filter-bar > .tabs {
  max-width: 100%; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.filter-bar > .tabs::-webkit-scrollbar { display: none; }
.filter-bar > .tabs > button { flex: none; white-space: nowrap; }
.filter-bar > .tabs > button:focus-visible { outline-offset: -2px; }
/* Da/nu e un .check-item și își ține treapta lui (15/22); pe rând doar nu se
   rupe, și primește aer cât să nu se lipească de conturul vecinului. */
.filter-bar > .check-item { white-space: nowrap; padding-inline: 4px; }

/* ─── ✕ pe un select — golește valoarea și ia locul săgeții ───
   Butonul e .btn.ghost.xs.icon-only, primitivul de buton-icon (garda
   check-icon-buttons.py), și stă LÂNGĂ trigger, nu în el: un <button> într-un
   <button> nu e HTML valid, iar un click pe ✕ ar deschide și lista.
   Stă peste slotul săgeții: centrul ei e la 14 + 7 = 21px de marginea
   dreaptă, deci ✕-ul de 26px începe la 21 − 13 = 8px. Centrat vertical prin
   top/bottom + margin auto, nu prin transform — transformul unui .btn e al
   hover-ului lui.
   Săgeata devine invizibilă, nu dispare: slotul rămâne, deci valoarea nu sare
   când apare ✕-ul. Același schimb ca la perioadă: săgeată cât e gol, ✕ cât e
   ales — o singură regulă pentru orice filtru care deschide o listă. */
.select-anchor > .select-clear {
  position: absolute; inset-inline-end: 8px;
  top: 0; bottom: 0; margin-block: auto;
}
.select-anchor:has(> .select-clear) > .select-trigger > .chev { visibility: hidden; }
/* Câmpul e unul singur: peste ✕ trigger-ul își ține hover-ul. */
@media (hover: hover) {
  .select-anchor:has(> .select-clear:hover) > .select-trigger:where(:not(:disabled):not(:focus-visible):not([aria-expanded="true"])) {
    border-color: var(--c-input-border-hover);
  }
}

/* ─── Mai multe valori: „+N" e un .badge.sm.neutral ───
   Aceeași piesă ca numărul din .tabs. Stă lipit de valoare, nu împins la
   capăt: slotul valorii nu mai crește când îl urmează un badge, iar săgeata își
   ia singură capătul (margin-left: auto). Badge-ul nu se taie — se taie numele. */
.select-trigger > .select-value:has(+ .badge) { flex: 0 1 auto; }
.select-trigger > .badge { flex-shrink: 0; font-variant-numeric: tabular-nums; }

/* ─── Antetul listei: titlu · număr ───
   min-height 20px = înălțimea numărului, rezervată: rândul nu crește în clipa
   în care apare primul filtru. Resetarea nu mai stă aici — e ✕-ul de la
   capătul barei (.filter-reset, mai jos). */
.filter-hd {
  display: flex; align-items: center; gap: 12px;
  min-height: 20px;
}
.filter-count {
  font-family: var(--font-sans);
  font-size: 14px; line-height: 20px;
  color: var(--c-fog); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.filter-hd + .filter-bar { margin-top: 16px; }
/* Forma veche, cu bara DEASUPRA carcasei. Tiparul de acum e tabelul filtrat
   (.tbl-wrap > .tbl-filters, mai sus, la Table): bara stă în carcasă. Regula
   rămâne cât timp copiile ștampilate migrează. */
.filter-bar + .tbl-wrap { margin-top: 20px; }

/* ─── Resetarea tuturor: ✕-ul de la capătul barei ───
   .btn.secondary.icon-only (40px, înălțimea filtrelor) — acțiunea neutră a
   DS-ului, cu fill bej-1: se citește ca butonul barei, o treaptă peste ✕-ul
   ghost al unui câmp. Nu .danger (negrul e al ștergerii, iar resetarea nu
   pierde nimic) și nu .primary (acțiunea principală a ecranului e alta).
   .filter-reset e SLOTUL, pe copilul direct al barei — de
   regulă .tooltip-wrap-ul care ține butonul, fiindcă un buton fără text își
   duce numele și în tooltip.
   La capătul rândului, nu lipit de căutare: căutarea e ultima și are propriul
   ✕, iar două ✕ alăturate ar spune lucruri diferite.
   Apare doar de la DOUĂ filtre aplicate. Cu unul singur ar face exact ce face
   ✕-ul acelui filtru, deci n-are rost; o bară cu un singur filtru nu-l are
   niciodată. Când apare, căutarea i-a ținut deja locul și se strânge ca să i-l
   dea (mai sus) — restul barei nu se mișcă. Ascuns cu `hidden` sau nerandat,
   e la fel: căutarea umple rândul. */
.filter-bar > .filter-reset { flex: none; margin-left: auto; }
/* `hidden` trebuie să ascundă de-adevăratelea: .tooltip-wrap e `inline-flex`,
   iar o regulă de autor bate `[hidden] { display: none }` al browserului. Fără
   linia asta ✕-ul „ascuns" rămânea pe ecran în orice aplicație fără un
   `[hidden]` global cu !important — și căutarea, care îi crede ascuns, nu-i mai
   făcea loc. */
.filter-bar > .filter-reset[hidden] { display: none; }

/* ─── Suma: de la / până la, în popoverul filtrului ─── */
.filter-range { display: flex; align-items: center; gap: 8px; }
.filter-range > .input-group { width: 132px; }
.filter-range > .filter-range-sep { color: var(--c-fog); }

/* ─── Zero rezultate, cu ieșire ───
   .tbl-empty spune ce lipsește; când lipsa vine din filtre, „Resetează
   filtrele" stă pe rândul următor, centrat sub mesaj. */
.tbl-empty:has(> .btn.link) {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
}

/* ─── Mobil: două pe rând, căutarea și segmentul pe rând întreg ─── */
@media (max-width: 1023.98px) {
  .filter-bar > * { flex: 1 1 calc(50% - 4px); min-width: 0; }
  .filter-bar > :is(.select-anchor, .input-group, .typeahead) { flex: 1 1 calc(50% - 4px); min-width: 0; }
  .filter-bar > :is(.tabs, .input-group:has(> input[type="search"])) { flex-basis: 100%; }
  /* Opțiunile umplu rândul în părți egale cât încap; nu se strâng sub textul
     lor — peste, segmentul derulează (mai sus). */
  .filter-bar > .tabs > button { flex: 1 0 0; justify-content: center; }
  /* ✕-ul general stă pe rândul căutării, nu singur pe un rând nou: cât e
     vizibil, căutarea se strânge cu 48px ca să-i facă loc. */
  .filter-bar:has(> .filter-reset:not([hidden])) > .input-group:has(> input[type="search"]) { flex-basis: calc(100% - 48px); }
}
