/* ============================================================
   Split din app.css pe layer/audienta (audit 2026-08-06 SF, ADR 0023).
   Precedenta layerelor (ultimul castiga): vendor < app < kf (tokens..utilities).
   Ordinea @layer e declarata IDENTIC in capul fiecarui fisier -
   prima declaratie stabileste ordinea, repetarea e no-op.
   ATENTIE: ordinea <link>-urilor e CONTRACT de cascada (intra-layer
   castiga ultimul incarcat): tokens, scoreboard, components, admin,
   base-layout, site-palette; juratul: tokens, judge, base-layout.
   NU adauga/reordona <link>-uri fara audit de coliziuni (ADR 0023).
   base-layout.css - S2 LAYOUT/nav + chrome partajat (theme-ico, flash toasts)
   + S13 UTILITIES + S16 MOBILE/touch + S17 HOME/page-heads
   + :focus-visible global (@layer base). Se incarca pe TOATE paginile,
   DUPA components/admin (S13/S16/S17 bat componentele la egalitate).
   ============================================================ */
@layer vendor, app, tokens, base, layout, components, utilities;

@layer app {
/* === 2. LAYOUT ============================================= */

/* ═══ LINIA DE DECIZIE a lățimii paginilor publice (B68) ═══
   `.page-wide` stă pe <body>, iar bara, wrapperul și subsolul sunt toate DESCENDENȚII lui — deci
   remapul de aici urcă toate trei deodată, prin moștenire, dintr-un singur loc. Înainte erau trei
   `max-width: 1200px` copiate, iar `.page-wide` ridica DOAR wrapperul: pe fiecare pagină publică
   conținutul ieșea cu 400px în afara barei și a subsolului.

   Nicio coliziune de cascadă: `:root` declară pe <html>, regula asta pe <body> — elemente diferite,
   deci nu contează nici layerul, nici ordinea <link>-urilor.

   Ca să coborâm conținutul la 1200 în loc să urcăm chromeul la 1600: ȘTERGE regula. Ca să fie lat
   peste tot: mută declarația în `:root` din tokens.css. Nu există a treia variantă, și e intenționat
   — starea de dinainte (doar wrapperul lat) nu mai e exprimabilă. */
body.page-wide { --container-max: var(--container-max-wide); }

/* Navbar — Kinetic Sharp (light × brutalist: flat, hairline 1px, accent lime) */
.app-nav {
    background: var(--sm-s1);
    border-bottom: 1px solid var(--sm-hair);
    box-shadow: none;
    position: sticky;
    top: 0;
    z-index: 1020;
}
/* Trei grupuri, nu o listă plată: brand | navigație principală | utilitare (dreapta).
   Gap FLUID (clamp) → bara se mulează, nu cade brusc la un prag. */
.app-nav__inner {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: 0 1rem;
    display: flex;
    align-items: center;
    gap: clamp(0.5rem, 2vw, 1.25rem);
    height: var(--nav-h);
}
/* Brandul din bară e o imagine, nu text: wordmarkul e desenat în logo. De-asta n-au mai rămas
   reguli de tipografie aici — dimensiunea vine din --brand-logo-h (vezi components.css „Semnul de
   brand"). Opacitatea la hover ține loc de virajul de culoare de dinainte: un logo nu se recolorează. */
.app-nav__brand {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    text-decoration: none;
    --brand-logo-h: 30px;
    /* Podea de tinta (WCAG 2.2 SC 2.5.8 AA): sub 420px lockup-ul colapseaza la wordmark si logoul
       ajunge la 22px inaltime — sub cele 24 sub care nu se coboara. Bara are deja 52px pe telefon,
       iar brandul e `display: flex`, deci cutia creste fara sa miste nimic. Masurat: 48x22 → 48x24. */
    min-height: var(--tap-aa);
    transition: opacity .1s;
}
.app-nav__brand:hover { opacity: .75; }

/* Navigația principală = listă semantică (nav > ul > li), resetată vizual. */
.app-nav__primary {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    align-items: center;
    flex-wrap: wrap;                    /* plasă de siguranță, nu mecanismul principal */
    gap: clamp(0.15rem, 1.2vw, 0.6rem);
    min-width: 0;                       /* altfel minimul flex e min-content → nu se strânge deloc */
}
.app-nav__primary > li { min-width: 0; }

/* Utilitarele (cont / login / temă) — dreapta-sus, unde le caută oamenii. */
.app-nav__utility {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-left: auto;
    flex: 0 0 auto;
}

/* --nav-ctl-h = înălțimea COMUNĂ a oricărui control din bară. Fără ea, linkul, butonul și
   comutatorul de temă au trei înălțimi diferite și bara se citește nealiniată. */
.app-nav__link,
.app-nav__btn,
.app-nav__user,
.app-nav__theme {
    height: var(--nav-ctl-h);
    display: inline-flex;
    align-items: center;
}
.app-nav__link {
    font-size: var(--font-md);
    font-weight: 600;
    color: var(--kf-text-variant);
    text-decoration: none;
    padding: 0 clamp(0.35rem, 1.4vw, 0.7rem);
    border-radius: var(--kf-r-sm);
    white-space: nowrap;
    transition: color .1s, background .1s;
}
.app-nav__link:hover { color: var(--kf-text); background: var(--kf-tint); }
.app-nav__link--active {
    color: var(--kf-accent);
    box-shadow: inset 0 -2px var(--kf-marker);
}

.app-nav__btn {
    font-size: var(--font-sm);
    font-weight: 700;
    padding: 0 0.75rem;
    border: var(--kf-border);
    border-radius: var(--sm-r-pill);
    background: transparent;
    color: var(--kf-text);
    text-decoration: none;
    transition: background .1s, color .1s, border-color .1s;
}
.app-nav__btn:hover { background: var(--kf-text); color: var(--kf-surface-bright); }
.app-nav__btn--flash {
    background: var(--kf-primary);
    border-color: var(--kf-primary);
    color: var(--kf-on-primary);
}
.app-nav__btn--flash:hover {
    background: var(--kf-primary-solid);
    border-color: var(--kf-primary-solid);
    color: var(--kf-on-solid);
}
.app-nav__theme {
    justify-content: center;
    width: var(--nav-ctl-h); padding: 0; line-height: 1;
    background: transparent; border: var(--kf-border);
    border-radius: var(--sm-r-pill); color: var(--kf-text);
    cursor: pointer; transition: background .1s, color .1s, border-color .1s;
}
.app-nav__theme:hover { background: var(--kf-text); border-color: var(--kf-text); color: var(--kf-surface-bright); }

/* Dropdown-urile din bară (login + cont) împart aceeași ramă flat — o regulă, două consumatoare. */
.app-nav__login,
.app-nav__menu {
    border: var(--kf-border); border-radius: var(--sm-r-card);
    box-shadow: var(--sm-elev-float);
    background: var(--kf-surface-bright);
}
.app-nav__login { min-width: 16rem; padding: .75rem; }
/* „Vrei cont de organizator?" sub formular (ADR 0053, it. 2): orice login are dedesubt „nu ai cont?". */
.app-nav__login-help { margin: .7rem 0 0; font-size: var(--font-xs); color: var(--kf-text-variant); }
.app-nav__login-help a { color: var(--kf-text); }
.app-nav__login-lbl {
    display: block; margin-bottom: .4rem;
    font-family: var(--kf-font-display); font-weight: var(--type-label-weight, 900); font-size: var(--font-2xs);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .08em); color: var(--kf-accent);
}

/* Meniul de cont — UN singur punct pentru identitate + navigarea proprie + ieșire. */
.app-nav__user {
    font-size: var(--font-md);
    font-weight: 600;
    color: var(--kf-text-variant);
    gap: 6px;
    padding: 0 0.5rem;
    background: transparent;
    border: var(--kf-border);
    border-radius: var(--sm-r-pill);
    cursor: pointer;
    white-space: nowrap;
    transition: color .1s, background .1s, border-color .1s;
}
.app-nav__user:hover { color: var(--kf-text); background: var(--kf-tint); }
.app-nav__menu { min-width: 14rem; padding: 0; }
.app-nav__menu-id {
    display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
    padding: .6rem .75rem;
}
.app-nav__menu-name { font-weight: 700; font-size: var(--font-md); color: var(--kf-text); }
.app-nav__menu-mail { font-size: var(--font-xs); color: var(--kf-text-variant); word-break: break-word; }
/* app.css n-avea NICIO regulă `dropdown-item` → fără asta meniul ar rămâne Bootstrap-default,
   în afara skin-ului. Butonul de logout e tot `.dropdown-item`, deci reset de buton inclus. */
.app-nav__menu .dropdown-item {
    font-size: var(--font-md);
    color: var(--kf-text);
    padding: .45rem .75rem;
    width: 100%;
    text-align: left;
    background: none;
    border: 0;
    cursor: pointer;
}
.app-nav__menu .dropdown-item:hover,
.app-nav__menu .dropdown-item:focus { background: var(--kf-tint); color: var(--kf-text); }
/* Acțiunea care PORNEȘTE ceva (owner, 2026-09-28: „de acolo se fac concursurile noi"). Accentul e
   doar greutate + o iconiță — NU o umplere: un item plin într-un meniu de text ar citi ca un buton
   lipit înăuntru, iar meniul are deja un singur nivel de suprafață. Iconița face treaba grea,
   fiindcă e SINGURA din meniu; greutatea doar o susține.
   ⚠️ Culoarea iconiței NU se scrie aici: fișierul ăsta e partajat cu ecranul juratului, unde tokenii
   `--sm-*` nu există — de aia regulile din jur folosesc `--kf-*`. Valoarea de site stă în
   `components.css`, în secțiunea „VALORI DE SITE pentru reguli care traiesc in foi PARTAJATE". */
.app-nav__menu .app-nav__menu-cta { font-weight: 700; }
.app-nav__menu .dropdown-divider { margin: 0; border-top: var(--kf-border); opacity: 1; }
/* Ultimul element din meniu e „Iesire”, care picteaza la hover si acoperea coltul de jos.
   ⚠️ El sta intr-un `<form>`, iar `<form>`-ul nu picteaza — deci raza trebuie sa treaca PRIN el:
   `inherit` se inlantuie (formularul o ia de la meniu, butonul de la formular). Fara al doilea
   selector, raza ar ajunge pe un element invizibil si n-ar repara nimic.
   Ca si la `.panel-acc`: se vede doar la hover, pe un meniu deschis — sonda nu-l poate masura. */
.app-nav__menu > :last-child,
.app-nav__menu > :last-child > .dropdown-item {
    border-end-start-radius: inherit; border-end-end-radius: inherit;
}
.app-nav__role {
    font-family: var(--kf-font-mono);
    font-size: var(--font-2xs);
    background: var(--kf-surface-2);
    border: 1px solid var(--kf-line);
    padding: 0 5px;
    border-radius: var(--kf-r-sm);
    color: var(--kf-text-variant);
    letter-spacing: var(--type-label-tracking, .05em);
}

/* Wrapper principal */
.app-wrap {
    max-width: var(--container-max);
    margin: 0 auto;
    /* Gutter fluid: sub 640px scade spre ~9.5px (aliniat cu .app-nav__inner de la ≤640px),
       de la 640px în sus e exact 1rem ca înainte. */
    padding: 0 clamp(.5rem, 2.5vw, 1rem);
}
/* Rândul Bootstrap din base.html.twig are margini negative = jumătate de gutter (-12px). Sub 480px padding-ul
   de mai sus e mai mic de 12px, deci pagina derula lateral 2-4px (măsurat: 2.25px la 390, 4px la 320).
   Gutterul urmează padding-ul wrapperului: la ≥480px rămâne 24px (desktop neschimbat), sub el scade odată
   cu padding-ul. `--bs-gutter-x` pe `.app-wrap` NU ajunge — `.row` și-l redeclară; `>` lasă neatinse
   rândurile imbricate `g-2` / `g-3`. */
.app-wrap > .row { --bs-gutter-x: min(1.5rem, 2 * clamp(.5rem, 2.5vw, 1rem)); }

/* Pagina judecatorului — fara sidebar, full screen */


/* Container login — latime fixa centrata */
.login-container { max-width: 420px; }

/* --- Subsol ------------------------------------------------------------------------------
   Aranjament FLUID, nu grilă cu breakpointuri: cele patru blocuri stau unul lângă altul și pică
   pe rând, unul câte unul, pe măsură ce se îngustează ecranul. `margin-top: auto` nu e nevoie —
   paginile sunt destul de lungi; dacă apare vreuna scurtă, se rezolvă acolo, nu aici. */
.app-footer {
    margin-top: clamp(2rem, 6vw, 4rem);
    border-top: 1px solid var(--sm-hair);
    background: var(--bg-elevated);
}
.app-footer__inner {
    max-width: var(--container-max);
    margin: 0 auto;
    padding: clamp(1.25rem, 3vw, 2rem) 1rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(0.75rem, 3vw, 2.5rem);
}
.app-footer__brand { --brand-logo-h: 26px; display: flex; flex: 0 0 auto; }
.app-footer__tagline {
    margin: 0;
    flex: 0 0 auto;
    font-family: var(--font-display);
    font-weight: 800;
    font-size: var(--font-sm);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text);
}
/* Cele patru sensuri ale numelui, ca un singur rând. Separatorul e generat, nu scris în markup:
   la wrap, punctul de după ultimul cuvânt de pe rând ar rămâne agățat în aer. `:last-child` scapă
   ultimul; `flex` + `gap` țin distanța egală indiferent unde cade ruptura. */
.app-footer__senses {
    margin: 0;
    flex: 1 1 20rem;
    display: flex;
    flex-wrap: wrap;
    gap: .2rem clamp(.5rem, 2vw, 1rem);
    font-family: var(--font-mono);
    font-size: var(--font-2xs);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: .08em;
    color: var(--text-muted);
}
.app-footer__senses span { display: inline-flex; gap: clamp(.5rem, 2vw, 1rem); }
.app-footer__senses span:not(:last-child)::after { content: "·"; color: var(--text-faint); }
.app-footer__domain {
    margin: 0;
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: var(--font-2xs);
    color: var(--text-muted);
}
/* Linkuri legale + identificarea operatorului (B68) — vocabularul lui `.app-footer__senses`:
   mono, 2xs, muted. E informație obligatorie, nu navigație, deci nu trebuie să concureze vizual cu
   bara; dar nici să fie ilizibilă, fiindcă atunci nu-și face treaba legală. */
.app-footer__legal {
    flex: 0 0 auto;
    display: flex;
    flex-wrap: wrap;
    gap: .2rem clamp(.5rem, 2vw, 1rem);
    font-family: var(--font-mono);
    font-size: var(--font-2xs);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: .08em;
}
/* Podea de țintă (B170, WCAG 2.2 SC 2.5.8 AA): linkurile măsurau 18px înălțime pe 390px — sub cele
   24px sub care nu se coboară. Cutia crește, TEXTUL nu: `--font-2xs` e alegerea de densitate a
   subsolului, nu un defect. `inline-flex` + centrare verticală ține glifa pe linia ei, iar surplusul
   se împarte egal sus/jos. Pe un rând cu `gap`, lățimea nu se atinge: linkurile au deja 49-133px. */
.app-footer__legal a { color: var(--text-muted); text-decoration: none; }
.app-footer__legal a { display: inline-flex; align-items: center; min-height: var(--tap-aa); }

.app-footer__legal a:hover { color: var(--accent); text-decoration: underline; }
/* `flex: 1 1 100%` = rând propriu: identificarea ruptă între celelalte blocuri de subsol se citește
   ca zgomot, iar rostul ei e să poată fi găsită dintr-o privire. */
.app-footer__operator {
    margin: 0;
    flex: 1 1 100%;
    font-size: var(--font-2xs);
    color: var(--text-faint);
}
.app-footer__operator a { color: inherit; }

/* Container formular ingust */


/* === CHROME PARTAJAT (toate paginile) - mutat din S10/S11b la split === */
/* Iconita temei = derivata din <html data-theme>, NU pusa din JS: butonul are ambele glife si
   CSS arata una. Sursa unica de adevar = atributul, deci starea corecta supravietuieste oricarei
   re-randari de markup (swap de polling, Turbo). Se arata ce OBTII la click. */
.theme-ico--sun { display: none; }
[data-theme="dark"] .theme-ico--sun { display: inline-block; }
[data-theme="dark"] .theme-ico--moon { display: none; }

/* Flash = toast fix, vizibil indiferent de scroll (sub navbar, centrat). Auto-dispar prin flash-toast.js. */
.flash-stack {
    position: fixed; top: 64px; left: 50%; transform: translateX(-50%);
    z-index: 1080; width: min(560px, 92vw);
    display: flex; flex-direction: column; gap: .4rem; pointer-events: none;
}
.flash-stack:empty { display: none; }
.flash-toast { pointer-events: auto; margin: 0; box-shadow: var(--sm-elev-float); }

/* Butonul ink + chrome-ul de modal brand-uit — MUTATE din components.css (amendament ADR 0023):
   modalul de confirmare e construit în JS (confirm-action.js + form-dirty.js) și apare pe TOATE
   audiențele, inclusiv pe pagina juratului (rezolvă calificarea / generează bracket / finalizează
   battle), care NU încarcă components.css. Stau ÎNAINTE de §13/§16 ca touch-target-ul mobil pe
   .btn-ink (min-height + inline-flex, mai jos) să câștige la egalitate — exact ordinea din fișierul
   unic. */
.btn-ink {
    /* Contopita cu regula kf la B159 L10 (F1): valorile de mai jos sunt cele kf, care bateau
       oricum prin layer. Contopita, NU pusa mai jos: ca regula separata, scurtatura `border: 0`
       de aici ar fi omorat `border-color` din `.btn-ink--danger` de dedesubt (aceeasi
       specificitate, shorthand peste longhand) — exact ce a prins `css-structure`. */
    background: var(--sm-btn-fill); color: var(--sm-on-btn-fill);
    border: 0; border-radius: var(--radius-control, 0);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-sm);
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .05em);
    /* `justify-content: center` CONTEAZA doar cand butonul are latime FORTATA (`.w-100`,
       `.manage-act > .btn-ink`): la latime auto nu exista spatiu liber de impartit. Fara el,
       butonul lat isi lipea continutul la stanga — `inline-flex` nu citeste `text-align`, pe care
       `<button>` il are `center` din foaia browserului, deci trecerea `inline-block` -> `inline-flex`
       l-a pierdut tacut. Prins de sonda pe „Concurs nou" (2026-09-24): iconita „+" sarise 325px, de
       la mijlocul butonului la marginea lui. Pe telefon centrarea exista deja, din regula de tap
       (§16, `@media (max-width: 640px)`) — deci desktopul era singurul care nu se potrivea. */
    padding: .5rem 1.1rem; display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    text-decoration: none; transition: none;
box-shadow: none; border-color: var(--sm-btn-fill); }
.btn-ink:hover { background: var(--sm-btn-fill-hover); border-color: var(--sm-btn-fill-hover); color: var(--sm-on-btn-fill); }
/* Blocat după trimitere (garda de dublu-submit din category-builder.js) — semnal vizibil, altfel
   butonul arată identic și omul apasă din nou exact ce voiam să prevenim. */
.btn-ink:disabled { background: var(--btn-disabled-bg); color: var(--btn-disabled-fg); border-color: var(--btn-disabled-bg); cursor: progress; }
.btn-ink--ghost { background: var(--sm-s3); color: var(--sm-text-hi); }
.btn-ink--danger { background: var(--sm-danger); border-color: var(--sm-danger); color: var(--sm-on-danger); box-shadow: none; }
.btn-ink--danger:hover { background: var(--sm-danger-hover); border-color: var(--sm-danger-hover); color: var(--sm-on-danger); }

/* ---- BUTOANELE INK: venite din `kf-skin.css` la B159 L10 (F1; fișierul a fost șters la F4) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* btn-ink (page/acct primary) → olive solid plat, hover invert instant */
  /* Regula `.btn-ink` a fost CONTOPITA cu cea de baza, mai sus — vezi motivul acolo. */
  /* `:hover` sters: skinul da si umplerea si cerneala starii. */
  .btn-ink.btn-xs { padding: .35rem .8rem; font-size: var(--font-xs); }
  /* Variantele semantice ale lui .btn-ink SUB skin (iter. 2d, feedback user: „culorile din modal
     sunt exact la fel"): .btn-ink (0,2,0) bătea .btn-ink--ghost/--danger (0,1,0), deci
     ghost-ul și danger-ul deveneau olive plin identic — inclusiv perechea OK/Anulează de pe
     paginile de confirmare. Regula casei: modifierul se scopează tot sub body.kf, același layer. */
  /* Starea dezactivata: `@layer app` n-o mai face din opacitate (o pata translucida peste umplere), dar regula
     de acolo e UMBRITA de `.btn-ink` din @layer components - deci fiecare lume isi scrie starea. */
  /* `:disabled` sters: skinul da toate trei (umplere, cerneala, culoarea ramei). */
  .btn-ink--ghost { border: 1px solid var(--kf-outline); box-shadow: inset 0 0 0 1px var(--sm-line); border-color: transparent; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-ink--ghost:hover { background: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); border-color: transparent; color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--sm-line); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-ink--ghost:active { background: var(--sm-btn-fill); border-color: transparent; color: var(--sm-on-btn-fill); box-shadow: inset 0 0 0 1px var(--sm-btn-fill); }
  /* ⛔ ȘTERSE la B159 L10 (F2/F3): perechea INVERSĂ a butonului de pe banda finală a paginii de start.
     Erau ale paletei kf, acoperite de skin prin layer — nu se randau de mult. După coborârea skinului
     ar fi câștigat înapoi (aceeași specificitate ca `.btn-ink`, dar mai jos în fișier). */


/* ⚠️ CROM PARTAJAT CU ECRANUL JURATULUI. `confirm-action.js` CONSTRUIESTE modalul de confirmare din
   JS, deci `.brand-modal` + `.btn-ink` apar si la jurat, desi nu-s in niciun template de-al lui —
   verificat in `assets/js/confirm-action.js`, nu prin grep pe `templates/judge/`.
   Regulile de aici citesc tokeni de ROL (`--sm-btn-fill`, `--sm-on-btn-fill`, `--sm-line`…), iar
   fiecare lume si-i declara pe blocul ei: `tokens.css` da valorile JURATULUI pe `:root` (ambele lumi
   il incarca), `site-palette.css` le suprascrie pentru SITE (juratul nu-l incarca). Asa componenta
   are o SINGURA regula, neprefixata, corecta in amandoua.
   NU muta familia asta intr-o foaie de site si nu-i scrie valori literale: juratul ar ramane fie
   fara chrome, fie cu tokeni inexistenti — adica `unset`, nu valoarea veche. Greseala a fost facuta
   o data (B159 L10 F2, comitul 9631e89) si prinsa de auditul de contrast, trecerea `judge-rules`;
   de la F4 o pazeste si `StylesheetChainScopeTest`, care numara foile din `<head>`-ul juratului. */
/* Chrome de modal brand-uit (SSOT) — ramă 2px ink + offset shadow, titlu Archivo uppercase, în loc
   de cardul soft Bootstrap. Scopat pe `.brand-modal`, NU pe `.confirm-modal`: același chrome îl
   poartă și modalul de înscriere (`.enroll-modal`, components.css §12). Clasa de identitate rămâne
   pe fiecare modal în parte — `.confirm-modal` e și santinela pe care o citește judge-panel.js.
   Consumatori: confirm-action.js + form-dirty.js (confirmare), _enroll_modal.html.twig (înscriere). */
.brand-modal .modal-content {
    border: 1px solid var(--sm-line);
    border-radius: var(--radius-card, var(--radius-sm));
    box-shadow: var(--sm-elev-float);
    background: var(--sm-s1);
    color: var(--sm-text-hi);
}
.brand-modal .modal-header { border-bottom: 1px solid var(--sm-hair); padding: .6rem .9rem; }
.brand-modal .modal-title { font-family: var(--font-display, inherit); font-weight: var(--type-title-weight, 900); text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .04em); font-size: var(--font-lg); }
.brand-modal .modal-body { padding: .9rem; }
.brand-modal .modal-footer { border-top: 0; padding: .6rem .9rem; gap: .5rem; }

/* Câmpurile MARI din modalul de confirmare (iter. 2g, confirm-action.js data-confirm-fields):
   cifrele-cheie (Runde/Cronometru) RE-apar editabile, cu stepper −/+ la mărime de deget
   (ținte ≥44px — ghidul touch), ca un organizator solo să vadă și să corecteze setările chiar
   în confirmare. Containerul e gol/ascuns (`hidden`) pentru toate celelalte confirmări. */
.cf-fields {
    display: flex;
    flex-wrap: wrap;                       /* aranjament fluid: pică unul câte unul */
    align-items: flex-start;               /* un câmp cu glosă e mai înalt — nu întinde vecinii */
    gap: clamp(.5rem, 3cqi, 1rem);
    padding: .9rem .9rem 0;
}
.cf-field { display: flex; flex-direction: column; align-items: flex-start; gap: .25rem; }
.cf-lbl {
    font-family: var(--font-display, inherit);
    font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, .06em);
    font-size: var(--font-xs);
    color: var(--fg2);
}
.cf-step { display: flex; border: 2px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; border-color: var(--sm-line); }
/* Glosa cifrei (data-echo-hint): ce ÎNSEAMNĂ numărul, sub stepperul lui. `max-width` în `ch` ca
   fraza să se rupă pe 2-3 rânduri scurte lângă cutie, nu să lățească modalul. */
.cf-hint { font-size: var(--font-xs); color: var(--text-secondary); line-height: 1.3; max-width: 26ch; }
.cf-btn {
    min-width: var(--tap-min);
    min-height: var(--tap-min);
    border: 0;
    background: var(--surface-2, var(--surface));
    color: var(--text);
    font-family: var(--font-mono);
    font-size: var(--font-xl);
    font-weight: 700;
    line-height: 1;
}
.cf-btn:active { background: var(--sm-btn-hover); color: var(--sm-on-btn-hover); }
.cf-in {
    width: 4.5em;
    min-height: var(--tap-min);
    border: 0;
    border-inline: 2px solid var(--border-strong);
    background: var(--surface);
    color: var(--text);
    font-family: var(--font-mono);
    font-size: var(--font-xl);
    font-weight: 700;
    text-align: center;
    -moz-appearance: textfield;            /* săgețile native-s inutile lângă −/+ */
    appearance: textfield;
border-inline-color: var(--sm-line); }
.cf-in::-webkit-outer-spin-button,
.cf-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.cf-in:focus { outline: 2px solid var(--volt); outline-offset: -2px; outline-color: var(--sm-text-hi); }
/* CE urmează să pornească — identitatea categoriei, deasupra cifrelor (iter. 2m). Rundele n-au
   înțeles fără format: „2 runde" înseamnă altceva la 1v1 și altceva la 2v2, iar organizatorul are
   mai multe categorii deschise în aceeași seară. `hidden` îl ascunde reboot-ul Bootstrap (`!important`, @layer vendor):
   o regulă `.cf-ctx[hidden]` aici n-ar avea efect (ștearsă în B159 L0b 0.7). */
.cf-ctx {
    margin: 0;
    padding: .6rem .9rem 0;
    font-family: var(--font-display, inherit);
    font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, .06em);
    font-size: var(--font-sm);
    line-height: 1.2;
    color: var(--text);
}
/* Consecințele, la o PRIVIRE: puncte scurte sub o linie, nu proză (cerința userului, iter. 2m). */
.cf-points {
    margin: 0;
    padding: .55rem 0 0;
    border-top: 2px solid var(--border-strong);
    list-style: none;
    display: grid;
    gap: .3rem;
border-top-color: var(--sm-hair); }
.cf-points li { position: relative; padding-left: .85rem; font-size: var(--font-sm); line-height: 1.35; }
/* Pătrat, nu bulină: marcajul e din același vocabular brutalist ca restul chrome-ului. `currentColor`
   ține contrastul pe ambele teme fără o a doua definiție. */
.cf-points li::before {
    content: "";
    position: absolute;
    left: 0;
    top: .5em;
    width: 6px;
    height: 6px;
    background: currentColor;
    opacity: .55;
}

/* === 13. UTILITIES ========================================= */


/* Familia UNICĂ de utilitare font-size = .fs-* (§16). Perechea veche .text-xs/.text-sm (cu
   !important) a fost migrată și ștearsă la auditul 2026-08-06 — două familii pentru aceiași
   tokeni însemnau două moduri de a pierde cascada. */


.action-group { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* Buton ultra-compact */
.btn-xs {
    font-family: var(--font-display);
    font-size: var(--font-xs);
    font-weight: var(--type-control-weight, 900);
    text-transform: var(--type-control-case, uppercase);
    letter-spacing: var(--type-control-tracking, 0.06em);
    padding: 0.15rem 0.45rem;
    line-height: 1.4;
    border-radius: var(--radius-control, var(--radius-sm));
}



/* === 16. MOBILE / RESPONSIVE =============================== */
/* Mobile-first dense data. Vezi skill: references/mobile-first-dense-data.md.
   Bază = stiluri care merg pe telefon; @media(min-width) ar adăuga desktop unde e cazul.
   Aici: utilitare ca să scoatem style="" inline + nav + touch. (Pattern-ul card-reflow a fost
   scos din CSS și din reguli la auditul 2026-08-06 — se reintroduce doar când o pagină o cere.) */

/* --- Utilitare font/opacity (înlocuiesc style="font-size:var(--font-*)") --- */
.fs-2xs  { font-size: var(--font-2xs); }
.fs-xs   { font-size: var(--font-xs); }
.fs-sm   { font-size: var(--font-sm); }


/* O singură variantă folosită în markup (`.empty-ico--lg`); baza `.empty-ico` a fost ștearsă
   2026-08-14 — nu era modificator peste ea, ci o a doua mărime pe care n-o mai cerea nimeni. */
.empty-ico--lg { font-size: var(--font-8xl); opacity: .25; color: var(--kf-outline); }   /* culoarea: venita din kf, B159 L10 F1 */

/* --- Navbar pe telefon: cele 3 destinații rămân VIZIBILE (fără hamburger) ---------------------
   Navigația ascunsă costă ~20% descoperire și ~15% timp pe telefon (studiul NN/g vizibil vs ascuns),
   iar trei etichete scurte încap la 360px dacă bugetul de lățime e respectat:
     marcă 34 + linkuri ~190 + cont 44 + temă 44 + gap-uri ≈ 330px din 341 disponibili.
   De-asta dispar, în ordine: eticheta „Contul meu" (≤640px) și wordmarkul (≤420px) — nu linkurile.
   `flex-wrap` rămâne ca plasă: dacă un font de sistem e mai lat, bara se rupe în loc să iasă din ecran,
   iar elementele lipicioase ancorate în --nav-h se suprapun cu câțiva px, cosmetic. */
@media (max-width: 640px) {
    :root { --nav-h: 52px; --nav-ctl-h: var(--tap-min); }
    .app-nav__inner { height: auto; min-height: var(--nav-h); flex-wrap: wrap; gap: 0.35rem; padding: 0.25rem 0.6rem; }
    .app-nav__brand { --brand-logo-h: 26px; }
    .app-nav__primary { gap: 0.1rem; }
    .app-nav__link { font-size: var(--font-sm); padding: 0 0.4rem; }
    /* Triggerul devine doar iconiță — numele accesibil vine din `aria-label`, identic cu textul
       vizibil de pe desktop (WCAG 2.5.3 „Label in Name" cere să se potrivească, nu să lipsească). */
    .app-nav__user-lbl { display: none; }
    .app-nav__user { width: var(--nav-ctl-h); justify-content: center; padding: 0; }
    .app-nav__utility { gap: 0.3rem; }
}
/* Sub 420px lockup-ul colapsează la wordmark — pattern standard, și e ultimul lucru sacrificat
   înainte ca bara să se rupă pe două rânduri. Comutarea imaginii se face în components.css
   („Semnul de brand"), pe `.brand-logo--responsive`; aici rămâne doar înălțimea. */
@media (max-width: 420px) {
    .app-nav__brand { --brand-logo-h: 22px; }
}

/* --- Touch targets + forms fluide pe telefon --- */
@media (max-width: 640px) {
    /* `.adj-btn` a IEȘIT din listă intenționat: regula asta îl readucea la 44px exact pe telefon,
       adică exact unde s-a cerut jumătatea de înălțime. Minimul lui e --tap-aa, declarat la componentă. */
    /* `.btn-ink` NU e `.btn` — fără el, „Creează concursul" (acțiunea principală) rămânea la ~35px,
       adică mai MIC decât „Anulează" de lângă el, care e `.btn`. */
    /* `.role-tab`: banda de tab-uri (admin + clasament) e RAIL pe telefon, deci tab-urile se apasă
       cu degetul, nu se citesc doar. Fără asta rămâneau la ~30px, sub pragul WCAG 2.5.5. */
    .btn, .btn-ink, .judge-chip__del, .filter-pill, .badge-category, .role-tab { min-height: var(--tap-min); }
    /* Marcaj STERS la B159 L10 F1 ca ORFAN: justifica o umbra data de `body.kf`, iar regulile
       de buton si de tab au coborat in acelasi layer. Pe cele trei pagini fara clasa
       (Concurs nou, editare, confirmare) butoanele primesc de acum acelasi aspect ca in rest —
       decizia ownerului din 2026-09-23. */
    .btn, .btn-ink, .role-tab { display: inline-flex; align-items: center; justify-content: center; }
    /* Linia de comandă a organizatorului (iter. 2n, cererea userului: „extrem de mari pe mobil").
       Pastile de 44px cu literă de 8px arătau absurd și împingeau rândul pe 3-4 linii. Pragul se
       alege pe criteriul VERIFICABIL din „Ținte touch", nu pe cât de „importantă" pare acțiunea:
       a treia condiție cere ca apăsarea greșită să se anuleze dintr-un tap — iar un buton cu
       `data-confirm` o îndeplinește prin construcție (tapul greșit deschide doar un dialog).
       Deci: coboară la podeaua AA tot ce e ajutător (Recalculează, Anulează, Cheamă, Ecran,
       cronometru — conturate sau neutre, fiecare ori confirmat, ori fără consecință)… */
    .tab-actions .btn { min-height: var(--tap-aa); }
    /* …și rămâne la 44px ce SCHIMBĂ starea concursului: butoanele COLORATE (Start, Decizie, Rezolvă,
       Bracket, Pregătește, Închide). Criteriul e clasa de rol, nu prezența confirmării, ca butonul
       să nu-și schimbe înălțimea de la un start la altul (`data-confirm` pe Start apare doar la
       primul din fază). În practică e cel mult UNUL pe ecran deodată, deci rândul nu se umflă. */
    .tab-actions .btn-warning,
    .tab-actions .btn-success,
    .tab-actions .btn-danger { min-height: var(--tap-min); }
    /* × de ștergere a unui rând de jurat / staff: podeaua de mai sus e doar pe ÎNĂLȚIME, iar butonul e cât o
       singură glifă, deci LĂȚIMEA decide. Măsurat în B159 L6b: 24.5px înainte de skin, 23.5px după (skinul scoate
       majusculele și tracking-ul de pe controale, deci glifa ocupă mai puțin) — adică sub pragul AA de 24×24.
       Ștergerea unui rând NEsalvat e reversibilă dintr-un tap (îl adaugi la loc), deci podeaua e --tap-aa. */
    .judge-inline-row .btn-xs,
    .staff-inline-row .btn-xs { min-width: var(--tap-aa); }
    /* × de închidere a categoriei: Bootstrap îi dă 16px, prea mic pentru deget. Nu mai are nevoie
       de rezervă de padding pe rând — de la B144 stă în antet, nu absolut peste câmpuri. */
    .category-row__hdr > .btn-close { min-width: var(--tap-min); min-height: var(--tap-min); }
    /* Chip organizator: prezență/invitat/șterge = ținte cât tot chipul (44px), acțiuni late și distanțate. */
    .e-chip-del { --chip-act-h: var(--tap-min); font-size: var(--font-sm); }
    .e-chip-del__x, .e-chip-del__act { min-width: 44px; }
    .e-chip-del__act { font-size: var(--font-md); }
    /* Inputuri/select cu lățime fixă (admin) → fluide pe telefon */
    .w-in-lg, .w-in-md, .w-in-sm { width: 100%; }
    /* Formularul de înscriere rămâne FLUID pe telefon (flex-wrap în .enf-row) — câmpurile se
       împachetează 1–2 pe linie după lățime, aliniate la stânga. NU stivuire dură pe coloană
       (dădea spații mari + aliniere pe mijloc). Anti-overflow = min-width:0 pe .enf-opt/.ac-wrap
       (globale mai jos) → câmpurile pot micșora și rup natural, fără casete ieșite din div. */
}

/* --- Clase semantice care înlocuiesc style="" repetat în template-uri --- */


.tbl-th { font-size: var(--font-xs); color: var(--sm-text-mid); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .05em); padding: .2rem .6rem; font-weight: 600; }
.col-rank-narrow { width: 1.8rem; font-weight: 700; }

/* Numărul de runde de sub eticheta fazei — urmează scala bracketului (`--bk-u`, scoreboard.css). */
.bk-col-count { font-size: clamp(var(--font-4xs), calc(var(--bk-u, 12rem) * .052), var(--font-3xs)); font-weight: 400; opacity: 1; color: var(--sm-text-mid); }
.admin-edit-cell { background: var(--paper-2); padding: 0.5rem 0.6rem; }
.w-in-lg { width: 200px; }   /* input lat (email)   — devine 100% pe mobil */
.w-in-md { width: 140px; }   /* input mediu (parolă)— devine 100% pe mobil */
.w-in-sm { width: 120px; }   /* select (rol)        — devine 100% pe mobil */

/* Familia `.dup-warn*` a PLECAT in components.css la B159 L10 (F2): e construita din JS de
   `entry_autocomplete_controller.js`, deci apare DOAR pe formularul de inscriere de pe site.
   Fisierul asta e incarcat si de ecranul juratului, unde tokenii `--sm-*` nu exista — iar
   auditul de contrast masoara pe FISIER, nu pe ce se randeaza acolo. */

/* Input oraș îngust în rândul de membru (echipă) — încape pe o linie */


/* Formular înscriere: TOATE câmpurile pe UN rând care se împachetează fluid (nu 2 rânduri rigide).
   Câmpurile „text lat" (nume/crew/email/oraș) cresc (flex-grow) ca să umple linia fără gol la capăt;
   gen + dată = fixe. La lățimi mici rup natural pe 2+ linii, fiecare justificată de câmpurile care cresc. */
/* BAZELE sunt intenționat sub lățimea „confortabilă", nu egale cu ea: `flex-basis` decide DOAR unde
   se rupe rândul, iar `flex-grow` umflă câmpurile înapoi pe rândul pe care au încăput. Baze mari =
   rupturi premature și rânduri pe jumătate goale; baze mici + grow = aceeași lățime finală, dar cu mai
   multe câmpuri pe rând. Suma bazelor era ~48rem, adică peste cele ~40rem ale formularului dintr-un
   card la două coloane — de aici rândul 2 cu emailul singur, întins pe toată lățimea (raport user).
   Acum grupul obligatoriu (nume·crew·oraș·țară·email) încape la ~31rem, deci ține un singur rând până
   pe telefon; gen+dată, fixe și mutate la coadă în DOM, formează al doilea.
   `max-width` NU e acolo ca să constrângă împachetarea, ci ca să nu iasă un input de 40rem pe un
   formular lat — o lățime peste care câmpul nu mai e mai ușor de citit, doar mai mare. */
.enf-row    { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.enf-name   { flex: 2 1 7rem;   min-width: 7rem;   max-width: 20rem; }
.enf-crew   { flex: 2 1 6.5rem; min-width: 6rem;   max-width: 20rem; }
.enf-city   { flex: 1 1 5rem;   min-width: 4.5rem; max-width: 14rem; }
.enf-country{ flex: 0 0 auto; }   /* lățime fixă cât un steag (.cs--flag) — ca gen/dată, nu crește */
/* Grow mai mare: adresele sunt cel mai lung text din rând, deci primesc partea leului din spațiul rămas. */
.enf-email  { flex: 3 1 8.5rem; min-width: 7rem;   max-width: 22rem; }
.enf-gender { flex: 0 0 auto; }
.enf-gender > select { width: 3.6rem; }
.enf-dob    { flex: 0 0 auto; }
/* Dată naștere = 3 selecturi (zi · lună · an). Font mic + padding redus ca să încapă cuvântul/anul întreg. */
.dob-row { display: inline-flex; gap: 3px; }
.dob-row select { font-size: var(--font-xs); padding: .2rem .15rem .2rem .3rem; background-position: right .2rem center; background-size: 8px 8px; }
.dob-row .dob-d { flex: 0 0 2.6rem; }   /* Zi  — 2 cifre */
.dob-row .dob-m { flex: 0 0 3.2rem; }   /* Lună — 2 cifre */
.dob-row .dob-y { flex: 0 0 3.6rem; }   /* An  — 4 cifre */

/* Câmp opțional (email/gen/dată/crew/oraș) cu comutare input↔readout „pe profil" (B22). Dansator
   existent care ARE câmpul completat → inputul DISPARE, apare readout-ul (email = mascat, crew/oraș =
   valoarea, gen/dată = doar bifă). Lipsă → rămâne input editabil (backfillProfile salvează la înscriere).
   Crew/oraș needitabile: crew-ul de bază NU se schimbă la reînscriere (ar muta dansatorul în alt crew ELO). */
.enf-opt { display: inline-flex; align-items: center; min-width: 0; }
.enf-opt > .ac-wrap { flex: 1 1 auto; min-width: 0; }
.enf-opt > input { flex: 1 1 auto; min-width: 0; }
.enf-opt > .enf-onfile { display: none; }
.enf-opt.is-onfile > select,
.enf-opt.is-onfile > input,
.enf-opt.is-onfile > .dob-row,
.enf-opt.is-onfile > .ac-wrap { display: none; }
.enf-opt.is-onfile > .enf-onfile {
    display: inline-flex; align-items: center; gap: .3rem;
    font-size: var(--font-xs); font-weight: 600; color: var(--text); white-space: nowrap;
    padding: .18rem .45rem; border: 1px solid var(--sm-hair); border-radius: var(--radius-sm);
    background: transparent;
}
.enf-opt.is-onfile > .enf-onfile .bi { color: var(--success); }
.enf-onfile-val { font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* On-file (readout): pilula = content-size + shrink-friendly, NU dimensiunea inputului. Elimină golul
   de flex-grow (crew→oraș) + suprapunerea de nowrap (oraș sub email). Împachetare naturală în .enf-row. */
.enf-opt.is-onfile { flex: 0 1 auto; min-width: 0; max-width: 100%; }
.enf-opt.is-onfile > .enf-onfile { max-width: 100%; min-width: 0; }

/* Informarea art. 13 + clickwrapul, deasupra butonului de înscriere (doar formularul PUBLIC).
   Stă lângă familia `.enf-*` deși e text, nu câmp: coeziunea de familie bate euristica pe audiență
   de fișier — următorul om o caută unde stau frații ei.
   Discret, dar NU sub --font-xs: e informare obligatorie, nu subsol de amănunte. Linia hairline de
   sus o separă de câmpuri fără să adauge un card. */
.enf-legal {
    margin: .75rem 0 .25rem; padding-top: .6rem;
    border-top: 1px solid var(--border-hair);
    font-size: var(--font-xs); line-height: 1.5; color: var(--text-muted);
}
.enf-legal p { margin: 0 0 .35rem; }
.enf-legal p:last-child { margin-bottom: 0; }
.enf-legal__wrap { font-weight: 600; color: var(--fg3); }
.enf-legal a { color: var(--accent); }

/* Membru de echipă = bloc distinct: cele 2 rânduri (person_rows) + număr mic în gutter + zebra discret,
   ca să deosebești concurent 1/2/3/4 fără să aglomerezi. #num tăiat din spațiu la ecrane mici prin padding. */
.member-rows { display: flex; flex-direction: column; gap: .15rem; }
/* `<fieldset>`, nu `<div>`: `disabled` scoate blocul opțional din validare ȘI din payload (vezi
   _entry_form.html.twig). Costul e că trebuie stinse trei implicite de user-agent — `min-inline-size:
   min-content` ar rupe împachetarea fluidă a `.enf-row` exact pe ecranele înguste, iar rama și
   marginile ar dubla zebra. */
.member-block {
    /* Gutterele laterale fluide pe telefon; min stânga 1rem ține cifra __num (absolut la .3rem,
       o cifră — formatele merg până la 4-8 membri) fără suprapunere. */
    position: relative; border-radius: var(--radius-sm);
    padding: .3rem clamp(.4rem, 8vw - 1.5rem, 1.6rem) .3rem clamp(1rem, 6vw - .5rem, 1.5rem);
    min-inline-size: 0; border: 0; margin: 0;
}
.member-block:nth-child(even) { background: var(--paper-2); }
.member-block__num {
    position: absolute; left: .3rem; top: .5rem;
    font-size: var(--font-xs); font-weight: 700; line-height: 1; color: var(--text-muted);
}
/* Retragerea unui membru opțional. Stă în gutterul din dreapta (paddingul de mai sus îi face loc),
   ca să nu intre în rândul fluid de câmpuri. */
.member-block__del {
    position: absolute; right: .25rem; top: .35rem;
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.35rem; height: 1.35rem; padding: 0;
    border: 0; background: transparent; cursor: pointer; line-height: 1;
    font-size: var(--font-xs); color: var(--text-muted);
}
.member-block__del:hover { color: var(--danger); }
/* Blocul care ARE buton de retragere (doar membrii opționali) își păstrează tot gutterul drept:
   ×-ul e țintă de atingere pe o acțiune destructivă — nu-l micșorăm și nu lăsăm inputurile sub el. */
.member-block:has(> .member-block__del) { padding-right: 1.6rem; }

/* Anti-overflow orizontal: media nu depășește lățimea ecranului */
img, svg, table { max-width: 100%; }


/* === 17. HOME (landing) + page heads — Battle-Broadcast ===== */

/* ---- LANDING / HERO: venit din `kf-skin.css` la B159 L10 (F1; fișierul a fost șters la F4) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* === LANDING `/` — Kinetic Sharp (flat, hairline, display Black) ============ */
  /* `border`, `border-top` si `background` n-au venit din kf: skinul da rama INTREAGA
     (`1px solid --sm-card-edge`) si fundalul de card. Ramane geometria. */
  .home-hero {
    border-radius: var(--radius-card, 0);
    box-shadow: none; padding: var(--kf-6) var(--kf-4);
    text-align: left; margin: var(--kf-4) 0 var(--kf-5);
  }
  .home-hero__title {
    font-family: var(--kf-font-display); font-weight: var(--type-display-weight, 900);
    text-transform: var(--type-display-case, uppercase); letter-spacing: var(--type-display-tracking, .01em);
    color: var(--kf-text);
  }
  .home-hero__tag { color: var(--kf-text-variant); }

/* Marcaj STERS la B159 L10 F1 ca ORFAN: apara o diferenta care are acum nume propriu —
   `.page-head--form` de mai jos. */
/* Contopita cu regula kf la B159 L10 (F1): marimea, tracking-ul si line-height-ul de mai jos sunt
   cele kf, care bateau oricum prin layer. */
.page-head {
    font-family: var(--font-display); font-weight: var(--type-display-weight, 900);
    text-transform: var(--type-display-case, uppercase); letter-spacing: var(--type-display-tracking, .01em);
    font-size: var(--font-8xl); line-height: .98; color: var(--text); margin: 0;
    display: inline-flex; align-items: center;
}
/* Capul MIC, pentru ecranele de LUCRU: „Concurs nou", editarea concursului si pagina de confirmare.
   Pana la B159 L10 diferenta exista din ACCIDENT — paginile alea nu primeau clasa `kf`, deci luau
   regula de baza (3xl). Decizia ownerului (2026-09-23): diferenta ramane, dar DECLARATA, nu
   accidentala — un formular lung n-are de ce sa piarda inaltime utila pe un titlu de 40px. */
.page-head--form { font-size: var(--font-3xl); line-height: 1.1; letter-spacing: var(--type-display-tracking, .02em); }

/* --- Breadcrumbs (SSOT) + marcaj de context (LIVE / REZULTATE / ARHIVĂ) --- */
.breadcrumbs {
    display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
    margin: 0 0 var(--kf-5); font-family: var(--kf-font-mono);
    font-size: var(--font-sm); color: var(--text-muted);
}
.breadcrumbs a { color: var(--text-muted); text-decoration: none; }
.breadcrumbs a:hover { color: var(--accent); }
.breadcrumbs__sep { color: var(--text-faint); }
.breadcrumbs__current { color: var(--text); font-weight: var(--type-label-weight, 700); }

.page-state {
    display: inline-flex; align-items: center; gap: .4rem; vertical-align: middle;
    font-family: var(--font-display); font-weight: var(--type-status-weight, 900); font-size: var(--font-sm);
    text-transform: var(--type-status-case, uppercase); letter-spacing: var(--type-status-tracking, .08em);
    padding: .2rem .65rem; border-radius: var(--sm-r-pill);
}
.page-state--live            { background: var(--status-active-bg); color: var(--status-active-fg); }
.page-state--results,
.page-state--archive-results { background: var(--surface-inverse);  color: var(--text-on-inverse); }
.page-state--archive         { background: var(--status-done-bg);   color: var(--status-done-fg); }
.page-state__dot {
    width: .5rem; height: .5rem; border-radius: 50%; background: currentColor;
    animation: page-state-pulse 1.2s ease-in-out 3;
}
@keyframes page-state-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

/* Proiector: ascunde chrome-ul (nav + breadcrumbs). Clasa pe <body> vine din `?proiector=1` (server),
   nu dintr-un buton de comutare — ăla a fost șters 2026-08-14 (ecranul de sală, ADR 0019, e calea). */
.live-projector .app-nav,
.live-projector .app-footer,
.live-projector .breadcrumbs { display: none; }

/* --- Arhivă publică (/arhiva) -------------------------------------------------------------
   Rândul = afiș PORTRET 3:4 în stânga, cu înălțime EGALĂ pe toate rândurile, + o coloană de
   text care se mulează. Înălțimea fixă e decizia de design, nu o scăpare: dacă afișul ar crește
   odată cu conținutul, un concurs cu 12 categorii ar primi un afiș de 2× față de unul cu 2 →
   grilă zimțată, imposibil de scanat pe verticală.

   Dimensionarea internă vine din LĂȚIMEA CARDULUI (container query pe .arch-card), nu din
   viewport: două carduri pe rând și unul singur au același ecran dar cutii diferite, deci un
   @media ar minți. Aceeași motivație ca .judge-col.
   ⚠️ CAPCANĂ: unitățile `cq*` NU se rezolvă pe elementul care DECLARĂ containerul → toate
   clamp-urile stau în COPII (.arch-row/.arch-poster/.arch-title/.arch-body), niciodată pe
   .arch-card. Și `inline-size`, NICIODATĂ `size` (ăla adaugă containment pe bloc → card prăbușit). */
.arch-toolbar { display: flex; flex-wrap: wrap; gap: .5rem clamp(.5rem, 2vw, 1.25rem); align-items: center; margin-bottom: var(--kf-4); }
.arch-toolbar__filters { display: flex; flex-wrap: wrap; gap: .35rem; }
.arch-toolbar__status { display: flex; align-items: center; gap: .5rem; margin-left: auto; }

/* Bară de salt la an — refolosește .filter-pill (deci și bump-ul de touch 44px de pe mobil). */
.arch-jump { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; margin-bottom: var(--kf-5); }

/* Antet de an LIPICIOS: reperul cronologic nu mai dispare la derulare. `top` = înălțimea
   navbarului (el însuși sticky, z-index 1020) → anul se oprește SUB el, nu peste.
   Fundal OPAC obligatoriu, altfel cardurile se văd prin el. z-index explicit: itemii de grilă
   creează contexte de stivuire și ar picta peste antet. */
.arch-year {
    position: sticky; top: var(--nav-h); z-index: 5;
    display: flex; align-items: baseline; gap: .6rem;
    padding: .3rem 0; margin: 1.4rem 0 .6rem;
    font-family: var(--font-display); font-weight: var(--type-title-weight, 900); font-size: var(--font-3xl);
    text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .02em); color: var(--text);
    /* Venit din kf la B159 L10 (F1). `--bg` nu e in blocul de remap al skinului (doar `--kf-bg` e
       fundalul paginii), deci fara linia asta antetul lipicios ar avea alta nuanta decat pagina si
       s-ar vedea o dunga la derulare. */
    background: var(--kf-bg); border-bottom: 1px solid var(--kf-line);
}
.arch-year__n { font-size: var(--font-sm); font-weight: 400; color: var(--text-muted); font-family: var(--kf-font-mono); }
.arch-year-block { scroll-margin-top: calc(var(--nav-h) + var(--kf-3)); }

/* Coloane INTRINSECI, fără @media: --arch-col-min > --container-max-wide / 3 ⇒ maximum 2 coloane pe
   .page-wide; trecerea la o coloană se face singură pe la ~1100px. `min(100%, …)` = plasa
   anti-overflow când viewportul e sub lățimea minimă de coloană. Derivarea numește TOKENUL, nu
   numărul: altfel se desincronizează tăcut la prima modificare de container.

   auto-FIT + pistă plafonată + centrare (B68), nu auto-fill cu `1fr`: auto-fill păstrează pistele
   goale, iar `1fr` le întinde — deci un an cu UN singur concurs arhivat randa cardul pe toată
   lățimea. Era același bug ca pe /concursuri, doar că nimeni nu-l raportase. Bonus gratuit:
   `archive-filter.js` ascunde cu `style.display='none'`, iar un item display:none iese din grid →
   auto-fit pliază pista, deci și filtrele scapă de goluri, fără o linie de JS. */
.arch-list {
    --arch-col-min: 34rem;
    display: grid; gap: clamp(.5rem, 1.2vw, var(--kf-4));
    grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--arch-col-min)), var(--card-col-max)));
    justify-content: center;
}
.arch-card { container-type: inline-size; }   /* SINGURA declarație de containment */

.arch-row { display: flex; align-items: stretch; gap: clamp(.5rem, 2.5cqi, 1rem); flex: 1 1 auto; }
.arch-poster {
    flex: 0 0 auto; aspect-ratio: 3 / 4; height: clamp(9.5rem, 30cqi, 13.75rem);
    border-radius: var(--radius-sm); overflow: hidden;
    background: var(--kf-surface-3);   /* venit din kf, B159 L10 F1 */
}
.arch-poster[data-lightbox] { cursor: zoom-in; }
.arch-poster img { display: block; width: 100%; height: 100%; object-fit: cover; filter: none; }
.arch-poster--empty { display: flex; align-items: center; justify-content: center; font-size: var(--font-2xl); color: var(--kf-outline); }

.arch-body {
    flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: .35rem;
    padding: clamp(.5rem, 2cqi, .75rem) clamp(.6rem, 2.5cqi, 1rem) clamp(.5rem, 2cqi, .75rem) 0;
}
.arch-title {
    font-weight: var(--type-title-weight, 900); line-height: 1.03; margin: 0;
    font-size: clamp(var(--font-xl), 6cqi, var(--font-4xl));
    overflow-wrap: anywhere;
    /* Venite din kf, B159 L10 (F1). Tracking-ul e .01em, nu .02em ca la `.arch-year`:
       `--type-title-tracking` nu e declarat nicaieri, deci fallback-ul de la call-site E valoarea
       reala (tiparul „rol la call-site, fallback = valoarea de azi", tokens.css § roluri). */
    font-family: var(--kf-font-display);
    text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .01em);
    color: var(--kf-text);
}
.arch-meta {
    display: flex; flex-wrap: wrap; align-items: center; gap: .2rem clamp(.35rem, 2cqi, .8rem);
    /* venite din kf, B159 L10 F1 */
    font-family: var(--kf-font-mono); font-size: var(--font-xs); color: var(--kf-text-variant);
}
.arch-meta i { opacity: .75; }
.arch-month { text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .05em); color: var(--text-muted); }

/* margin-top:auto împinge chipurile la baza coloanei → se aliniază între cardurile de pe același rând. */
.arch-cats { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: auto; padding-top: .55rem; }
.arch-cat { display: inline-flex; align-items: center; gap: .3rem; padding: .15rem .4rem; border: 1px solid var(--border-subtle); border-radius: var(--radius-md); text-decoration: none; color: var(--text-muted); }
.arch-cat:hover { border-color: var(--accent); color: var(--text); }
/* Fața de titlu ca pe /concursuri (ROL SUBTITLU), la mărimea actuală. Fără `color`, ca
   .arch-cat:hover să ridice textul. */
.arch-cat__fmt { font-family: var(--kf-font-display); font-weight: var(--type-label-weight, 900); font-size: var(--font-xs); letter-spacing: var(--type-label-tracking, .02em); }
.arch-cat--more { font-weight: 700; }
/* „Niciun rezultat" — ASCUNS din CSS by default. Altfel e vizibil la primul paint, sub lista
   completă, până rulează JS-ul (și rămâne vizibil dacă JS-ul cade). JS-ul comută clasa .is-on,
   NU style.display: cardurile folosesc deja axa aia, iar două mecanisme pe aceeași axă se bat. */
.arch-empty { display: none; }
.arch-empty.is-on { display: block; }
/* h1 = SEMNUL (alt = numele) + restul tagline-ului („your move."): textul accesibil e tagline-ul
   întreg, pe ecran numele e semnul. Slotul semnului ia mărimea din --brand-logo-h.
   It. 5 (ownerul): semnul și fontul lui se CENTREAZĂ unul pe altul, iar distanța dintre ele se strânge.
   `gap: 0`, nu o valoare mică: PNG-ul e tăiat exact pe cerneală (măsurat: 0px margine transparentă pe toate
   laturile), deci tot spațiul vizibil rămas vine din jumătatea de leading a lui `line-height: 1` de mai jos. */
.home-hero__title { display: flex; flex-direction: column; align-items: center; gap: 0; margin: 0 0 .6rem; }
.home-hero__mark { --brand-logo-h: clamp(64px, 11vw, 124px); line-height: 1; }
.home-hero__words {
    font-family: var(--font-display); font-weight: var(--type-display-weight, 900); text-transform: var(--type-display-case, uppercase); letter-spacing: var(--type-display-tracking, .015em);
    font-size: clamp(2.2rem, 6vw, 3.4rem); line-height: 1; color: var(--text);
}
.home-hero__tag { font-size: var(--font-md); max-width: 38rem; margin: 0 auto; }
.home-card__body { padding: .8rem .9rem; display: flex; flex-direction: column; gap: .65rem; align-items: flex-start; }
.home-card__body p { margin: 0; font-size: var(--font-sm); color: var(--kf-text-variant); }   /* culoarea: venita din kf, B159 L10 F1 */
.home-card__cta { display: flex; flex-wrap: wrap; gap: .5rem; }

/* --- Pagina de start (ADR 0053, 2026-09-16; it. 2 în aceeași zi, după vizualizarea ownerului).
   REUSE-FIRST: ușile sunt .hub-card, cardurile de concurs .ccard + _contest_hero, clasamentul
   .lb-panel + .tier-table, butoanele .btn-ink, contorul din capete .filter-pill__n. Aici stau
   DOAR blocurile noi, fiecare o dată: grila hero-ului, coloana ușilor, contoarele, capul de
   secțiune, lista de rânduri (campioni + arhivă, ACEEAȘI clasă), adresa cu „copiază".
   Tipografia NU se scrie aici: .home-sec__h și .home-stats__l sunt în lista rolului ETICHETĂ
   (components.css §18b); titlurile de conținut sunt display 900. --- */

/* Hero pe DOUĂ coloane: promisiunea stânga, ușile dreapta. Sub ~52rem — o coloană, ușile sub
   text (hero-ul are lățimea paginii, deci aici ecranul chiar e măsura potrivită). */
.home-hero { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: clamp(1rem, 4vw, 2.5rem); align-items: center; border: 1px solid var(--sm-card-edge); background: var(--sm-card-surface); }
/* Centrarea se declară pe COPIL, nu pe secțiune: `body.kf .home-hero` pune `text-align: left`, iar o
   declarație proprie bate valoarea moștenită indiferent de layere — deci nu cere nicio regulă în skin. */
.home-hero__text { min-width: 0; text-align: center; }
.home-doors { display: grid; gap: .75rem; min-width: 0; }
@media (max-width: 52rem) {
    .home-hero { grid-template-columns: minmax(0, 1fr); }
}

/* Contoarele pe BANDĂ INVERSĂ față de temă (it. 3, ownerul): ink pe light, paper pe dark —
   perechea --surface-inverse / --text-on-inverse, COMPLETĂ (fundal tematizat → text tematizat).
   4 blocuri pe un rând, cad UNUL CÂTE UNUL (flex-wrap + gap fluid, nu @media). Cifra în mono
   tabular; eticheta = rolul ETICHETĂ (lista din components.css §18b). Separatorul dintre blocuri e
   DECOR (linie), derivat din cerneala benzii cu color-mix — nu e text, nu intră în auditul de contrast.
   Cerneala secundară a etichetei NU mai e `opacity` (B159 L4.2): opacitatea e invizibilă pentru auditul de contrast,
   iar banda nu mai e mereu inversă — pe skin e tonală. `--text-on-inverse-mid` randează identic, dar e o culoare. */
.home-stats {
    list-style: none; margin: 0 0 1.4rem; padding: clamp(.9rem, 3vw, 1.4rem) clamp(.8rem, 4vw, 2rem);
    display: flex; flex-wrap: wrap; justify-content: center; gap: .9rem clamp(1.2rem, 6vw, 4rem);
    background: var(--surface-inverse); color: var(--text-on-inverse);
}
.home-stats__it {
    display: flex; flex-direction: column; align-items: flex-start; min-width: 0;
    border-inline-start: 1px solid var(--sm-line); padding-inline-start: .7rem;
}
.home-stats__v {
    font-family: var(--font-mono); font-weight: 700; font-variant-numeric: tabular-nums;
    font-size: clamp(1.6rem, 5vw, 2.6rem); line-height: .95; color: inherit;
}
.home-stats__l { color: var(--text-on-inverse-mid); margin-top: .3rem; white-space: nowrap; }

/* Capul de secțiune: TITLU (ADR 0054 decizia 7 — secțiunea ESTE conținutul căutat: „Concursuri deschise",
   „Clasament", „Arhivă") + „mai mult" pe aceeași riglă; pe îngust cad unul sub altul. Rețeta de text stă AICI, ca la
   `.home-cta-band__t`, nu în lista rolului ETICHETĂ din components.css §18b, de unde a ieșit la B159 L4.4.
   16→18px, adică MAI MARE decât orice cuprinde (numele de concurs din carduri și rândurile de arhivă) — ierarhia se
   citește de sus în jos; cerneala secundară îl ține totuși în urma conținutului. */
.home-sec { margin: clamp(1.2rem, 4vw, 2rem) 0 0; min-width: 0; }
.home-sec__head {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .5rem 1rem;
    border-bottom: var(--border-dark); padding-bottom: .35rem; margin-bottom: .8rem;
}
.home-sec__h {
    font-family: var(--font-display); font-weight: var(--type-title-weight, 900); text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .02em);
    font-size: clamp(var(--font-lg), 1.3vw, var(--font-xl)); line-height: 1.2;
    margin: 0; color: var(--text-secondary); display: inline-flex; align-items: center; gap: .5rem; flex-wrap: wrap;
}
.home-sec__sub { color: var(--text); }
/* Contorul din cap NU-și scrie fontul nicăieri (components.css îi dă doar cifre tabulare + stingere): îl moștenea
   de la etichetă, iar de când capul e TITLU l-ar moșteni la 18px/800. Rămâne ce e — o cifră de context. */
.home-sec__h .filter-pill__n { font-family: var(--font-mono); font-weight: 700; font-size: var(--font-sm); }
.home-sec__empty { margin: 0; font-size: var(--font-sm); color: var(--text-secondary); }
.home-sec__empty a { color: var(--text); }

/* Cardurile de concurs: aceeași .ccard ca pe /concursuri, fără masonry (max 3, o grilă simplă). */
.home-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); gap: 1rem; }

/* Lista de rânduri (campioni + arhivă): ramă, hairline între rânduri, stânga = ce, dreapta = cine/când.
   Numele oamenilor NU sunt uppercase (B74 ③: caps doar pe etichete). */
.home-list { list-style: none; margin: 0; padding: 0; /* `--border-dark` era `2px solid var(--border-strong)`: chenarul se subtiaza la 1px si ia
     culoarea de card din paleta de azi (coborat din `@layer skin` la B159 L10, F2). */
    border: 1px solid var(--sm-card-edge); border-radius: var(--radius-card, 0); background: var(--sm-card-surface); }
.home-list__row {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .3rem 1rem;
    padding: .55rem .75rem; border-bottom: 1px solid var(--border-hair);
}
.home-list__row:last-child { border-bottom: 0; }
.home-list__cat { min-width: 0; color: inherit; text-decoration: none; }
.home-list__cat:hover .home-list__name { text-decoration: underline; }
.home-list__name { font-weight: 700; font-size: var(--font-md); color: var(--text); overflow-wrap: anywhere; }
.home-list__sub { font-family: var(--font-mono); font-size: var(--font-xs); color: var(--text-secondary); }

/* Clasament + arhivă, unul lângă altul; pe îngust unul sub altul. */
.home-two { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); gap: 0 1.5rem; }

/* Banda finală (Serial Position: ultimul lucru citit) — aceeași pereche inversă ca banda de cifre.
   Butonul ia PERECHEA INVERSĂ (paper pe ink): .btn-ink e el însuși ink, ar dispărea pe bandă. */
.home-cta-band {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .75rem 1.5rem;
    margin: clamp(1.5rem, 5vw, 2.5rem) 0 1rem; padding: clamp(1rem, 3vw, 1.6rem) clamp(1rem, 4vw, 2rem);
    background: var(--surface-inverse); color: var(--text-on-inverse);
}
.home-cta-band__t {
    font-family: var(--font-display); font-weight: var(--type-title-weight, 900); text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .02em);
    font-size: clamp(var(--font-lg), 3vw, var(--font-2xl)); line-height: 1.1; margin: 0;
}
.home-cta-band__s { margin: .2rem 0 0; font-size: var(--font-sm); color: var(--text-on-inverse-mid); }
/* ⛔ ȘTERSE la B159 L10 (F3), din același motiv: culoarea ramei vine de la `.btn-ink`. */



} /* END @layer app */

@layer base {
  /* FOCUS VIZIBIL — SSOT unic (WCAG 2.4.7 nivel A + 2.4.11 Focus Appearance).
     Trei reguli de formular kf ștergeau `outline` și lăsau ca semnal doar culoarea unei borduri;
     pe `.entry-form` (înscrierea publică) nu mai rămânea NIMIC — cine navighează cu Tab nu vedea
     unde e. `:focus-visible`, NU `:focus`: inelul apare la tastatură, nu la click de mouse.
     `outline`, NU box-shadow: nu e tăiat de `overflow: hidden` și nu intră în coliziune cu
     shorthand-urile de box-shadow deja folosite pe câmpuri. `--focus-ring` era token MORT. */
  :focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
  }

  /* ---- Bara lipita sus: UN element de sticla (ADR 0054, Decizia 5). Coborat din `@layer skin` la
     B159 L10 (F2); blocul sta la coada layerului ca sa bata regula de baza a lui `.app-nav`. ---- */
  @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .app-nav {
      background: var(--sm-glass-bg);
      -webkit-backdrop-filter: saturate(140%) blur(14px);
      backdrop-filter: saturate(140%) blur(14px);
    }
  }
  /* Varianta OPACA e cea implicita: cine cere mai putina transparenta sau mai mult contrast primeste
     suprafata plina, fara filtru. */
  @media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .app-nav { background: var(--sm-s1); -webkit-backdrop-filter: none; backdrop-filter: none; }
  }

  /* Partea din garda de miscare a carei regula de baza traieste in fisierul asta: punctul de stare al
     paginii. Coborata din `@layer skin` la B159 L10 (F2). */
  @media (prefers-reduced-motion: reduce) {
    .page-state__dot { animation: none; }
  }

/* Coborata din `@layer skin` la B159 L10 (F2). */
.form-check-input:focus { border-color: var(--sm-text-hi); box-shadow: 0 0 0 .25rem color-mix(in srgb, var(--sm-text-hi) 22%, transparent); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.form-check-input:checked { background-color: var(--sm-switch-on); border-color: var(--sm-text-hi); }}
