/* ============================================================
   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).
   components.css - S3 TABLES, S4 BADGES, S5/5b PILLS/TABS, S6 CHIPS,
   S7 CARDS+cont, S9 FORMS, S12/12b/12c PUBLIC, S14 RANKING, S15 AUTH/ERROR,
   S18 RESULTS.
   ============================================================ */
@layer vendor, app, tokens, base, layout, components, utilities;

@layer app {
/* === 3. TABLES ============================================= */

/* Tabel compact */


/* Coloane fixe frecvente */


.col-actions { width: 100px; text-align: right; white-space: nowrap; }

/* Stat column */

.stat-value { font-weight: 700; color: var(--fg1); }


/* Results table */


/* Section header */


/* === 4. BADGES & STATUS ==================================== */

/* Badge status concurs */
/* Marcaj STERS la B159 L10 F1 ca ORFAN. Justifica o diferenta care nu mai exista: pe editarea
   concursului insignele de stare primesc de acum acelasi aspect ca in rest — aceeasi decizie
   ca la butoane (owner, 2026-09-23). Diferenta era un accident: pagina n-a primit niciodata clasa. */
.badge-status {
    font-family: var(--font-display);
    font-size: var(--font-xs);
    font-weight: var(--type-status-weight, 900);
    padding: var(--pad-badge);
    border-radius: var(--radius-md);
    display: inline-block;
    white-space: nowrap;
    line-height: 1.4;
    text-transform: var(--type-status-case, uppercase);
    letter-spacing: var(--type-status-tracking, 0.08em);
}
.badge-status--draft    { background: var(--status-draft-bg);   color: var(--status-draft-fg); }
.badge-status--open     { background: var(--status-open-bg);    color: var(--status-open-fg); }
.badge-status--closed   { background: var(--status-closed-bg);  color: var(--status-closed-fg); }

/* ---- INSIGNE DE STARE + NUMARATOAREA DE ZILE: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* status badge / countdown → fill plat pătrat (flat, fără border) */
  .badge-status, .cbc-days {
    border: 0; border-radius: var(--radius-status, 0);
    padding: .22rem .55rem; font-size: var(--font-xs); font-weight: 700;
    font-family: var(--kf-font-mono); letter-spacing: var(--type-status-tracking, .06em); text-transform: var(--type-status-case, uppercase);
    display: inline-flex; align-items: center;
  }
  .badge-status--open   { background: var(--kf-tint);       color: var(--kf-on-tint); }
  .badge-status--active { background: var(--kf-secondary);  color: var(--kf-on-secondary); }
  .badge-status--done   { background: var(--kf-surface-inset); color: var(--kf-text-variant); }
  .cbc-days { background: var(--kf-alert-soft); color: var(--kf-on-alert-soft); }
  /* `.cbc-days--today` sters de AICI: umplerea si cerneala ii vin intregi din `admin.css`
     (`background: var(--sm-warning); color: var(--sm-bg)`), unde a coborat regula de skin la F2. */

.badge-status--active   { background: var(--status-active-bg);  color: var(--status-active-fg); }
.badge-status--done     { background: var(--status-done-bg);    color: var(--status-done-fg); }

/* Badge status battle/bout */

.badge-battle--pending   { background: var(--battle-pending-bg);   color: var(--battle-pending-fg); }
.badge-battle--progress  { background: var(--battle-progress-bg);  color: var(--battle-progress-fg); }
.badge-battle--completed { background: var(--battle-completed-bg); color: var(--battle-completed-fg); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-tab .cat-panel-hdr .badge-battle--progress { color: var(--sm-on-live); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-tab .cat-panel-hdr .badge-battle--completed { color: var(--sm-on-accent); }

/* Badge faza bracket */


/* Badge categorie */
.badge-category {
    font-size: var(--font-xs);
    font-weight: 700;
    padding: var(--pad-badge);
    border-radius: var(--radius-md);
    background: var(--surface-inverse);
    color: var(--text-on-inverse);
    text-decoration: none;
    display: inline-block;
    transition: background 0.1s;
}
.badge-category:hover { background: var(--primary); color: var(--on-primary); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.ecat__hdr .badge-category:hover { background: var(--sm-s2); color: var(--sm-text-hi); }

/* Badge metoda calificare */


/* Valori semantice */


/* === 5. PILLS & FILTERS ==================================== */

.filter-pill {
    font-size: var(--font-sm);
    padding: var(--pad-pill);
    border-radius: var(--radius-sm);
    border: 1.5px solid var(--border-control);
    background: var(--sm-s1);
    color: var(--sm-text-hi);
    cursor: pointer;
    transition: background 0.1s, color 0.1s;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
    line-height: 1.3;
}
.filter-pill:hover  {    }
.filter-pill.active { background: var(--sm-btn-fill); color: var(--sm-on-btn-fill); border-color: var(--sm-btn-fill); }

/* ---- PAGINARE / PASTILE DE FILTRU: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* Paginare numere. Nota veche „scoped kf — nu atinge paginile legacy cu .filter-pill" NU mai e
     adevarata: toate template-urile care randeaza `.filter-pill` (home, arhiva, concursuri, clasament,
     admin/traduceri, `_pager`, `_tab_dansatori`) sunt azi pe pagini cu `kf`. Verificat la B159 L10 F1.
     `color`, `background` si variantele lor de stare n-au venit incoace: skinul le da intregi. */
  .filter-pill {
    font-family: var(--kf-font-mono); border: 1px solid var(--sm-line);
    border-radius: var(--radius-control, 0);
  }


/* ── Paginare — SSOT pentru toate listele paginate (`_pager.html.twig`) ─────────────────────
   Trăiește lângă `.filter-pill` fiindcă din ea e făcută: paginile SUNT pastile, deci moștenesc
   gratis și ținta de atingere de 44px de la ≤640px (§16). Aici rămâne doar aranjamentul rândului
   plus separatorul de interval — restul e refolosire, nu componentă nouă.

   Pagina curentă e `<span>`, nu `<a>`: un link către pagina pe care ești deja e o țintă de tap
   care nu face nimic. `.filter-pill` acoperă ambele tag-uri (n-are selector de element). */
.pager      { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; padding: .5rem; }
.pager__gap { font-family: var(--font-mono); font-size: var(--font-sm); color: var(--fg3); padding-inline: .1rem; }
/* Săgețile poartă doar o iconiță: fără o lățime minimă ar fi mai înguste decât pastilele cu cifre
   și rândul s-ar citi neregulat. */
.pager__nav { justify-content: center; min-width: 2rem; }

/* Rândul tab-uri + filtru (/concursuri): nav-ul și pill-ul „Ale mele" pe aceeași linie, la aceeași
   înălțime vizuală.
   Pill-ul stă IMEDIAT DUPĂ tab-uri, nu împins la capătul opus (B68): cu `justify-content:
   space-between` rămânea singur în dreapta și se citea ca destinație de meniu, nu ca filtru al
   listei de dedesubt — exact raportul „nu se înțelege ce e". Gap-ul mai mare decât cel dintre
   tab-uri îl ține un grup distinct, fără să-l exileze. */
.tabs-row { display: flex; align-items: center; gap: clamp(.5rem, 2vw, 1.25rem); flex-wrap: wrap; margin-bottom: 1rem; }
.tabs-row .filter-pill { padding: .4rem .85rem; font-size: var(--font-xs); text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .06em); font-weight: 700; }
/* Contorul din pill = semnalul care îl face să se citească a FILTRU („câte rămân dacă apăs"), nu a
   buton. Îl scrie mine-filter.js: serverul nu-l poate calcula, filtrarea e client-side. Tabular ca
   să nu salte lățimea pill-ului la fiecare recalculare. */
.filter-pill__n { font-variant-numeric: tabular-nums; opacity: .65; }
.filter-pill.active .filter-pill__n { opacity: .8; }

/* Pastila de TIER (rang / local), pe `/arhiva` și pe tabloul de bord. Clasă proprie tocmai ca să nu
   atingă `text-bg-*` din Bootstrap, care e `!important` în `@layer vendor`.
   Venită din `kf-skin.css` la B159 L10 (F1, prima familie deblocată de B178): acolo era singura ei
   definiție — nicio regulă de bază, nicio suprascriere de skin — deci mutarea e doar o schimbare de
   layer, cu valorile calculate neatinse. Tokenii `--kf-*` rămân ai ei până la promovarea din F3. */
.tier-pill {
    border: 0; border-radius: var(--radius-chip, 0); padding: .2rem .5rem;
    background: var(--kf-surface-inset); color: var(--kf-text-variant);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-xs);
    text-transform: var(--type-status-case, uppercase); letter-spacing: var(--type-status-tracking, .03em);
    display: inline-flex; align-items: center;
}
.tier-pill--rank { background: var(--kf-tint); color: var(--kf-on-tint); }


/* === 5b. FOLDER TABS (segmented page tabs, lipite de conținut) === */
.folder-tabs {
    align-items: flex-end;
}
.folder-tab {
    line-height: 1.3; text-decoration: none; cursor: pointer;
}

/* ---- TAB-URI DE PAGINA: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* ---- Folder tabs → segmented flat, divizor 1px subțire ---- */
  .folder-tabs {
    display: flex; flex-wrap: wrap; gap: 0;
    border: 1px solid var(--sm-line); margin-bottom: var(--kf-5);
    background: var(--kf-surface-bright);
    padding: 0;
    border-radius: var(--kf-r-sm);
    width: fit-content;
    overflow: hidden;
  }
  .folder-tab {
    border: 0; border-right: 1px solid var(--sm-hair); margin: 0;
    border-radius: 0;
    padding: .5rem 1.1rem;
    font-family: var(--kf-font-mono);
    /* .06em = tracking-ul rolului de ETICHETĂ (components.css § 18b). Mărimea rămâne `--font-sm`:
       un tab de PAGINĂ are voie să fie mai mare decât o etichetă de grup — rolul deține familia,
       cazul și tracking-ul, nu treapta. */
    font-weight: 700; font-size: var(--font-sm); text-transform: var(--type-tab-case, uppercase); letter-spacing: var(--type-tab-tracking, .06em);
    color: var(--kf-text-variant);
    background: transparent;
    display: inline-flex; align-items: center; gap: .4rem;
    transition: none;
  }
  .folder-tab:last-child { /* `border-right: 0` (prescurtarea) resetează ȘI culoarea, pe care skinul o dădea; longhand-urile
     păstrează exact ce se randează azi — lățime 0, fără stil, culoarea de la `.folder-tab`.
     B159 L10 (F2). */
  border-right-width: 0; border-right-style: none; }
  .folder-tab:hover { color: var(--kf-text); background: var(--kf-surface-2); }
  .folder-tab.active {
    background: var(--kf-primary);
    color: var(--kf-on-primary);
  }



/* === 5c. RAIL — o bandă care SCROLLEAZĂ pe orizontală ========================================
   SSOT al mecanicii. Era scrisă de doua ori (`.tline__rail` mai jos in fisier, `.cat-strip` in
   scoreboard.css), iar banda de tab-uri din admin ar fi fost a treia copie. Regula scrisa:
   UI_RULES „Tab-urile stau pe UN rand care scrolleaza, niciodata pe doua".

   Mecanica si ESTOMPAREA sunt clase SEPARATE, pentru ca nu merg mereu impreuna:
     `.rail`      = deruleaza + ascunde bara de scroll  → benzi de tab-uri (au cap + sageti)
     `.rail-fade` = doar masca de margine               → containere care isi PASTREAZA bara
                                                          (la un tabel bara E afordanta, nu zgomot)
   ⚠️ NU declara `padding-inline` aici: `.tline__rail` isi pune singur revarsarea (`--tline-bleed`)
      si, fiind mai jos in acelasi fisier, o regula de aici i-ar bate-o.
   ⚠️ NU `justify-content: center` la consumatori: centrarea continutului impinge inceputul la
      offset NEGATIV = zona inaccesibila. Se centreaza CUTIA (width: fit-content; margin-inline: auto). */
.rail {
    /* `nowrap` e valoarea inițială, dar e scrisă explicit fiindcă e CONTRACTUL benzii: un rail care
       ajunge să se împacheteze pe două rânduri nu mai e rail. (Inertă pe consumatorii care nu
       sunt flex, ex. wrapperul de tabel.) */
    flex-wrap: nowrap;
    overflow-x: auto; overscroll-behavior-x: contain;
    scroll-snap-type: x proximity; scroll-padding-inline: .5rem;
    scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }

/* Masca spune IN CE PARTE mai e continut. Starea vine din `data-ov`, pus de controllerul `rail`
   (stare in CSS, nu in JS: JS-ul raporteaza faptul, CSS-ul decide aspectul). Masca lucreaza pe
   canalul ALFA — `black`/`transparent` de aici nu sunt culori de brand, ci opacitate.
   ⚠️ Nu e gratuita: `mask-image` diferit de `none` creeaza stacking context si promoveaza
   elementul la strat compus, deci antialiasingul textului cade pe grayscale. De-aia clasa se pune
   DOAR pe benzile care chiar primesc `data-ov`, niciodata „pentru orice eventualitate".
   Selectorul acopera ambele cazuri: controllerul poate sta pe un stramos SAU pe banda insasi
   (Stimulus permite controller si target pe acelasi element). */
.rail-fade { --rail-fade-l: 0px; --rail-fade-r: 0px;
    mask-image: linear-gradient(to right,
        transparent 0, black var(--rail-fade-l),
        black calc(100% - var(--rail-fade-r)), transparent 100%);
}
[data-ov~="l"] .rail-fade, .rail-fade[data-ov~="l"] { --rail-fade-l: 1.75rem; }
[data-ov~="r"] .rail-fade, .rail-fade[data-ov~="r"] { --rail-fade-r: 1.75rem; }

/* Capul benzii: NUMESTE controlul, spune CATE elemente sunt si da sageti de derulare. Fara el, o
   banda care scrolleaza e invizibila ca mecanism pe telefon: omul vede doua elemente si n-are de
   unde sti ca mai exista trei (raport user 2026-08-15). Markup-ul = macro `_rail.html.twig`. */
.rail-nav__hd  { display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding-bottom: .15rem; }
.rail-nav__lbl { display: inline-flex; align-items: baseline; gap: .35rem; color: var(--text-muted); }
.rail-nav__n {
    font-family: var(--font-mono); font-size: var(--font-2xs); font-weight: 700;
    background: var(--surface-2); color: var(--text-secondary);
    border: 1px solid var(--border-hair); padding: 0 4px;
}
/* `hidden` in markup = starea implicita FARA JS (sagetile n-ar face nimic). Controllerul il scoate
   doar cand banda chiar depaseste. `display: flex` pierde in fata lui `[hidden]` prin reboot-ul Bootstrap
   (`[hidden] { display: none !important }`, @layer vendor) — o regula `[hidden]` aici n-ar avea efect. */
.rail-nav__arrows { display: flex; gap: .2rem; }
.rail-nav__ar {
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.5rem; height: 1.5rem; padding: 0;
    border: 1px solid var(--border-control); background: var(--surface);
    color: var(--text); cursor: pointer; line-height: 1;
}
.rail-nav__ar:hover:not(:disabled) { background: var(--volt); color: var(--volt-ink); border-color: var(--volt-ink); }
.rail-nav__ar:disabled { opacity: .35; cursor: default; }


/* === 6. CHIPS ============================================== */

/* Chip competitor inline */


/* Chip judecator */
.judge-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: var(--font-sm);
    padding: var(--pad-chip);
    border: 1.5px solid var(--border-strong);
    border-radius: var(--radius-md);
    background: var(--card);
    color: var(--fg1);
    text-decoration: none;
    transition: background 0.1s, color 0.1s;
    line-height: 1.4;
}
.judge-chip:hover { background: var(--volt); color: var(--volt-ink); border-color: var(--volt-ink); }

.judge-chip .chip-system  { font-size: var(--font-xs); color: var(--fg3); background: var(--paper-2); padding: 0 3px; border-radius: var(--radius-sm); }
.judge-chip__del-form { display: inline; line-height: 0; }
.judge-chip__del { border: 0; background: transparent; color: var(--danger); padding: 0; line-height: 1; cursor: pointer; font-size: var(--font-xs); }
.judge-chip:hover .judge-chip__del { color: var(--volt-ink); }
/* Jurat marcat pentru eliminare (staged) — se aplică la „Salvează modificările" */


/* Hint jurați per categorie (panel: X/N roluri · mai alege …) */
.exhint { font-size: var(--font-xs); margin-bottom: 2px; min-height: 1em; }
.exhint--ok   { color: var(--success); }
.exhint--warn { color: var(--warning); }
.exhint--err  { color: var(--danger); font-weight: 600; }


/* === 7. CARDS — Hub organizator ============================= */

.hub-card {
    border: 2px solid var(--border-strong);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--sm-card-surface);
    transition: box-shadow .1s;
}
.hub-card:hover {  }

/* Strip status colorat (top) */
.hub-strip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: .28rem .75rem;
    font-size: var(--font-xs);
    text-transform: var(--type-status-case, uppercase);
}
.hub-strip--open   {     }
.hub-strip--closed {   }
.hub-strip--active {   }

/* ---- CONTUL MEU + ACTIUNI DE CATEGORIE + USILE: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* ═══════════ DASHBOARD „Contul meu" /cont (scoped body.kf) ═══════════ */

  /* Page header → titlu display + acțiune dreapta, hairline jos */
  .page-header {
    display: flex; align-items: flex-end; justify-content: space-between;
    flex-wrap: wrap; gap: var(--kf-3);
    border-bottom: var(--kf-border); padding-bottom: var(--kf-3); margin-bottom: var(--kf-5);
  }
  .page-header__title {
    font-family: var(--kf-font-display); font-weight: var(--type-display-weight, 900); font-size: var(--font-8xl);
    line-height: .98; letter-spacing: var(--type-display-tracking, .01em); text-transform: var(--type-display-case, uppercase);
    color: var(--kf-text); margin: 0;
  }

  /* Familia BUTOANELE INK a plecat in base-layout.css la B159 L10 (F1). */


  /* Card profil organizator → flat hairline */
  .acct-card {
    border: var(--kf-border); border-radius: var(--radius-card, 0);
    padding: var(--kf-3) var(--kf-4);
  border-color: var(--sm-card-edge); }
  .acct-card__ico { color: var(--kf-text-variant); }
  .acct-card__name {
    font-family: var(--kf-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: var(--font-xl); color: var(--kf-text);
  }
  .acct-card__email { font-family: var(--kf-font-mono); font-size: var(--font-sm); color: var(--kf-text-variant); }
  .acct-form { border-top: 1px solid var(--kf-line); }
  .acct-form__lbl {
    font-family: var(--kf-font-mono); font-size: var(--font-2xs); font-weight: 700;
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .05em); color: var(--kf-text-variant);
  }
  .acct-card .form-control {
    background: var(--kf-surface-bright); border: 1px solid var(--sm-line);
    /* ⚠️ `border-radius: var(--radius-field, 0)` RAMANE desi nu se randeaza azi (regula generica a
       campurilor e mai specifica in lant): e ULTIMUL call-site din tot repo-ul al rolului de forma
       `field`, iar un rol fara cititor moare — `TypeRoleTokensTest` il cere. Fallbackul e colțul de
       azi. Stearsa o data de ucigasul de treziri la B159 L10 (F3), pusa la loc cu motivul. */
    border-radius: var(--radius-field, 0);
    color: var(--kf-text); font-family: var(--kf-font-mono); font-size: var(--font-md); box-shadow: none;
  }

  /* Rând de arhivă (/arhiva) — afiș portret + coloană de text.
     AICI stă DOAR ce un token nu poate exprima: fontul mono, uppercase, filtrul grayscale→color
     și fundalul paginii. Bordurile, razele (=0) și suprafețele vin gratis din remap-ul global de
     tokeni de mai sus — NU scrie reguli de culoare pentru ele. Dimensiunile (aspect-ratio, clamp,
     container query) rămân în @layer app: kf-ul e o piele, nu un layout. */
  /* Familia `.arch-*` a plecat in base-layout.css § „Arhiva publica" la B159 L10 (F1): fiecare
     declaratie kf adauga o proprietate pe care regula de baza n-o avea, deci a fost o reuniune
     curata, fara conflict.
     Cele doua reguli de `filter` (grayscale pe afis + revenirea la hover) NU s-au mutat: erau MOARTE.
     `body.skin-modern .arch-poster img { filter: none }` statea in `@layer skin`, care batea
     `@layer components` inaintea specificitatii — deci nici macar regula de hover nu ajungea la
     element. Odata cu ele, `filter: none` a ramas fara ce sa anuleze si a disparut la F2. */

  /* Familia TABEL DE CALIFICARE a plecat in components.css la B159 L10 (F1). */

  /* „Înscriși" avea DOUĂ rețete pentru același cuvânt: `.ccat__lbl` (/concursuri, --font-xs .06em)
     și `.cat-entries-lbl` (pagina categoriei, --font-2xs .05em). `.cat-entries-lbl` e deja în lista
     ROL 3 ETICHETĂ (components.css § 18b) — care i-ar da exact rețeta lui `.ccat__lbl` — dar regula
     kf de aici îl trăgea înapoi. Cazul din UI_RULES „îl scoți întâi din regula kf, apoi rolul preia":
     regula de familie/mărime/tracking e ȘTEARSĂ, rămâne doar culoarea, ca să coincidă cu `.ccat__lbl`.
     NU muta selectorul în regula `.ccat__lbl` (acum în components.css, B159 L10 F1): aceea are
     `margin: 0` shorthand, care ar șterge `margin-right: 3px` din components.css. */
  /* `.tier-pill` a plecat în components.css § 5 la B159 L10 (F1): era singura ei definiție din tot
     repo-ul, deci mutarea n-a fost decât o coborâre de layer. B178 a deblocat-o — până atunci ieșea
     din scopul `body.kf` al trecerii de reguli și producea chei fantomă. */

  /* Acțiuni per-categorie → next-step = lime plat; restul = outline hairline mono */
  .cat-actions .btn {
    border-radius: var(--radius-control, 0); border: 1px solid var(--kf-line);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-xs);
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .04em); padding: .3rem .7rem;
    transition: none; background: var(--kf-surface-bright); color: var(--kf-text-variant);
  }
  .cat-actions .btn:hover { border-color: var(--kf-primary-solid); color: var(--kf-text); }
  .cat-actions .btn-success,
  .cat-actions .btn-warning,
  .cat-actions .btn-dark {
    background: var(--kf-primary); color: var(--kf-on-primary); border-color: var(--kf-primary);
  }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-dark { --bs-btn-color: var(--sm-on-btn-fill); --bs-btn-bg: var(--sm-btn-fill); --bs-btn-border-color: var(--sm-btn-fill); --bs-btn-hover-color: var(--sm-on-btn-fill); --bs-btn-hover-bg: var(--sm-btn-fill-hover); --bs-btn-hover-border-color: var(--sm-btn-fill-hover); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill-hover); --bs-btn-active-border-color: var(--sm-btn-fill-hover); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: var(--sm-s3); --bs-btn-disabled-border-color: var(--sm-s3); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.tab-actions .btn-dark { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
  .cat-actions .btn-success:hover,
  .cat-actions .btn-warning:hover,
  .cat-actions .btn-dark:hover {
    background: var(--kf-primary-solid); color: var(--kf-on-solid); border-color: var(--kf-primary-solid);
  }

  /* Editează profil → outline hairline mono transparent */
  .acct-card .btn-outline-secondary {
    border-radius: var(--radius-control, 0); border: 1px solid var(--border-control);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-xs);
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .04em);
    transition: none;
  border-color: var(--sm-line); color: var(--sm-text-hi); background: var(--sm-s3); }
  /* `background`, `color` si tot hover-ul au plecat: skinul le da intregi. */

  /* Familia LANDING / HERO a plecat in base-layout.css la B159 L10 (F1). */



  /* Hub cards (DOAR landing) → flat hairline, fără radius/umbră */
  .hub-card {
    /* `--kf-border` era `1px solid var(--kf-line)`: ramane 1px, culoarea vine din paleta de azi
     (coborata din `@layer skin` la B159 L10, F2). */
    border: 1px solid var(--sm-card-edge); border-radius: var(--radius-card, 0);
    transition: none;
  box-shadow: var(--sm-elev-float); }
  .hub-strip {
    font-family: var(--kf-font-display); font-weight: var(--type-status-weight, 900);
    border-bottom: 1px solid var(--sm-hair); letter-spacing: var(--type-status-tracking, .08em);
  color: var(--sm-text-hi); background: var(--sm-strip-surface); }


/* Descriere concurs (sub hero, în metarow) */


/* Rândul de arhivă (.arch-*) trăiește ACUM într-un singur bloc, în secțiunea „Arhivă publică"
   de mai jos — căutați `--- Arhivă publică (/arhiva) ---`. Clasele vechi `hub-head`/`hub-thumb`
   au fost redenumite acolo (2026-07-27); `hub-card`/`hub-strip` de pe home sunt alt component. */

/* Acțiuni de stare pe rândul de categorie — următorul pas explicit */
.cat-actions { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .35rem; }
.cat-actions form { display: inline; margin: 0; }

/* === Cont organizator (profil) === */
.acct-card {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem;
background: var(--sm-card-surface); }
.acct-card__id { display: flex; align-items: center; gap: .55rem; flex: 1 1 auto; min-width: 0; }
.acct-card__ico { font-size: var(--font-4xl); }
.acct-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .6rem; padding-top: .6rem; margin-top: .5rem; }
.acct-form__row { display: flex; flex-direction: column; gap: .15rem; flex: 1 1 220px; }
.acct-form__actions { flex: 0 0 auto; }



/* === 9. FORMS ============================================== */

.form-label-xs {
    font-family: var(--font-display);
    font-size: var(--font-xs);
    font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, 0.08em);
    color: var(--fg3);
    margin-bottom: 2px;
    display: block;
}
.form-control-xs { height: 26px; font-size: var(--font-sm); padding: 0.15rem 0.4rem; }
.form-select-xs  { height: 26px; font-size: var(--font-sm); padding: 0.1rem 1.5rem 0.1rem 0.4rem; }
.form-row-compact {
    display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.65rem 0.55rem;
}

/* === GRUPURI DE CÂMPURI într-un rând dens (B144) ============================================
   Rândul de configurare al unei categorii avea 14 câmpuri într-un singur `.form-row-compact`:
   identitatea, calificarea, battle-ul și afișajul amestecate, iar `flex-wrap` rupea rândul prin
   mijlocul unui cluster. Aici câmpurile intră în grupuri cu nume, iar ruptura cade ÎNTRE ele.

   Separatorul e GAP-ul, nu o linie: o hairline verticală ar rămâne orfană când grupul cade pe
   rândul lui pe telefon și ar cere un `@media` ca s-o ștergem (UI_RULES: „un nivel = un mecanism",
   breakpoint dur = ultim resort). Capul de grup e mecanismul.

   Grupul se STRÂNGE înainte să cadă: câmpurile lui se înfășoară în interior (`.fgroup__b` are
   wrap), fiindcă `min-width: 0` îl lasă să coboare sub min-content. Fără el, minimul automat al
   unui flex item e min-content și nimic nu se micșorează. */
/* RÂNDURI LOGICE, nu o grilă de celule (rescris 2026-09-04, it. 4). Am încercat, în ordine: șir
   plat, grupuri-cutie (câmpuri decalate între grupuri — respins), grilă uniformă de celule (toate
   câmpurile ajungeau la aceeași lățime, deci „prea lungi" — respins). Ce a rămas e cel mai simplu
   lucru care le face pe toate: TREI `.form-row-compact` una sub alta, fiecare cu înțelesul ei —
   identitate · calificare+runde · reguli de battle — plus banda de premii.

   Fiecare câmp e cât conținutul lui (eticheta sau controlul, care e mai lat), iar la îngustare
   câmpurile pică unul câte unul, în interiorul rândului lor. Gruparea se citește din VECINĂTATE și
   din ruptura dintre rânduri, fără nicio cutie și fără niciun cap de secțiune.

   `.frow-sep` = hairline subțire între rânduri: separă fără să adauge înălțime cât un cap de grup. */
.frow-sep { border-top: 1px solid var(--border-hair); padding-top: 0.45rem; margin-top: 0.15rem; }
/* Stepperele de fază vin dintr-un `<template>` clonat într-un container: `display: contents` le
   scoate pe fiecare direct în rând, ca să curgă lângă restul câmpurilor. */
.phases-cell { display: contents; }
/* Nota „se recalculează la salvare" de lângă rundele de fază (/edit): fazele se regenerează abia la
   salvare, fiindcă inputurile sunt cheiate pe id-urile config-urilor EXISTENTE. */
.frow-note { align-self: center; font-size: var(--font-xs); color: var(--warning); }
/* Glosa unui RÂND întreg (nu a unui câmp): `flex: 1 1 100%` o rupe pe linia ei în `.form-row-compact`,
   deci nu îngustează niciun câmp. Scrisă permanent — spre deosebire de ⓘ, care explică UN câmp — de
   aceea câmpul care ar fi purtat același text nu mai primește ⓘ (ar deschide ce scrie deasupra lui). */
.frow-gloss { flex: 1 1 100%; margin: 0; font-size: var(--font-xs); line-height: 1.3; color: var(--text-muted); }

/* ⓘ stă ABSOLUT în colțul câmpului, pe linia etichetei: în flux ar fi ocupat o celulă de grilă
   întreagă doar pentru o iconiță. La ÎNCEPUTUL etichetei, nu la sfârșit (it. 5): la dreapta, cutia de
   24px călca peste text (eticheta rezerva 1.25rem = 20px) și stătea după cuvânt, adică se citea după
   ce deja aveai nevoie de ea — raportat de owner: „semnele ⓘ stau peste scris și în față".

   Cutia de ATINGERE rămâne `--tap-aa` (24px, podeaua WCAG 2.2 SC 2.5.8 AA — vezi UI_RULES „Ținte
   touch": sub 24px nu se coboară). Se micșorează doar GLIFA, iar cutia se centrează pe linia
   etichetei (`top: -5px`), ca surplusul ei de înălțime să cadă în gap-ul de deasupra rândului, nu peste
   control. `flex-start` lipește glifa de marginea stângă — centrată într-o cutie de 24px ar fi lăsat o
   gaură între ea și cuvânt. */
.fhint {
    position: absolute; top: -5px; left: 0; z-index: 2;
    display: inline-flex; align-items: center; justify-content: flex-start;
    min-width: var(--tap-aa); height: var(--tap-aa); padding: 0;
    border: 0; background: none; color: var(--text-muted); font-size: var(--font-xs);
}
.fhint:hover, .fhint:focus-visible { color: var(--primary); }
/* Rezerva de pe etichetă stă LÂNGĂ `.form-field > label`, mai jos în fișier — nu aici. Pusă aici,
   era ștearsă tăcut de shorthandul `padding: 0` al regulii de bază: aceeași specificitate (0,1,1),
   același layer, deci decidea ordinea din fișier. Vezi comentariul de la `.form-field--hinted`. */
/* Clasa, nu atributul `hidden`: reboot-ul Bootstrap are `[hidden]{display:none!important}`, deci o
   cutie ascunsă cu `hidden` n-ar mai putea fi deschisă din CSS. */
/* Cutia PLUTEȘTE peste conținut (absolut), nu în flux: în flux, deschiderea ei împingea rândul
   următor și lăsa o gaură mare la închidere — raportat de owner („când apare lasă spațiu mare pe
   următorul rând"). Un strat care se deschide și se închide nu are voie să miște layoutul. */
.fhint__box {
    display: none;
    position: absolute; top: 100%; left: 0; z-index: 20;
    width: max-content; max-width: min(20rem, 70vw);
    margin-top: 2px; padding: 0.35rem 0.5rem;
    background: var(--sm-card-surface); border: 1px solid var(--sm-card-edge); border-radius: var(--radius-sm);
    box-shadow: var(--sm-elev-float);
    font-size: var(--font-xs); line-height: 1.35; color: var(--text);
}
.fhint__box.is-open { display: block; }
.judge-inline-row,
.staff-inline-row { display: flex; gap: 0.4rem 0.5rem; align-items: flex-end; flex-wrap: wrap; }

/* Grila juraților (edit + builder): 4 coloane Nume · Rol/Sistem · Țară · × cu headerele O DATĂ în
   cap. Coloanele trebuie să se alinieze ÎNTRE rânduri → UN grid pe CONTAINER, iar rândurile
   (.judge-inline-row) devin display:contents ca celulele tuturor să curgă în același grid
   (UI_RULES: ce se aliniază între frați nu poate sta în interiorul lor). Fără wrap până la 320px:
   coloanele fixe ≈ 168px, numele ia restul și taie cu ellipsis. */
.jgrid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(5.5rem, 8.5rem) 2.4rem 1.5rem; gap: 4px 6px; align-items: center; }
.jgrid > .judge-inline-row,
.jgrid > .staff-inline-row,
.jgrid > .jgrid-rows,
.jgrid > .jgrid-rows > .judge-inline-row,
.jgrid > .jgrid-rows > .staff-inline-row { display: contents; }
.jgrid__h { font-size: var(--font-2xs); font-weight: 700; text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em); color: var(--fg3); }
.jgrid__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-sm); }
.jgrid__span { grid-column: 1 / -1; }   /* „Fără jurați." peste toate coloanele */
/* lățimile utilitare cedează coloanelor grilei */
.jgrid .input-w-system { min-width: 0 !important; width: 100%; }
.jgrid .input-w-role { width: 100% !important; }
.jgrid .input-w-name { max-width: none !important; }
/* rolul static al staff-ului existent — aceeași pastilă ca la juratul blocat, dar în afara .judge-chip */
.jgrid .chip-system { font-size: var(--font-xs); color: var(--fg3); background: var(--paper-2); padding: 0 3px; border-radius: var(--radius-sm); justify-self: start; }
.jgrid .ac-wrap { width: auto; min-width: 0; }
.jgrid .form-field, .jgrid .jr-sys { min-width: 0; }
/* selectul doar-steag e mult mai îngust în grilă (globalul 3.4rem rămâne pt. formularele de înscriere) */
.jgrid .cs--flag { width: 2.4rem !important; padding-left: .15rem !important; padding-right: .15rem !important; }
/* HEAD = doar subliniat, nu chip (feedback 2026-08-17) */
.jhead { text-decoration: underline; text-underline-offset: 2px; font-weight: var(--type-label-weight, 900); font-size: var(--font-2xs); letter-spacing: var(--type-label-tracking, .04em); }

/* === TOP-ALIGNED label — SSOT label câmpuri creare/editare concurs ==========================
   Cercetare UX: label deasupra inputului = cel mai scanabil pt formulare dense (vezi plan/Surse).
   Markup: control ÎNTÂI, <label> DUPĂ → `flex-direction: column-reverse` afișează label-ul DEASUPRA
   fără reordonare DOM (dropdown autocomplete rămâne absolut → neafectat). Font Archivo 600, text
   ÎNTREG (fără trunchiere). Padding sus/jos pe control = lizibilitate (anti-aglomerare). */
.form-field { display: flex; flex-direction: column-reverse; gap: 3px; min-width: 0; align-items: flex-start; position: relative; }
/* `margin: 0` DA, `padding: 0` NU. Shorthandul era defensiv (nici UA, nici reboot-ul Bootstrap nu
   dau padding unui `<label>`) și ștergea tăcut rezerva pentru ⓘ: `.form-field--hinted > label` are
   EXACT aceeași specificitate (0,1,1) și același layer, deci câștiga cine era mai jos în fișier.
   A costat două iterații — o dată ca `padding-right` (B144), o dată ca `padding-left` (B146), în
   ambele cazuri fără niciun efect. Emitentul șters, nu specificitatea ridicată. */
.form-field > label {
    margin: 0;
    font-family: var(--font-sans); font-weight: 600; font-size: var(--font-sm);
    color: var(--text-secondary); line-height: 1.15; white-space: nowrap;
}
/* Eticheta începe după iconiță, ca să nu treacă pe sub ea (glifă ~11px + aer). Stă IMEDIAT sub
   regula de bază: relația dintre ele e fragilă și trebuie să se vadă la citit. */
.form-field--hinted > label { padding-left: 1.05rem; }
.form-field > .form-control,
.form-field > .form-select,
.form-field > textarea.form-control {
    width: 100%; height: auto; min-height: 1.95rem;
    padding: 0.32rem 0.55rem; font-size: var(--font-md); line-height: 1.3;
}
.form-field > textarea.form-control { min-height: 3rem; }
/* Câmp FIXAT (disabled, ex. structuralele la edit după creare) = clar ne-editabil, în temă:
   chenar dashed stins + text muted + lacăt pe label (pattern consistent cu .config-pill--lock). */
.form-field > .form-control:disabled,
.form-field > .form-select:disabled {
    background-color: transparent;
    border-style: dashed; 
    color: var(--text-muted); cursor: not-allowed;
    opacity: 1;  /* anulează estomparea Bootstrap pe select — dashed+muted e deja semnalul */
}
/* Selectul dezactivat = același aspect ca inputul dezactivat de dinainte (ADR 0049: blocat =
   ACELAȘI control, `disabled`, fără `name`) — fără caret, că nu e nimic de deschis. */
.form-field > .form-select:disabled { background-image: none; }
.form-field:has(> :disabled) > label { color: var(--text-faint); }
.form-field:has(> :disabled) > label::after {
    font-family: 'bootstrap-icons'; content: "\f47a";  /* bi-lock-fill */
    font-size: .85em; margin-left: .25rem; opacity: .7;
}
/* stepper „Runde/fază": label (cod fază) deasupra, input îngust centrat */
/* Baze de creștere pentru câmpurile dintr-un `.form-row-compact`: se împachetează progresiv, unul
   câte unul, în loc să se prăbușească toate la un breakpoint. `8.5rem` = minimul sub care un
   `<input type="date">` nativ își taie propriul text (`zz.ll.aaaa`). */
.fld-grow  { flex: 1 1 9rem; }
.fld-grow2 { flex: 2 1 10rem; }
.fld-date  { flex: 1 1 8.5rem; }
/* Grupul celor două date (Data + Până la) = sub-flex care pică ÎMPREUNĂ sub locație când rândul
   se îngustează (col-lg-4 la creare) — intervalul e o singură informație, „Până la" nu rămâne
   orfan pe rândul lui. Foarte îngust → datele se rup și între ele (wrap interior, progresiv). */
.fld-dates { display: flex; flex-wrap: wrap; gap: 0.65rem 0.55rem; flex: 2 1 17.5rem; min-width: 0; }
.form-field--num { width: 3.4rem; }
.form-field--num > .form-control { width: 100%; text-align: center; }
/* Câmpurile de TIMP („Rundă (s)" / „Smoke (min)"): etichetele-s mai late decât cutia de 3.4rem a
   stepper-elor de fază și se revărsau peste câmpul vecin (labelul e nowrap) — variantă cu loc. */
.form-field--time { width: 5.2rem; }

/* Premiul categoriei (B8 revizuit; 2026-08-10): DOAR locul 1, DOAR pe liniile de listă din
   /concursuri — 🏆 (auriul medaliilor) + textul liber. */
/* Mono: e o CIFRĂ (suma premiului), deci rolul de numerale — și era una din cele două insigne rămase
   pe familia sans în rândul de categorie. */
.prize-line { display: inline-flex; align-items: center; gap: .25rem; font-family: var(--font-mono); font-size: var(--font-xs); font-weight: 700; letter-spacing: var(--type-number-tracking, .04em); color: var(--fg2); }
.prize-line .bi-trophy-fill { color: var(--rank-gold-ink); }

/* Anvergura concursului — UN câmp cu DOUĂ coloane sub aceeași etichetă („Intră / Nu intră în
   clasament"): apartenența la ELO se vede din structură, nu din text în <option>. Pill = radio
   ascuns accesibil (:checked / :focus-visible pe input) + label derivat din rețeta .filter-pill.
   FĂRĂ chenare per zonă (feedback 2026-08-16): două rame = doi „câmpi" în capul userului — voia
   să completeze amândouă. Coloanele sunt despărțite de O hairline; selecția = pastila inversată
   + BIFĂ (se citește fără percepția culorii); zona de clasament se distinge prin header
   (trofeu + primary). Fostul :has(:checked) pe zonă = semnal mort oricum (--paper-2 și
   --surface-2 se remapează la același --kf-surface-bright). */
.tier-pick { border: 0; padding: 0; margin: 0; min-width: 0; }
.tier-pick__legend {
    float: none; width: auto; margin: 0 0 3px; padding: 0;
    font-family: var(--font-sans); font-weight: 600; font-size: var(--font-sm);
    color: var(--text-secondary); line-height: 1.15;
}
.tier-pick__zones { display: grid; grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr)); gap: .5rem .9rem; }
.tier-pick__zone { min-width: 0; }
.tier-pick__zone--rank { border-left: 1px solid var(--border-hair); padding-left: .9rem; }
.tier-pick__head {
    font-size: var(--font-2xs); font-weight: 700; text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em);
    color: var(--fg3); margin-bottom: .35rem;
}
.tier-pick__zone--rank .tier-pick__head { color: var(--primary); }
.tier-pick__opts { display: flex; flex-wrap: wrap; gap: .35rem; }
.tier-pick__opt {
    display: inline-flex; align-items: center; gap: .3rem;
    font-size: var(--font-sm); font-weight: 600; line-height: 1.3;
    padding: .3rem .6rem; border-radius: var(--radius-sm);
    border: 1.5px solid var(--sm-line); background: var(--sm-s1); color: var(--fg2);
    cursor: pointer; margin: 0;
}
.tier-pick__opt .tier-pick__check { display: none; }
.tier-pick__opt:hover {  color: var(--volt-ink);  }
.tier-pick__input:checked + .tier-pick__opt { background: var(--sm-btn-fill); color: var(--sm-on-btn-fill); border-color: var(--sm-btn-fill); }
.tier-pick__input:checked + .tier-pick__opt .tier-pick__check { display: inline; }
.tier-pick__input:focus-visible + .tier-pick__opt { outline: 2px solid var(--primary); outline-offset: 2px; }
.tier-pick__input:disabled + .tier-pick__opt { opacity: .55; cursor: not-allowed; }
/* în rândurile jurat/staff: câmpul = coloană proprie (label sus, control jos) */
.judge-inline-row .form-field,
.staff-inline-row .form-field { min-width: 0; }

/* Panou formular pe temă (kinetic sharp light×brutalist) — înlocuiește card+bg-dark Bootstrap
   pe creare/editare concurs. Cap deschis cu hairline tare + label display uppercase.
   FĂRĂ overflow:hidden: reteza dropdown-ul de autocomplete al rândurilor de jurat (aceeași
   poveste ca F6/.ccard, 2026-08-06), iar sub remap-ul kf radius-ul e 0 — clipul nu rotunjea
   nimic. Dacă radius-ul revine vreodată, clipul se mută pe __head, nu înapoi aici. */
.form-panel {
    border: 2px solid var(--border-strong);
    border-radius: var(--radius-lg);
    background: var(--sm-card-surface);
    display: flex;
    flex-direction: column;
border-color: var(--sm-card-edge); border-width: 1px; }
.form-panel__head {
    /* Antetul e PICTAT, iar panoul are colt rotunjit: fara asta, banda patrata acopera coltul ramei (semnalat de
       owner la V-ul B159 L6b: „colturile sunt acoperite, nu se vad"). `inherit` ia chiar raza panoului, deci merge
       in toate lumile: 0 pe kf (unde panoul e patrat), raza cardului pe skinul modern. `overflow: hidden` pe panou
       NU e o optiune: `.fhint__box` e pozitionat absolut si iese din panou. */
    border-start-start-radius: inherit; border-start-end-radius: inherit;
    background: var(--sm-strip-surface);
    color: var(--text);
    border-bottom: 2px solid var(--border-strong);
    font-family: var(--font-display);
    font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, 0.04em);
    font-size: var(--font-sm);
    padding: 0.5rem 0.75rem;
    display: flex;
    align-items: center;
border-bottom-color: var(--sm-hair); }
.form-panel__head--action { justify-content: space-between; gap: 0.5rem; }
.form-panel__body { padding: 0.75rem; flex: 1 1 auto; }

/* Tabel PhaseRoundConfig */


/* Formular QualificationConfig */



/* === 12. PUBLIC — /concursuri ============================== */

/* Pagina LIVE = proiector fullscreen, fără chrome. Clasă pe <body> (NU body{} inline — evită
   bug-ul de fundal scurs prin reconcilierea <head> la navigare Turbo, vezi UI_RULES). */
.live-projector { background: var(--paper); min-height: 100vh; }
/* Proiector = lățime MAXIMĂ (datele trebuie citite din sală), conținut centrat ORIZONTAL și lipit de
   marginea de SUS. Vertical NU se mai centrează (2026-08-14): pagina duce mult conținut sub antet
   (calificări, bracket, smoke), iar centrarea împingea capul paginii în jos și scotea rândurile de
   jos din ecran — exact informația pentru care se uită sala la proiector.
   Bate plafonul `--container-max` (0-2-0 vs 0-1-0), nu prin ordinea fișierelor — motivarea „vine mai
   târziu în fișier" era greșită: sunt fișiere diferite. Zoom-ul A−/A+ = proprietatea CSS
   `zoom` (re-layout real, ca Ctrl+/−) prin --proj-zoom setat pe <html> (morph-ul Turbo atinge
   doar body → variabila supraviețuiește auto-refresh-ului). */
.live-projector .app-wrap {
    max-width: none; padding: 1rem 1.5rem;
    min-height: calc(100dvh - 1rem);   /* -1rem = mt-3 (utilitar !important, nu-l putem tăia) */
    display: flex; flex-direction: column;
}
/* Zoom-ul se aplică pe CONȚINUT (rândul de sus), NU pe wrapperul full-height: pe wrapper,
   min-height-ul de 100dvh se scala și el (130dvh la 1.3×) → spații mari sus/jos. Pe rând:
   wrapperul rămâne exact viewportul, iar conținutul mărit curge în jos din marginea de sus. */
.live-projector .app-wrap > .row { margin-block: 0; zoom: var(--proj-zoom, 1); }
/* Antet + tab-uri centrate orizontal */
/* Proiector: din antetul mare de concurs rămâne DOAR rândul de identitate (stare + nume) — sala
   trebuie să știe la ce concurs se uită și dacă e LIVE, dar afișul, locația/data, programul și
   butoanele ownerului sunt chrome de birou și fură din înălțime. Ascunderea e STRUCTURALĂ (copiii
   antetului), nu o listă de clase per element — un element nou în antet rămâne ascuns din start.
   ⚠️ Butoanele ownerului stau ACUM în rândul păstrat, deci nu mai sunt ascunse de structură: le
   ascunde `.proj-hide` de pe `.res-hero__acts` (regula UI_RULES „ce trebuie să poată fi ascuns de un
   mod primește clasă semantică" — „Editează/Șterge" sunt <a>, nu <form>). */
/* ── ANTETUL DE SALĂ (iter. 2o) ──
   Antetul normal ȘI banda de categorii DISPAR pe proiector; le ia locul `.proj-head`, o singură
   linie cu concursul, categoria de pe scenă și faza. Motivul e de citit din fundul sălii: antetul
   căra afiș, meta, butoane de birou; banda arăta cinci categorii dintre care patru nu sunt pe scenă
   acum. Sala are nevoie de trei lucruri, nu de o navigație.
   ⚠️ CONSECINȚĂ pe ADR 0019 §6: „ecranul se comandă din click-uri" nu mai e adevărat pe proiector —
   fixarea manuală pe o categorie se făcea tocmai prin click pe bandă. Urmărirea automată
   (`judgingActiveAt`) rămâne mecanismul principal, iar fixarea rămâne posibilă din ADRESĂ (URL-ul
   canonic al categoriei + `?proiector=1`), adică exact ce făcea butonul „Fixează". Comutatorul
   „Urmărește" din `.proj-zoom` rămâne, fiindcă un ecran deschis pe un URL fixat trebuie să se poată
   întoarce la urmărire. Decizie a userului, consemnată aici ca să nu pară o scăpare. */
.live-projector .res-hero,
.live-projector .cat-nav { display: none; }
/* Antetul de sală: randat doar cu `?proiector=1`, dar ascuns și din CSS ca regula de mod să fie
   completă într-un singur loc (dacă mâine se randează necondiționat, nu apare pe pagina publică). */
.proj-head { display: none; }
/* FĂRĂ linie despărțitoare, nici între segmente, nici sub rând (cererea userului): separă MĂRIMEA
   și spațiul. O riglă în plus pe un ecran care are deja rigla tab-urilor dedesubt e al doilea
   contur concentric, nu un ajutor de citire. */
/* AȘEZAREA: concursul la STÂNGA, categoria la MIJLOC, faza la DREAPTA — trei poziții, trei roluri,
   fiecare acolo unde o cauți. Se face din flex, nu din grid cu coloane fixe: laturile primesc
   `flex: 1 1 0` (spațiu egal), deci categoria cade la mijloc oricât de lungi ar fi numele, iar faza
   se lipește de marginea dreaptă prin `text-align`. Rămâne `flex-wrap`, deci pe ecran îngust cade
   pe rânduri în loc să se strivească.
   FĂRĂ linie despărțitoare, nici între segmente, nici sub rând (cererea userului).

   O SINGURĂ MĂRIME pentru toate trei (`--proj-head-fs`). Prima livrare le dăduse trei trepte
   diferite — concursul mic, categoria uriașă — și userul a respins-o pe bună dreptate: pe un rând
   citit de la distanță, mărimile inegale se citesc ca trei ecrane lipite, nu ca o propoziție.
   Diferența de ROL o poartă FAMILIA (mono vs display), NUANȚA și POZIȚIA, nu scara și nu o umplere.
   Un singur `clamp` = un singur loc de calibrat dacă mai trebuie urcat/coborât. */
.live-projector .proj-head {
    --proj-head-fs: clamp(var(--font-xl), 2.3vw, var(--font-6xl));
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .35rem clamp(.75rem, 2.5vw, 2.5rem);
    min-width: 0;
}
/* Concursul: mono, ca să se distingă de categorie prin FAMILIE, nu prin mărime. */
.proj-head__contest {
    flex: 1 1 0; min-width: 0;
    font-family: var(--font-mono); font-weight: 700;
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em);
    font-size: var(--proj-head-fs); color: var(--text);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Categoria = subiectul ecranului. Rețeta e cea a lui `.cat-strip__l1` (scoreboard.css), NU una
   nouă: numele stilului și formatul la ACEEAȘI mărime, lipite, formatul doar cu altă nuanță
   (`--text-faint`), gap `.35rem`. Umplerea volt a fost încercată și RESPINSĂ de user — fundalul verde
   trage prea mult atenția pe un ecran de sală, unde accentul trebuie păstrat pentru ce se schimbă
   (starea battle-ului), nu pentru o etichetă care stă acolo toată categoria.
   Rândul se citește deci CONTINUU: aceeași mărime peste tot, rolurile din familie și poziție. */
.proj-head__cat {
    flex: 0 1 auto; min-width: 0;
    display: inline-flex; align-items: baseline; flex-wrap: wrap; gap: 0 .35rem;
    color: var(--text);
    font-family: var(--font-display); font-weight: 900;
    text-transform: uppercase; letter-spacing: .01em;
    font-size: var(--proj-head-fs); line-height: 1.15;
}
/* Formatul (2vs2) e calificativ, nu nume: o treaptă mai jos, dar pe ACEEAȘI cerneală — pe umplerea
   volt, `--text-secondary` ar fi ieșit dintr-o altă pereche de contrast. */
.proj-head__fmt { color: var(--text-faint); }
/* Discriminatorii (U18, FEMALE) vin din partialul comun — aceeași cerneală ca restul pastilei. */
.live-projector .proj-head__cat .cat-disc { font-size: 1em; letter-spacing: .04em; color: var(--text-faint); }
/* Faza, la marginea DREAPTĂ: display 900, aceeași mărime, fără fill — perechea „ce categorie /
   unde am ajuns" se citește din poziție și familie, nu din a doua culoare. */
.proj-head__phase {
    flex: 1 1 0; text-align: right;
    font-family: var(--font-display); font-weight: 900;
    text-transform: uppercase; letter-spacing: .03em;
    font-size: var(--proj-head-fs); line-height: 1.15; color: var(--text);
}
.live-projector .cat-block { border-inline-start: 0; padding-inline-start: 0; }
.live-projector .res-cat-head { width: fit-content; max-width: 100%; margin-inline: auto; }
/* Rândul „Înscriși" (tab calificare) — centrat ca restul antetului. */
.live-projector .cat-entries-row { justify-content: center; }
/* Mod prezentare = fără controale de management pe ecranul public (ROUTING_GUIDELINES §2):
   ascunde formularele de acțiune ale ownerului — lifecycle din antet (Deschide/Pornește/
   Rezolvă/Bracket), Start/Finalizează din panoul battle-ului curent, ștergerea/adăugarea
   de înscriși — și notele de lucru ale organizatorului (.owner-note: panel incomplet,
   departajare). Butonul „Proiector" (button simplu, nu form) rămâne = ieșirea din mod.
   `.proj-hide` = aceeași regulă pentru elementele care NU sunt formulare (ex. linkul „Ecran de
   sală": pe proiector ești deja acolo). */
.live-projector .action-group form,
.live-projector .tab-actions,
.live-projector .e-chip-del__form,
.live-projector .owner-note,
.live-projector .proj-hide,
.live-projector [data-bs-target^="#f"] { display: none; }
.live-projector .cat-tabs { justify-content: center; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.cat-tabs { border-bottom: 1px solid var(--sm-hair); }
/* ⚠️ TOATE regulile de mai jos stau ÎN media query, în oglindă cu cele de bază din scoreboard.css
   (`@media (min-width: 992px)`). Erau nelayerizate pe lățime și se aplicau și în modul COLOANĂ, unde
   nu însemnau ce par:
     · `max-width: min(70rem, 65vw)` plafona panoul la 65vw pe toată lățimea ecranului → 35% rămâneau
       GOI, exact acolo unde stătea bracketul (raportat: „scoreboardul nu sare full width, rămâne un
       spațiu gol în locul bracketului");
     · `flex-basis` pe axa verticală e ÎNĂLȚIME, deci `flex: 1 0 min(30rem, 40vw)` întindea panoul
       pe verticală degeaba;
     · `justify-content` centrează pe axa PRINCIPALĂ, care în coloană e verticala — nu ajuta cu nimic.
   Sub prag, proiectorul se comportă acum identic cu pagina normală: scoreboard pe toată lățimea,
   bracket stivuit dedesubt (MC-ul își ia „cine urmează" tot din bracket, deci nu se ascunde). */
@media (min-width: 992px) {
    /* Scoreboard + bracket = UNITATE centrată împreună: scoreboard-ul iese din plafonul de 33%
       (lățime fixă confortabilă), bracketul rămâne la lățimea lui naturală (shrink → .bk-scroll preia),
       perechea e centrată cu justify-content. Alinierea verticală reciprocă = align-self: center existent. */
    .live-projector .bk-layout { justify-content: center; }
    /* Aceeași adaptare ca pe ecranul organizatorului, dar cu praguri de proiector: panoul are ca BAZĂ
       exact lățimea fixă de dinainte (deci cu bracket mare nimic nu se schimbă), poate CREȘTE în spațiul
       pe care un bracket mic nu-l cere, dar NU se micșorează.
       Bracketul rămâne `flex: 0 1 auto` — dimensionat de conținut, se strânge, dar NU crește; astfel
       surplusul rămas după plafonul panoului devine margine, iar `justify-content: center` de mai sus
       ține grupul centrat, adică exact aspectul de proiector de acum.
       Plafonul e mai generos decât la organizator (65vw vs 60%): aici cardul se citește din fundul sălii,
       deci un battle mare la un bracket de 4 e chiar scopul, nu un exces. */
    .live-projector .bk-layout__current { flex: 1 0 min(30rem, 40vw); width: auto; max-width: min(70rem, 65vw); }
    .live-projector .bk-layout__bracket { flex: 0 1 auto; }
    /* Pe proiector panoul are o bază fixă generoasă, deci bracketului îi rămâne mai mult decât în
       ecranul organizatorului → scala lui (ADR 0040) trebuie să știe asta, altfel ar desena carduri
       de 60% pe un loc de 70% și ar lăsa o bandă goală lângă finală. */
    .live-projector .bk-layout { --bk-share: .68; }
    /* Presetările ecranului de sală (ADR 0041) ridică plafoanele de proiector de mai sus. TREI clase
       deliberat: components.css se încarcă DUPĂ scoreboard.css (ADR 0023), deci regulile de bază
       `.bk-layout--stacked …` de acolo ar pierde la aceeași specificitate în fața celor de aici. */
    .live-projector .bk-layout--stacked { --bk-share: 1; }
    .live-projector .bk-layout--stacked .bk-layout__current,
    .live-projector .bk-layout--panel-only .bk-layout__current { flex-basis: 100%; max-width: 100%; }
}
/* Tabul Calificare: coloanele dense citesc mai bine plafonate + centrate.
   Ținta era `.tab-pane > .row` (Bootstrap-ul care ținea cele două coloane); din iter. 2o tabul e
   `.qual-tab`, iar regula ar fi rămas MOARTĂ tăcut dacă o lăsam pe selectorul vechi. */
.live-projector .qual-tab { max-width: 1500px; margin-inline: auto; }
/* Butoanele de zoom: există în DOM mereu, vizibile DOAR în mod proiector */
.proj-zoom { display: none; }
/* Comenzile ecranului (B114): un singur buton ⚙ jos-dreapta; panoul cu cele trei grupuri se desface
   DEASUPRA lui la click (`html.proj-open` — clasă pe <html>, pe care morph-ul Turbo nu-l atinge, ca
   `--proj-zoom`). După ~5 s fără mouse/tastă/atingere tot clusterul se ascunde (`html.proj-idle`) și
   reapare la prima mișcare — standardul playerelor video: sala nu vede niciun control, operatorul
   îl are la o mișcare de mouse. */
.live-projector .proj-zoom {
    display: flex; flex-direction: column; align-items: flex-end; gap: .4rem;
    max-width: calc(100vw - 2rem);     /* zece butoane: pe ecran îngust se rup pe rând, nu ies din ecran */
    position: fixed; right: 1rem; bottom: 1rem; z-index: 50;
    transition: opacity .25s ease;
}
.proj-zoom__panel { display: none; }
html.proj-open .proj-zoom__panel {
    display: flex; flex-wrap: wrap; justify-content: flex-end;
    gap: .25rem .6rem;                 /* mic ÎN grup (vezi __grp), mai mare ÎNTRE grupuri */
}
/* Același mecanism ca `.tab-actions__grp`: gap mic în grup, wrapul cade ÎNTRE grupuri (zoom ·
   oglindă+urmărire · presetări de afișare, ADR 0041). */
.proj-zoom__grp { display: flex; gap: .25rem; }
/* Starea „deschis" a ⚙: roata se rotește — semnal fără a doua paletă de culori. */
.proj-zoom__toggle .bi { display: inline-block; transition: transform .25s ease; }
html.proj-open .proj-zoom__toggle .bi { transform: rotate(90deg); }
html.proj-idle .proj-zoom { opacity: 0; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
    .live-projector .proj-zoom, .proj-zoom__toggle .bi { transition: none; }
}
/* Ecran FIXAT manual (nu mai urmărește organizatorul, 2026-08-10): pastila „Urmărește" pulsează —
   starea de pin nu mai e tăcută (incidentul „promite că vine și nu vine": un tap pe banda de
   categorii fixa ecranul fără niciun semnal). Același ritm ca smoke-queue-pulse (.9s). */
@keyframes proj-follow-off-pulse { 50% { box-shadow: 0 0 0 4px var(--warning); } }
.live-projector .proj-zoom__follow--off {
    background: var(--warning-soft); color: var(--on-warning-soft); border-color: var(--warning);
    animation: proj-follow-off-pulse .9s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) { .live-projector .proj-zoom__follow--off { animation: none; } }

/* Badge-uri tip eveniment */


/* (.badge-style-0..7 eliminate — înlocuite de .style-pill cu hue din name|style_hue) */

/* ---- PASTILA DE STIL (mutata din kf-skin.css, B159 L10) -------------------------------------
   Identitatea unui stil de dans: hue din markup (`--style-h`), scara din tokens.css.
   ⚠️ MERGE, nu mutare: fisierul kf avea DOUA reguli, iar cea care se randa era varianta
   `body.kf .style-pill` (mono, 700, muchie de 4px), nu cea de baza. Mutarea regulii de baza ar fi
   schimbat aspectul pe cinci pagini. Aici e rezultatul combinat, cu tokenii kf inlocuiti de
   echivalentii lor globali (`--kf-font-mono` -> `--font-mono`: sub skin amandoi dau aceeasi familie).
   Colturile citesc rolul `chip`, cu valoarea de azi ca fallback. */
.style-pill {
    display: inline-flex; align-items: center; gap: .25rem;
    padding: .2rem .5rem .2rem .42rem;
    border-radius: var(--radius-chip, 999px);
    border: 0; border-left: 4px solid hsl(var(--style-h) var(--style-s) var(--style-l-accent));
    background: hsl(var(--style-h) var(--style-s) var(--style-l-bg));
    color: hsl(var(--style-h) var(--style-s) var(--style-l-fg));
    font-family: var(--font-mono); font-weight: 700; font-size: var(--font-xs);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em);
    line-height: 1.25; white-space: nowrap;
}
/* Linia de titlu a categoriei (L1): stilul + formatul SUNT titlul cardului, deci pastila ia fata de
   titlu si o marime fluida din container. Combinator COPIL: un wrapper viitor in L1 nu trebuie sa
   traga alte pastile inauntru. */
.ccat__l1 > .style-pill {
    font-family: var(--font-display); font-weight: var(--type-label-weight, 900);
    font-size: clamp(var(--font-sm), 3cqi, var(--font-base));
    letter-spacing: var(--type-label-tracking, .02em);
}
/* /arhiva ia DOAR fata de titlu, nu si marimea: latimea utila din `.arch-cats` e ~209px si cardul are
   pana la 6 categorii, deci un pas de marime acolo adauga randuri intregi de cipuri sub poster. */
.arch-cat .style-pill {
    font-family: var(--font-display); font-weight: var(--type-label-weight, 900);
    letter-spacing: var(--type-label-tracking, .02em);
}

/* ── CONTEST GRID — masonry pe coloane (JS: assets/js/masonry-columns.js).
   Card = bandă poster SUS (titlu + meta peste) + categorii dedesubt. Coloanele-s independente
   → expand-ul unei înscrieri crește DOAR coloana cardului (fără salt, fără împins rândul de jos).
   Fallback fără JS = CSS Grid de mai jos.

   PISTELE SE PLAFONEAZĂ ȘI SE CENTREAZĂ (B68), nu se întind. Erau `repeat(2, 1fr)`: cu un singur
   concurs, a doua pistă rămânea în layout și lăsa ~800px goi la dreapta — „golul de pe desktop"
   raportat de primul utilizator. Dar nici întinderea cardului pe toată lățimea nu e răspunsul:
   `.ccard__hero` are înălțime FIXĂ în px (mai jos, în fișierul ăsta), deci raportul ar trece de la 4.1:1 la 8.25:1 și
   posterul s-ar reteza; titlul e --font-5xl fix; clamp-urile din card ating deja plafonul la ~776px.
   Deci: pistă plafonată la --card-col-max + `justify-content: center` → 1 card = 768px centrat cu
   margini simetrice, 2 carduri = exact ca înainte, 3+ = rânduri de câte două.
   `min(100%, 34rem)` = plasa anti-overflow sub lățimea minimă de pistă; ea înlocuiește și fostul
   `@media (max-width: 760px)`, care făcea intrinsec același lucru. ── */
.bcast-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 34rem), var(--card-col-max)));
    justify-content: center;
    align-items: start;
    gap: var(--kf-gutter);   /* venit din kf, B159 L10 F1 */
}
/* JS a construit coloane independente. ⚠ Plafonul și centrarea se REPETĂ aici: pe `display: flex`
   `grid-template-columns` e inert, deci fără ele o singură coloană cu `flex: 1` s-ar întinde tot pe
   toată lățimea și fixul de mai sus ar fi anulat exact în cazul pentru care a fost scris. */
.bcast-grid.is-masonry { display: flex; align-items: flex-start; justify-content: center; }
.bcast-col { flex: 1; min-width: 0; max-width: var(--card-col-max); display: flex; flex-direction: column; gap: var(--kf-gutter); }

/* FĂRĂ overflow:hidden pe card: dropdown-ul de autocomplete al formularului de înscriere
   (.ac-dropdown, absolut, 210px) trebuie să poată ieși din card — clip-ul îl reteza la marginea
   cardului. Ce avea de clipuit clip-ul (colțurile posterului) se face pe .ccard__hero. */
.ccard {
    box-shadow: var(--sm-elev-float);
    /* Venite din kf la B159 L10 (F1). `background` nu e scris aici de doua ori: regula de skin care
       il dadea (`--sm-card-surface`) a coborat tot in regula asta, la F2 — verificat pe element.
       `border` ramane SCURTATURA, iar `border-color` e scris separat DUPA ea: daca cineva sparge
       scurtatura in longhand-uri, muchia cardului dispare. */
    /* `--kf-border` era `1px solid var(--kf-line)`: ramane 1px, culoarea vine din paleta de azi
       (coborata din `@layer skin` la B159 L10, F2 — skinul dadea doar `border-color`). */
    border: 1px solid var(--sm-card-edge);
    border-radius: var(--radius-card, var(--kf-r-sm));
    display: flex; flex-direction: column;
    transition: border-color .1s;
    /* Inseturile laterale ale randurilor (.ccat) sunt fluide cu VW, nu cqi, desi cardul E container:
       masonry face 2 coloane peste 760px, deci un card de desktop la viewport 761-850px are latimea
       unui card de telefon — containerul nu poate distinge „ecran scump" de „desktop ingust". */
    --ccat-mx: clamp(.125rem, 12vw - 2.75rem, 1rem);   /* margine laterala rand in card */
    --ccat-px: clamp(.375rem, 9vw - 1.75rem, 1rem);    /* padding lateral interior rand */
    container-type: inline-size;
background: var(--sm-card-surface); }
/* Cardul cu dropdown-ul DESCHIS urcă peste frați: containment-ul kf (container-type pe .ccard)
   face fiecare card stacking context atomic, pictat în ordinea DOM — fără raise, cardul de
   DEDESUBT din aceeași coloană ar picta peste lista de sugestii. z-index merge fără position:
   cardurile sunt flex items (.bcast-col) / grid items (.bcast-grid). */
.ccard:has(.ac-dropdown.ac-open) { z-index: 2; }

/* HERO — poster bandă sus, full-width, cu titlu + meta PESTE (gradient pt lizibilitate) */
/* `height` bate `aspect-ratio` (raportul se aplica doar cand o dimensiune e `auto`), deci banda are
   190px ficsi — vezi nota de mai sus despre pista plafonata. Raportul ramane ca plasa daca inaltimea
   fixa dispare vreodata. Venite din kf la B159 L10 (F1). */
.ccard__hero {
    aspect-ratio: 16 / 9; border-radius: var(--sm-r-card) var(--sm-r-card) 0 0;
    position: relative; height: 190px; overflow: hidden;
    background: var(--kf-surface-3); cursor: pointer;
}
/* Cu program, suprapunerea mai poarta si linia de timp intre titlu si randul de locatie/data.
   Ea creste in SUS din marginea de jos si e clipuita de `overflow: hidden`, deci la 190px un titlu pe
   doua randuri ar taia programul. Doar cardurile cu program platesc cei ~75px. */
.ccard__hero:has(.tline) { height: 265px; }
/* `.ccard__hero` isi rotunjeste doar colturile de SUS — corect cat timp cardul continua cu randuri
   de categorie dedesubt. Pe home insa hero-ul e SINGURUL copil, deci marginea lui dreapta de jos
   acopera coltul cardului (raportat de owner: „doar in partea de jos"). Pe /arhiva, alta nuanta a
   aceleiasi probleme: `.arch-poster` are raza proprie de 8px fata de 12 ale cardului — `inherit` o
   potriveste din acelasi gest. */
.ccard > :last-child { border-end-start-radius: inherit; border-end-end-radius: inherit; }
/* `filter: grayscale(1)` si revenirea la hover n-au coborat din kf: erau MOARTE sub skin — skinul
   randa `filter: none` chiar si in timpul hover-ului, verificat pe element real (B159 L10 F1). */
.ccard__hero img { width: 100%; height: 100%; object-fit: cover; display: block; filter: none; }
/* Scrimul (gradientul peste afiș), poziția și tipografia hero-ului au coborât în fișierul ăsta la
   B159 L10 (F1): „plasa pentru o pagină fără skin” care stătea aici n-avea consumator — era umbrită
   de layer, ștearsă în B159 L0b 0.7 (gate `css-layer-shadow`). Aici rămâne doar ce
   kf nu redeclară. */
/* SCRIM plin ACOLO UNDE E TEXTUL. Varianta veche mergea de la .82 jos la TRANSPARENT sus — iar titlul
   e PRIMUL copil, deci cadea exact in zona cea mai transparenta: pe un afis incarcat numele concursului
   se pierdea in imagine (raport owner 2026-08-16). Acum: plin pana peste titlu, stins in ultimii ~12%,
   care cad in `padding-top` — adica DEASUPRA numelui. Pinnat pe ambele teme, fiindca textul e pinnat alb. */
.ccard__hero-ov  {
    display: flex; flex-direction: column; justify-content: flex-end; gap: 2px;
    position: absolute; inset: auto 0 0 0;
    padding: var(--kf-5) var(--kf-4) var(--kf-3);
    background: linear-gradient(to top,
        color-mix(in srgb, var(--c-ink-900) 88%, transparent) 0%,
        color-mix(in srgb, var(--c-ink-900) 88%, transparent) 72%,
        color-mix(in srgb, var(--c-ink-900) 55%, transparent) 88%,
        transparent 100%);
}
.ccard__title {
    font-family: var(--kf-font-display); font-weight: var(--type-title-weight, 900); font-size: var(--font-5xl);
    line-height: .95; color: var(--c-white);
    letter-spacing: var(--type-title-tracking, .01em); text-transform: var(--type-title-case, uppercase);
}
/* Titlul-link spre pagina canonică a concursului — moștenește look-ul titlului, underline la hover */
.ccard__title-link { color: inherit; text-decoration: none; }
.ccard__title-link:hover { text-decoration: underline; }
.ccard__herometa {
    align-items: center;
    display: flex; flex-wrap: wrap; gap: .25rem 1rem; margin-top: .35rem;
    font-family: var(--kf-font-mono); font-size: var(--font-md); color: rgba(255,255,255,.82);
}
.ccard__herometa i { opacity: .8; }
.ccard__hero-badges {
    justify-content: flex-end; max-width: 75%;
    position: absolute; top: var(--kf-3); right: var(--kf-3);
    display: flex; flex-wrap: wrap; gap: .35rem; z-index: 2;
}
.cbc-days        { white-space: nowrap; }
/* `top`/`left` (kf) si `bottom`/`right` (regula de baza) sunt toate patru setate, ca si pana acum:
   cu latime si inaltime fixe, cutia e supra-constransa si castiga top/left. `background` si `color`
   n-au venit din kf: le rescrie skinul (verificat pe element). */
.ccard__zoom {
    bottom: 6px; right: 6px; font-size: var(--font-2xs); padding: 1px 5px;
    position: absolute; top: var(--kf-3); left: var(--kf-3); z-index: 2;
    width: 32px; height: 32px; border-radius: var(--kf-r-sm);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; transition: opacity .18s;
box-shadow: var(--sm-elev-float); color: var(--sm-text-hi); background: var(--sm-s1); }
.ccard__hero:hover .ccard__zoom { opacity: 1; }

/* ── Notă de organizator care poartă o ACȚIUNE (bannerul „loc liber", `.owner-note--act`) ──
   Butonul stătea INLINE în paragraf, cu `ms-1`: nu se alinia cu textul pe nicio lățime, iar la
   îngustare fraza îl împingea oriunde (raportat de owner). Aici grupurile stau unul-lângă-altul,
   aliniate la stânga, cu gap FLUID, și pică pe rând UNUL CÂTE UNUL — fără `@media` dur care le
   prăbușește pe toate deodată (UI_RULES „Aranjament FLUID").
   `--full` = rândul care începe MEREU pe linie nouă (lista de retrași), în locul unui `<br>`. */
.owner-note--act {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(.2rem, 1.1vw, .55rem);
}
.owner-note__full { flex: 1 0 100%; }

/* ══ PROGRAM — LINIE DE TIMP (templates/contest/_schedule_timeline.html.twig) ══════════════════
   Un „stop" = oră / punct pe linie / etichetă / pastilă de stil (opțională). Spațierea e EGALĂ, nu
   proporțională cu ora: la ore apropiate pozițiile proporționale suprapun etichetele, iar o pauză de
   patru ore ar lăsa jumătate de bandă goală.

   ALINIEREA ÎNTRE STOPURI o ține gridul, nu min-height-uri ghicite: `.tline__stops` declară TREI
   rânduri (sus / punct / jos), iar fiecare `.tline__stop` le moștenește prin `subgrid`. De aici vine
   și alternanța: `--flip` doar schimbă între ele rândul orei și rândul textului, deci linia de puncte
   rămâne la aceeași înălțime la toate stopurile, indiferent pe ce parte le cade conținutul.

   PODEAUA COLOANEI e mai joasă decât lățimea de citire a etichetei, și asta NU e o scăpare: pe un rând,
   vecinul orizontal al unei etichete e MEREU o oră (alternanța `--flip` e globală peste zile, deci pe
   rândul de sus se succed oră / text / oră / text). Ora are lățime FIXĂ și mică — „10:00" în Space Mono
   la `--font-xs` = 5 × .6em + padding ≈ 2.4rem — deci lângă ea rămâne un culoar liber pe care nimeni
   nu-l folosește. Eticheta îl împrumută (`--tline-bleed`, revărsare simetrică peste vecini), și de aici
   coloana poate coborî de la 5.5rem la 4rem fără ca textul să se apropie de oră: la W=4rem eticheta are
   4.8rem de citit, cu `--tline-text-gap` de respirație până la cutia orei. Relația e derivată, nu
   ghicită — muți `--tline-stop-min` și revărsarea se recalculează singură.
   ⚠️ Ce se sprijină pe alternanță: dacă cineva scoate `--flip`, două etichete ajung vecine pe același
   rând și revărsarea le suprapune. Alternanța e portantă, nu decor.

   ⚠️ DIMENSIUNILE NU sunt `cqi` aici, deși regula generală o cere — și motivul e o constrângere de
   spec, nu comoditate. Ce contează pentru înghesuială e lățimea unui STOP, nu a benzii; dar ca stopul
   să fie container de interogare i-ar trebui `container-type: inline-size`, iar containment-ul face
   `subgrid` să se calculeze ca `grid` simplu → rândurile nu s-ar mai alinia între stopuri, adică fix
   ce ține componenta în picioare. Iar `cqi` măsurat pe `.tline` e inutil: pe un card lat clamp-ul
   lovește mereu plafonul, oricâte momente ar fi în bandă. Adaptarea e deci STRUCTURALĂ, nu tipografică:
   podea pe coloană (`--tline-stop-min`) + scroll + alternanță sus/jos, cu fonturi fixe din tokeni.

   CULOAREA ZILEI = hue, ca la `.style-pill`: saturația/luminozitatea vin din tokeni theme-aware, hue-ul
   din modificatorul segmentului. Culorile de STATUS (--success/--warning/--danger) NU se împrumută
   aici — în restul aplicației înseamnă terminat/atenție/eroare, iar „ziua a doua" nu e niciuna.

   ⚠️ `scripts/contrast-audit.php` NU poate verifica blocul ăsta: nu evaluează `hsl()` compus din
   variabile, `color-mix()` și nici alpha peste un gradient (limitări scrise în capul scriptului).
   A adăuga perechile în `PAIRS` ca hex-uri ar da fals-pozitive, fiindcă scriptul le-ar evalua pe
   AMBELE teme cu aceeași valoare. Deci contrastul e calculat de mână și scris aici, ca următoarea
   sesiune să nu-l re-derive. Cel mai prost caz per set (praguri: 4.5 text, 3.0 element UI):
     light s62/l26 → 5.44:1 (hue 76 pe card #f4f4f1)   · l30 pica la 4.30:1
     dark  s55/l66 → 5.39:1 (hue 285 pe card #1f211c)
     media s58/l76 → 4.91:1 (hue 285) / linia 45% alb → 3.53:1, ambele peste poster ALB sub
                     gradientul .82, adică fundalul cel mai deschis posibil. ══ */
.tline {
    min-width: 0;
    margin-top: .55rem;
    --tline-stop-min: 4rem;     /* sub asta nu se mai strânge; de aici încolo railul scrollează */
    --tline-time-w: 2.4rem;     /* „10:00" la --font-xs în Space Mono (5 × .6em) + padding-ul orei */
    --tline-text-gap: .4rem;    /* respirația dintre eticheta revărsată și cutia orei vecine */
    /* Cât poate ieși eticheta din coloana ei, în FIECARE parte: jumătatea de culoar liberă de lângă ora
       vecină, minus respirația. `max(0px, …)` ca o podea prea mică să nu producă revărsare negativă. */
    --tline-bleed: max(0px, calc((var(--tline-stop-min) - var(--tline-time-w)) / 2 - var(--tline-text-gap)));
    --tline-dot: 7px;           /* punctul rămâne px: sub ~5px dispare, indiferent de container */
    --tline-dot-row: 1.05rem;   /* înălțimea rândului punctului = distanța dintre oră și etichetă */
    --tline-s: 62%;
    --tline-l: 26%;             /* accent pe fundal DESCHIS; 30% pica hue-ul de brand la 4.30:1 */
    --tline-ink: var(--text);
    --tline-ink-muted: var(--text-muted);
    /* --border-control, nu --border-strong: hairline-ul de card (1.43:1) e invizibil, iar aici linia
       E componenta. Tokenul ăsta există exact pentru „conturul care trebuie să se vadă". */
    --tline-line: var(--border-control);
}
[data-theme="dark"] .tline { --tline-s: 55%; --tline-l: 66%; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .tline { --tline-s: 55%; --tline-l: 66%; }
}
/* Peste poster: gradientul heroului e întunecat FIX, indiferent de temă. Fundal pinned → cerneală
   pinned (UI_RULES) — moștenirea rolurilor ar da text închis pe întuneric la prima temă care le
   inversează. Aceeași logică ca `.ccard__herometa` de mai sus. */
.tline--onmedia {
    margin: .3rem 0 .4rem;
    /* Valorile sunt măsurate pe cazul CEL MAI RĂU (poster alb sub gradientul .82), nu pe unul comod:
       la s62/l68 hue-ul violet al zilei 4 cădea la 3.67:1, iar linia la 38% alb la 2.97:1. */
    --tline-s: 58%;
    --tline-l: 76%;
    --tline-ink: var(--c-white);
    --tline-ink-muted: color-mix(in srgb, var(--c-white) 72%, transparent);
    --tline-line: color-mix(in srgb, var(--c-white) 45%, transparent);
}

/* Mecanica de derulare vine din `.rail` (§5c) — pusă în markup. Aici rămâne doar ce e al liniei
   de timp. Linia de timp NU primește `.rail-fade`: n-are controller, deci n-ar primi niciodată
   `data-ov`, iar o mască inactivă tot ar costa (stacking context + text pe antialiasing grayscale). */
.tline__rail {
    display: flex; align-items: flex-start;
    margin: 0; padding: 0; list-style: none;
    /* Capetele railului n-au vecin peste care să se reverse: fără padding, eticheta primului stop ar
       ieși în stânga originii de scroll (zonă inaccesibilă) și ar fi TĂIATĂ, nu doar ascunsă. */
    padding-inline: var(--tline-bleed);
}

/* FĂRĂ `min-width: 0`: minimul automat al unui flex item e min-content, și AICI e exact ce vrem —
   un segment care poate coborî sub conținutul lui nu se strânge, ci își revarsă chipul de zi peste
   segmentul vecin (raport user: „Ziua 2 intră peste Ziua 1" la ecran îngust). Cu min-content păstrat,
   railul depășește și SCROLLEAZĂ, adică exact mecanismul ales pentru înghesuială. */
.tline__seg {
    flex: 1 1 auto;
    --tline-accent: hsl(var(--tline-h, 76) var(--tline-s) var(--tline-l));
}
.tline__seg--c0 { --tline-h: 76; }    /* hue-ul de brand (lime/olive) */
.tline__seg--c1 { --tline-h: 187; }
.tline__seg--c2 { --tline-h: 22; }
.tline__seg--c3 { --tline-h: 285; }
/* Separatorul dintre zile = PSEUDO-element, nu `border-left`: borderul ar intra în cutia flex și ar
   îngusta exact segmentele marcate față de frații lor (UI_RULES, „linie decorativă pe flex: 1"). */
/* Șanțul dintre zile ține cont de revărsare: etichetele de la marginile a două segmente vecine se
   revarsă UNA SPRE ALTA, iar linia punctată stă exact la mijloc. `bleed + .35rem` de fiecare parte =
   linia rămâne liberă în ambele sensuri; fără termenul `bleed`, textul ar călca peste ea. */
.tline__seg + .tline__seg {
    position: relative;
    margin-left: calc(var(--tline-bleed) + .35rem);
    padding-left: calc(var(--tline-bleed) + .35rem);
}
.tline__seg + .tline__seg::before {
    content: ""; position: absolute; inset-block: 0; left: 0;
    border-left: 1px dashed var(--tline-line); pointer-events: none;
}

/* Chipul zilei stă pe MUCHIA DE SUS a segmentului (pattern fieldset/legend): eticheta care stăpânește
   un grup îl încadrează, nu îl taie prin mijloc. Apare doar când programul chiar are mai multe zile.
   `nowrap` + `white-space: nowrap` sunt PORTANTE, nu cosmetice: ele fac min-content-ul segmentului să
   includă chipul întreg, deci segmentul nu poate ajunge mai îngust decât propria etichetă. */
.tline__day {
    display: flex; align-items: baseline; gap: .3rem; flex-wrap: nowrap; white-space: nowrap;
    font-family: var(--font-display); 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-2xs);
    line-height: 1.3;
    color: var(--tline-accent);
    padding-bottom: .15rem;
}
.tline__daydate {
    font-family: var(--font-mono); font-weight: 400; letter-spacing: var(--type-number-tracking, .02em);
    color: var(--tline-ink-muted);
}

.tline__stops {
    display: grid; grid-auto-flow: column;
    grid-auto-columns: minmax(var(--tline-stop-min), 1fr);
    grid-template-rows: auto var(--tline-dot-row) auto;   /* sus · punct · jos */
    margin: 0; padding: 0; list-style: none;
}
/* ZERO padding orizontal pe stop: `.tline__dot` se întinde pe lățimea stopului, deci orice padding aici
   ar tăia linia la fiecare punct (raport user: „linia e întreruptă la fiecare pas" — erau 2×2.4px).
   Respirația laterală o iau TEXTELE, care n-au nimic de desenat până la marginea coloanei. */
.tline__stop {
    display: grid; grid-row: 1 / -1; grid-template-rows: subgrid;
    justify-items: center; text-align: center;
    scroll-snap-align: start;
    padding: 0;
}
/* ALTERNANȚĂ sus/jos: implicit ora deasupra liniei și textul dedesubt; la fiecare al doilea punct se
   inversează. Nu e decor — la spațiere egală, două etichete lungi vecine se citesc greu una lângă
   alta; puse pe rânduri diferite își recapătă respirația fără să lățim coloana. */
.tline__time { grid-row: 1; align-self: end; }
.tline__dot  { grid-row: 2; }
.tline__text { grid-row: 3; align-self: start; }
.tline__stop--flip .tline__time { grid-row: 3; align-self: start; }
.tline__stop--flip .tline__text { grid-row: 1; align-self: end; }

/* `width`, nu `max-width`: stopul aliniază cu `justify-items: center`, adică lățimea copilului e
   shrink-to-fit FAȚĂ DE CELULĂ — un `max-width` mai mare n-ar crește-o niciodată, doar ar ridica un
   plafon pe care nimeni nu-l atinge. Lățimea explicită o duce peste celulă, iar centrarea o revarsă
   simetric, deci eticheta rămâne centrată pe punctul ei. */
.tline__text {
    display: flex; flex-direction: column; align-items: center; gap: .15rem;
    min-width: 0; padding-inline: .15rem;
    width: calc(100% + var(--tline-bleed) * 2);
}

/* Linia ȘI punctul într-un singur element: așa punctul e garantat pe linie, iar linia se întinde pe
   toată lățimea coloanei — stopurile lipite dau o linie continuă, fără nimic poziționat absolut față
   de un container care scrollează. */
.tline__dot { position: relative; justify-self: stretch; align-self: center; height: var(--tline-dot-row); }
.tline__dot::before {
    content: ""; position: absolute; left: 0; right: 0; top: calc(50% - .5px);
    height: 1px; background: var(--tline-line);
}
.tline__dot::after {
    content: ""; position: absolute; top: 50%; left: 50%;
    width: var(--tline-dot); height: var(--tline-dot);
    transform: translate(-50%, -50%);
    background: var(--tline-accent); border-radius: 50%;
}
/* Capetele se opresc ÎN primul/ultimul punct, nu în gol. */
.tline__seg:first-child .tline__stop:first-child .tline__dot::before { left: 50%; }
.tline__seg:last-child  .tline__stop:last-child  .tline__dot::before { right: 50%; }

.tline__time {
    font-family: var(--font-mono); font-weight: 700;
    font-size: var(--font-xs);
    font-variant-numeric: tabular-nums; letter-spacing: var(--type-number-tracking, .01em);
    line-height: 1.4; white-space: nowrap;
    padding-inline: .15rem;
    color: var(--tline-ink);
}
.tline__label {
    font-family: var(--font-display); font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .02em); line-height: 1.2;
    font-size: var(--font-2xs);
    color: var(--tline-ink-muted);
    max-width: 100%;
    /* Maxim două rânduri: un „Battle-uri finale open style" ar întinde banda peste tot cardul.
       Eticheta întreagă rămâne în program, pe pagina de concurs. */
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
    overflow: hidden; overflow-wrap: anywhere;
}
/* Pastila de stil: aceeași componentă ca pe rândurile de categorie, la scara benzii.
   Formatul (1v1 / 2v2) e ce diferențiază două categorii cu ACELAȘI stil, deci nu are voie să fie el
   cel tăiat: `flex: none` pe format + truncherea pe numele stilului. */
/* `gap` ERA AICI si era INERTA cat timp `.style-pill` statea in `@layer components` (kf-skin), care
   batea `@layer app`: spatiul dintre nume si format venea de la pastila (.25rem = 4px), nu de aici.
   Mutarea pastilei in `@layer app` (B159 L10) ar fi trezit-o pe tacute - masurat, 4px -> 2.88px.
   Declaratia moarta se sterge; nu ne bazam pe ordinea liniilor ca sa decidem cine castiga. */
.tline__cat { max-width: 100%; min-width: 0; }
.tline__catname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tline__catfmt { flex: none; font-weight: 400; }

/* CATEGORIE — bloc pe 3 rânduri + bară „înscrie-te" full-width jos.
   /concursuri tab Deschise: detaliile = link spre pagina concursului (.ccat__stretch acoperă cardul),
   ÎNSCRIEREA se comută DOAR din butonul .ccat__cta. Elementele interactive (CTA, acțiuni owner,
   „Vezi toți") stau DEASUPRA link-ului prin z-index. Celelalte taburi: .ccat e direct un <a> (link). */
/* Bara de acțiuni a ownerului pe card (editează/șterge concursul) — RÂND al cardului, nu butoane
   plutind: aceeași margine interioară ca `.ccat` + același hairline + etichetă `.ccat__lbl`, ca să
   citească la fel cu „Acțiuni" / „Înscriși" de dedesubt. (Înainte: `px-2 pt-2 justify-content-end`,
   care nu se alinia cu nimic din card.) */
.ccard__owner { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .45rem; padding: .45rem .65rem; border-top: 1px solid var(--border-hair); }
.ccat { display: block; padding: .5rem .65rem .6rem; border-top: 1px solid var(--border-hair); cursor: pointer; text-decoration: none; color: inherit; }
.ccat:first-of-type { border-top: none; }
.ccat:hover { background: var(--bg-elevated); }
.ccat--enroll { position: relative; }
.ccat__stretch { position: absolute; inset: 0; z-index: 1; }
.ccat--enroll .ccat__cta,
.ccat--enroll .cat-actions,
.ccat--enroll .chips-more-btn { position: relative; z-index: 2; }
/* Meta categoriei (TOP N · premiu · stare) = RÂND PROPRIU, sub identitate.
   Era un grup `inline-flex` ÎN interiorul lui `.ccat__l1`, ca ruperea de rând să cadă între grupuri
   și nu prin mijlocul lor. Grupul făcea ce trebuia, dar ținea discriminatorii (UNDER 18, Feminin)
   printre metadate — adică singurul lucru care deosebea două categorii surori era și cel mai mărunt,
   și al patrulea la citire. La un concurs real, concurenți s-au înscris în categoria greșită.
   Din 2026-09 discriminatorii au urcat în `.ccat__l1`, lângă stil și format; aici a rămas informația
   de rangul doi, care își permite un rând al ei. */
.ccat__meta { display: flex; flex-wrap: wrap; align-items: center; margin-top: .15rem; }
.ccat__row { flex-wrap: wrap; }
.ccat__cta { width: 100%; cursor: pointer; }

/* ---- Randul de categorie sub skin: venit din kf-skin.css la B159 L10 (F1) ----------------------
   Rama hairline e purtatoarea distinctiei intre randuri (common region): albul randului pe cardul
   `--kf-surface` da doar 1.10:1 (light) / 1.15:1 (dark) — tenta si spatiul singure nu se vad.
   Separator DECORATIV dublat de tenta+spatiu, nu element UI (WCAG 1.4.11 nu cere 3:1 aici).
   Selectorul ramane DESCENDENT, nu se contopeste cu `.ccat` de mai sus: aceea are alt `padding`
   si un `border-top`, pe care scurtatura `border` de aici le-ar calca. */
.ccard > .ccat,
.ccard .ccat {
    background: var(--kf-surface-bright);
    border: var(--kf-border); border-radius: var(--radius-row, 0);
    margin: 0 var(--ccat-mx) var(--kf-3);
    padding: var(--kf-3) var(--ccat-px);
    position: relative; overflow: hidden;
    transition: background .1s;
}
.ccard .ccat:first-of-type { margin-top: var(--kf-4); }
.ccard a.ccat { display: block; color: inherit; text-decoration: none; }
/* Strip-ul sta in padding-box (left: 0) → nu atinge rama. `background` NU a venit din kf: skinul ii
   da `--sm-live-mark` (verificat pe element). */
.ccat::before {
    content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
    opacity: 0; transition: opacity .15s;
background: var(--sm-live-mark); }
/* Categoria care se danseaza ACUM: banda laterala devine PERMANENTA. Zero mecanism nou — marcajul
   exista deja, il aprindem. */
.ccat--now::before { opacity: 1; }
.ccat--now { background: var(--kf-surface-2); }

/* Gap asimetric: cu titlul promovat, randul se rupe mai des pe telefon — randul suplimentar trebuie
   sa coste mai putin pe verticala decat pe orizontala. */
.ccat__l1 { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem .4rem; margin-bottom: .15rem; }
/* Aerul de sub cap sta pe randul de META, nu pe `.ccat__l1`: intre identitate si randurile de
   JURIU/INSCRISI se interpune meta, iar distanta se ia dupa bloc, nu in mijlocul lui. */
.ccat__meta { gap: .3rem .4rem; margin-bottom: .6rem; }
.ccat__row {
    display: flex; flex-direction: column; align-items: flex-start;
    gap: .15rem; margin-top: .45rem;
}
.ccat__lbl {
    font-family: var(--kf-font-mono); font-size: var(--font-xs); font-weight: 700;
    /* .06em = tracking-ul rolului de ETICHETA (§ 18b) — o singura valoare pentru uppercase-ul de
       eticheta in toata aplicatia. */
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em);
    color: var(--kf-text-variant);
    margin: 0; padding-top: 0; white-space: nowrap;
}
/* Valorile = cluster wrap. FARA `width: 100%`: intr-un flex column cu `align-items: flex-start`
   latimea e deja fit-content, iar `.ccat__vals` e refolosit si in `.res-jury`, unde o latime fortata
   ar strica.
   `.ccat__row .e-chip-list` a venit si el incoace odata cu familia cipurilor (B159 L10, F1): cat timp
   `body.kf .e-chip-list` statea in `@layer components`, o regula de aici ar fi fost umbrita. */
.ccat__vals {
    display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; min-width: 0;
}
/* Card LAT → eticheta langa valori, coloana de etichete aliniata vertical. Containerul e `.ccard`
   (vezi § CARDS): randul de program foloseste tot `.ccat__row` dar sta in card, in afara blocului
   de categorie — pe `.ccat` n-ar fi avut ce interoga. */
@container (min-width: 27rem) {
    .ccat__row {
        display: grid; grid-template-columns: 4.5rem 1fr;
        align-items: start; gap: .25rem .5rem;
    }
    .ccat__lbl { grid-column: 1; align-self: start; justify-self: start; padding-top: .28rem; }
    .ccat__vals, .ccat__row .e-chip-list { grid-column: 2; }
}
/* CTA inscriere. `background` si `color` NU au venit din kf: skinul le rescrie pe `button.ccat__cta`
   (verificat pe element, si la hover). */
.ccat__cta {
    margin-top: var(--kf-4);
    border: 0; border-radius: var(--radius-control, 0);
    padding: .7rem 1rem;
    font-family: var(--kf-font-mono);
    font-weight: 700; font-size: var(--font-md);
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .06em);
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    transition: none;
}

/* Modal de ÎNSCRIERE (_enroll_modal.html.twig) — chrome-ul vine din .brand-modal (base-layout,
   CHROME PARTAJAT); aici doar ce e specific. Formularul înlocuiește collapse-ul inline `.ccat-form`
   din 2026-08-21: desfăcut în pagină împingea rândurile de categorii și userul își pierdea reperul.
   Lățime: cât ține ecranul, plafonat — rândurile fluide ale formularului (.enf-row) câștigă din
   lățime, dar peste ~1100px un rând de câmpuri devine prea lung ca să-l urmărești cu ochiul.
   `92vw` ține fundalul VIZIBIL de jur împrejur pe orice mărime (cerință user): niciodată fullscreen,
   nici măcar pe telefon — o cutie lipită de muchii se citește ca altă pagină, nu ca un strat peste
   lista de categorii, și userul pierde exact reperul pentru care am făcut modalul.
   `margin-inline: auto` e OBLIGATORIU, nu decorativ: sub 576px Bootstrap pune `margin: .5rem` pe
   `.modal-dialog` (stânga/dreapta FIXE), deci cu un `max-width` mai mic decât ecranul cutia ar sta
   lipită la stânga, cu tot aerul adunat în dreapta. */
.enroll-modal__dialog { max-width: min(1100px, 92vw); margin-inline: auto; }
/* Titlul poartă și identitatea categoriei (pastila de stil + formatul): rândul de categorie nu se
   mai vede cât modalul e deschis. Wrap PROGRESIV — pastilele pică sub titlu una câte una, nu toate
   deodată la un breakpoint. */
.enroll-modal .modal-title { display: flex; flex-wrap: wrap; align-items: center; gap: .2rem clamp(.35rem, 1.5vw, .6rem); }

/* Confirmare „te-ai înscris" în formular — apare, ține câteva secunde, apoi se estompează și se pliază,
   lăsând formularul gol gata de o nouă înscriere (înlocuit prin Turbo Stream). */
@keyframes regDoneFade {
    0%, 60% { opacity: 1; max-height: 3rem; padding-top: .35rem; padding-bottom: .35rem; margin-bottom: .4rem; }
    100%    { opacity: 0; max-height: 0;    padding-top: 0;       padding-bottom: 0;       margin-bottom: 0; }
}
.reg-done {
    font-family: var(--font-display); font-weight: var(--type-status-weight, 900); text-transform: var(--type-status-case, uppercase); letter-spacing: var(--type-status-tracking, .04em);
    font-size: var(--font-sm); color: var(--success); overflow: hidden;
    animation: regDoneFade 4.5s ease forwards;
}

/* Lightbox poster — overlay seamless, view-only (poster-lightbox.js) */
body.lightbox-open { overflow: hidden; }
.lightbox { position: fixed; inset: 0; z-index: 2000; display: none; align-items: center; justify-content: center; background: rgba(0,0,0,.88); padding: 2rem; }
.lightbox.is-open { display: flex; }
.lightbox__img { max-width: 95vw; max-height: 92vh; border: 3px solid var(--ink); box-shadow: var(--shadow-hard); }
.lightbox__close { position: absolute; top: 1rem; right: 1.2rem; width: 40px; height: 40px; background: var(--ink); color: var(--c-white); border: 2px solid var(--c-white); border-radius: var(--radius-sm); font-size: var(--font-xl); cursor: pointer; display: flex; align-items: center; justify-content: center; }
.lightbox__close:hover { background: var(--primary); border-color: var(--primary); }



/* Grid formular dancer — `.dancer-grid` ștearsă 2026-08-14 (markupul ei nu mai există). */
/* 700, nu 900: pe paginile `kf` familia devine mono, iar asta era singura combinație mono-900 din
   formular — o etichetă de câmp n-are de ce să fie mai grea decât eticheta de grup de lângă ea. */
/* Marcaj `layer-shadow-ok` STERS la B159 L10 F1 ca ORFAN: justifica o umbra data de `body.kf`,
   iar regula kf a coborat in acelasi layer. Paginile citate (admin-*) sunt azi `?tab=` pe
   admin/dashboard, care poarta `kf admin`. */
.dancer-lbl  { font-family: var(--font-display); font-size: var(--font-2xs); font-weight: var(--type-label-weight, 700); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .08em); color: var(--fg3); margin-bottom: 1px; display: block; }
/* `.pc-form-title` ștearsă 2026-08-21: era titlul formularului inline de înscriere; în modal titlul
   e `.modal-title`, skinat o singură dată prin `.brand-modal` (base-layout, CHROME PARTAJAT). */

/* Pill spoturi */


/* Judecător pill: [Nume | Meta] — Flash-pink tint */
/* Tenta vine din tokenii de accent, NU din roz literal: pill-ul rămăsese pe flash-pink-ul
   „Battle Broadcast" după re-skin, deci avea ramă roz cu text olive/lime în interior. */
.j-pill     { overflow: hidden; white-space: nowrap; line-height: 1.7; vertical-align: middle; text-decoration: none; }
/* .j-pill ca <a> (link spre pagina juratului, doar owner) — fără underline la hover, cursor arată clickabil. */
a.j-pill:hover { text-decoration: none; }
.j-pill__n  { padding: 0 5px; font-weight: 700; }
.j-pill__m  { padding: 0 5px; background: var(--primary-soft); border-left: 1px solid var(--border-hair); }
/* Staff (DJ/Host) line-up pills — same frame as judge pills, distinct neutral/ink accent. */
.s-pill              { border-color: var(--border-control); background: var(--sm-s2); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.ccat .s-pill { background: var(--sm-s1); color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--sm-hair); }
.s-pill__r           { display: inline-flex; align-items: center; padding: 0 5px; }
/* HEAD COACH = subliniere pe nume, nu un segment de text „HEAD" (2026-08-16): cuvântul repeta ce
   spune deja poziția (ADR 0007: primul din listă E head-ul) și mânca ~34px pe fiecare rând de juriu,
   pe fiecare card. Cuvântul rămâne pentru cititoarele de ecran (`visually-hidden` în markup).
   `--primary` = marcajul care urmează tema (olive 6.06:1 pe light / lime-dim pe dark) — NU `--volt`,
   care ca linie dă 1.10:1 pe light. Aceeași rețetă de subliniere ca `.sc-av__v.is-win b`. */
.j-pill--head .j-pill__n { text-decoration: underline; text-underline-offset: .18em; text-decoration-thickness: .09em; text-decoration-color: var(--primary); }
.s-pill .j-pill__m   { background: var(--surface); border-left-color: var(--border-hair); }

/* Competitor chip simplu */
.e-chip     { white-space: nowrap; line-height: 1.7; vertical-align: middle; }

/* ---- Cipuri: venite din kf-skin.css la B159 L10 (F1) ------------------------------------
   Stau DUPA regulile de baza de mai sus: neprefixate, au aceeasi specificitate, deci
   decide ordinea in text — exact precedenta pe care o dadea layerul inainte. */
  /* juriu / staff / înscriși → fill plat pătrat (fără border) */
  /* `border-radius` nu e aici: skinul da `--sm-r-row` tuturor cipurilor (decizia de forma B172,
     „o forma, un sens"). Era mort si in kf — `@layer skin` batea `@layer components`. */
  .j-pill, .e-chip {
    border: 0;
    background: var(--kf-surface-2); color: var(--kf-text);
    font-size: var(--font-xs); padding: .2rem .55rem; display: inline-flex; align-items: center; gap: .3rem;
  border-radius: var(--sm-r-row); }
  /* numele juratului prelua --primary-strong (remapat la olive) → olive-pe-surface ilizibil.
     → text on-surface normal, lizibil pe chip. */
  .j-pill__n { color: var(--kf-text); }
  .j-pill__m, .e-city { color: var(--kf-text-variant); font-family: var(--kf-font-mono); font-size: var(--font-xs); }
  /* JURIUL ȘI LINE-UP-UL pe pagina categoriei — MARI (cererea userului: „sunt foarte importanți").
     Scris AICI, nu în components.css: cele două reguli de deasupra sunt în `@layer components` și
     bat orice din `@layer app`, deci `.res-jury .j-pill { font-size: var(--font-lg) }` a stat inert
     de la iter. 2k.1 până acum — pastilele se randau la 11px, nu la 16px.
     SCOPAT pe `.res-jury`: aceleași `.j-pill`/`.s-pill` sunt refolosite de cardurile din
     /concursuri (`_jury_pills`, `_staff_lineup`), unde trebuie să rămână compacte.
     Măsoară ECRANUL, deliberat: antetul categoriei e un rând de PAGINĂ, pe toată lățimea, nu o
     componentă dintr-un panou. Containerul de interogare a fost încercat pe `.res-cat-cluster` și
     RETRAS — motivul, cu regula generală, e scris lângă selector în components.css. */
  .res-jury .j-pill {
    font-size: clamp(var(--font-base), 1.6vw, var(--font-2xl));
    padding: clamp(.15rem, .35vw, .35rem) clamp(.35rem, .7vw, .7rem);
  background: var(--sm-s2); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.ccat .j-pill { background: var(--sm-s1); color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--sm-hair); }
  .res-jury .j-pill__m,
  .res-jury .s-pill__r { font-size: clamp(var(--font-2xs), .9vw, var(--font-md)); }
  /* `.s-pill { background }` sters: skinul ii da `--sm-s2`, era mort si in kf. */
  /* rolul (DJ/Host/oră) = text accent DIRECT pe chip (bg transparent) — bg-ul inverse din regula
     de bază dădea olive-pe-negru ilizibil pe light. Pe dark accentul olive nu ține → lime-dim. */
  /* `font-weight` si `color` nu sunt aici: skinul le da (`--sm-text-mid`, 600). Odata cu ele a
     plecat si regula de dark, care nu mai avea decat culoarea — deci ramasese goala. */
  .s-pill__r {
    background: transparent;
    font-family: var(--kf-font-mono); font-size: var(--font-2xs);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em);
  font-weight: 600; color: var(--sm-text-mid); }
  .e-chip-list { display: inline-flex; flex-wrap: wrap; gap: .3rem; }
  /* Ramasa AICI, nu mutata cu restul lui `.ccat__*` la B159 L10 (F1): regula de deasupra o umbreste
     din `@layer components`, iar in `@layer app` ar fi devenit inerta. Pleaca odata cu familia
     CIPURILOR, careia ii apartine de fapt `.e-chip-list`.
     De ce n-a prins-o sonda: `.ccat__row` e container flex, iar `display` al unui flex item se
     BLOCHIFICA — `inline-flex` computeaza tot `flex`, adica exact valoarea pe care o voia regula
     mutata. Dump identic, declaratie inerta. A prins-o gate-ul static `css-layer-shadow`. */
  .ccat__row .e-chip-list {
    display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; min-width: 0;
  }

  /* Cipurile de ÎNSCRIȘI iau culoarea categoriei — zona pe care publicul o caută vizual („cine
     s-a înscris") era gri-pe-alb și se pierdea. Fill DESATURAT (`--style-s-chip`) la aceeași
     luminozitate ca pastila: aceeași familie cromatică, rang vizibil mai jos, deci pastila
     stilului rămâne unicul element cu hue plin + text colorat.
     Șina de 2px NU e decor: pe dark un fill în hue e practic invizibil (`hsl(240 30% 22%)` pe
     `--kf-surface-bright` #1a1c17 = 1.21:1, sub cipul gri de azi) — pastila însăși se vede pe
     dark doar prin muchia colorată. Fără șină, „cipuri în culoarea categoriei" n-ar exista pe
     dark pentru hue-urile reci, adică pentru majoritatea stilurilor.
     Tenta se MOȘTENEȘTE din strămoș (`--style-h` pe rând), nu se pune pe cip → cipurile
     adăugate prin Turbo Stream (_register.stream) sunt colorate fără cod în partial.
     Contrast calculat de mână (hsl() din variabile = unghiul mort al lui contrast-audit.php),
     cel mai rău hue: text `--kf-text` pe fill 12.6:1 light / 8.2:1 dark · `.e-city`
     (`--kf-text-variant`) 6.9:1 light / 4.8:1 dark · șina pe dark ≈3.7:1. Fill-ul vs suprafața
     cardului e 1.36:1 light / 1.21:1 dark — grupare decorativă, textul poartă înțelesul. */
  .ccat__row--entries .e-chip,
  .cat-entries-row .e-chip {
    background: hsl(var(--style-h, 0) var(--style-s-chip, 40%) var(--style-l-bg, 90%));
    border-left: 2px solid hsl(var(--style-h, 0) var(--style-s, 70%) var(--style-l-accent, 45%));
    color: var(--kf-text);
    padding-left: .42rem;
  }

/* Zona „Înscriși" = înălțime FIXĂ 2 rânduri de e-chip peste tot (uniform, indiferent de câți).
   Colapsat: max 2 linii + „Vezi toți (N)" (JS); deschis (.is-open): crește inline pe loc.
   ~2.6rem = 2×(line-height 1.7 × .67rem) + gap-ul de wrap; sigur pt gap .2rem (default) ȘI .3rem (kf). */
/* Zona „Înscriși" = înălțime fixă 2 linii (uniform). Pliere LINE-BASED în JS: chip-urile de pe
   linia 3+ se ascund, iar „… Vezi toți (N)" se pune INLINE după ultimul chip vizibil (în flux,
   nu absolut). Deschis (.is-open): tot vizibil + „Ascunde". Selectorul e pe ATRIBUT pur: plierea
   trăiește și pe /concursuri (.e-chip-list) și pe pagina de categorie (.cat-entries-row). */
[data-collapse-entries] { min-height: 2.6rem; align-content: flex-start; }
/* Varianta pe O linie (pagina de categorie / ecran de sală): fără rezerva de 2 linii. */
[data-collapse-lines="1"] { min-height: auto; }
.chips-more-btn {
    appearance: none; border: 0; background: none; cursor: pointer;
    margin-left: .2rem; padding: 0 .15rem;
    font-family: var(--font-mono); font-size: var(--font-xs); font-weight: 700;
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .03em); color: var(--primary);
    text-decoration: underline; text-underline-offset: 2px;
}
.chips-more-btn:hover { color: var(--text); }

/* Competitor chip cu buton X delete — hub /cont + acțiuni organizator (prezență/invitat/șterge).
   Înălțime minimă generoasă (--chip-act-h) ca fiecare acțiune să fie ușor de atins cu degetul; pe
   telefon crește la --tap-min (44px, a11y). */
.e-chip-del {
    --chip-act-h: 1.9rem;
    display: inline-flex; align-items: stretch;
    font-size: var(--font-xs);
    min-height: var(--chip-act-h);
    border: 1px solid var(--border-hair);
    border-radius: var(--radius-sm);
    background: var(--paper-2);
    color: var(--sm-text-hi);
    white-space: nowrap;
    line-height: 1.4;
    vertical-align: middle;
    overflow: hidden;
    margin-bottom: 3px;
}
.e-chip-del__name { display: inline-flex; align-items: center; padding: 0 8px; }
.e-chip-del__form { display: inline-flex; margin: 0; padding: 0; }
/* Butoanele-acțiune (× și ★) = cutii pătrate, lățime confortabilă, separator vizibil între ele. */
.e-chip-del__x,
.e-chip-del__act {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 30px;
    background: var(--paper-2);
    border: none; border-left: 1px solid var(--border-strong);
    color: var(--fg3); line-height: 1;
    cursor: pointer; padding: 0;
    transition: background .1s, color .1s;
}
.e-chip-del__x { font-size: var(--font-md); }
.e-chip-del__act { font-size: var(--font-sm); }
.e-chip-del__x:hover { background: var(--danger); color: var(--on-danger); }
/* Fără X pe concursuri nemodificabile */
.e-chip-del:not(.e-chip-del--rm) .e-chip-del__name { padding: 0 8px; }

/* --- Invitat (calificat direct) + apel prezență --- */
/* Marcaj INVITAT — micro-badge pe chip (public ȘI organizator), derivat din tokenii de accent. */
/* `--font-2xs`, nu `--font-3xs`: era singura treaptă de 3xs de pe pagină — o excepție de o singură
   folosire nu e o treaptă de scară, e o valoare uitată. */
.e-chip__inv {
    font-size: var(--font-2xs); font-weight: 700; letter-spacing: var(--type-status-tracking, .04em); text-transform: var(--type-status-case, uppercase);
    padding: 0 3px; border-radius: var(--radius-sm);
    background: var(--primary); color: var(--on-primary); vertical-align: middle;
}
/* --- Retras / Neprezentat (ADR 0039) — statusul spune adevărul, nu note de 0 --- */
/* Frate cu .e-chip__inv (aceeași anatomie de micro-badge), dar NEUTRU: retragerea e informație,
   nu accent. Numele retrasului se taie discret — încă lizibil (istoria rămâne pe ecran). */
.e-chip__out {
    font-size: var(--font-2xs); font-weight: 700; letter-spacing: var(--type-status-tracking, .04em); text-transform: var(--type-status-case, uppercase);
    padding: 0 3px; border-radius: var(--radius-sm);
    background: var(--text-muted); color: var(--surface); vertical-align: middle;
}
.e-chip--out, .e-chip-del--out .e-chip-del__name { text-decoration: line-through; opacity: .75; }
.bgc__row--out .bgc__name { text-decoration: line-through; opacity: 1; }
.bgc__dot--out { background: var(--text-muted); }
/* Corecțiile „la obiect" (ADR 0039, iter. 2) — declanșator mic pe rândurile cu nume (grupă /
   calificați / necalificați) + modalul de corecții. Dropdown-ul a fost ÎNLOCUIT cu modal:
   overlay fix = zero clipping (e-chip-del/.bgc/.bk-battle au overflow:hidden).
   ⚠️ Protecția față de morph NU e garda de poll (ea se evaluează la începutul vizitei, deci un tick
   deja în zbor tot ateriza peste modal — bugul din iter. 2m): stă în `modal-morph-guard.js`, care
   sare peste `.modal.show` la `turbo:before-morph-element`. */
.cm-trigger {
    appearance: none; border: 0; background: transparent; cursor: pointer;
    /* Culoare EXPLICITĂ de temă, nu inherit (iter. 2c): rândurile colorate (activ/gata) își
       vopsesc numele, iar pe tema închisă moștenirea făcea creionul invizibil. */
    /* ⚠️ Opacitate PLINĂ și culoarea de text, nu `--fg2` la 70%: pe rândurile calificaților
       (`.qual-2col--in`, fundal `--success-soft`) ieșea verde-pe-verde și se vedea DOAR la hover
       — raportat de owner 2026-08-31. Un declanșator care nu se vede e un buton care nu există. */
    color: var(--text);
    padding: 0 4px; font-size: var(--font-sm); line-height: 1; opacity: 1;
}
.cm-trigger:hover { background: var(--volt); color: var(--volt-ink); }
.cm-body .cm-lbl { font-weight: var(--type-label-weight, 700); font-size: var(--font-sm); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em); }
.cm-body .cm-warn { font-size: var(--font-xs); color: var(--warning-ink, var(--text)); background: var(--warning-soft); padding: .3rem .5rem; border-radius: var(--radius-sm); }
.cm-sep { margin: .75rem 0; border-color: var(--border-hair); opacity: 1; }
/* Numele = ținta de tap pentru apel (buton, dar arată ca text în chip). */
button.e-chip-del__name {
    appearance: none; border: 0; background: transparent; color: inherit;
    font: inherit; text-align: left; cursor: pointer;
}
.e-chip-del__cform { display: inline-flex; margin: 0; padding: 0; min-width: 0; flex: 1 1 auto; }
.e-chip-del__cform .e-chip-del__name { width: 100%; }
.e-chip-del__tick { color: var(--success); margin-right: .25rem; }
/* Confirmat prezent = fundal verde discret pe tot chipul; invitat = chenar accent. */
.e-chip-del--in  { background: var(--sm-sage-fill); color: var(--sm-on-sage-mid); }
.e-chip-del--inv { border-color: var(--primary); }
/* Comutatorul ★ „invitat" — pornit = accent plin (se vede clar că e invitat). */
.e-chip-del__act:hover { background: var(--primary); color: var(--on-primary); }
.e-chip-del__act--on { background: var(--primary); color: var(--on-primary); }

/* Spinner de procesare la tap (prezență/invitat) — spațiul e rezervat mereu (lățime 0 → nu mișcă
   nimic), devine vizibil doar pe .is-busy. */
.e-chip-del__spin {
    display: inline-flex; align-items: center; justify-content: center;
    width: 0; overflow: hidden;
    transition: width .1s;
}
.e-chip-del__spin::before {
    content: ""; width: 12px; height: 12px;
    border: 2px solid var(--border-strong); border-top-color: var(--primary);
    border-radius: 50%;
}
.e-chip-del.is-busy { opacity: .65; }
.e-chip-del.is-busy .e-chip-del__spin { width: 20px; }
.e-chip-del.is-busy .e-chip-del__spin::before { animation: e-chip-spin .6s linear infinite; }
/* Cât procesează, nu mai accepta un al doilea tap pe același chip. */
.e-chip-del.is-busy .e-chip-del__name,
.e-chip-del.is-busy .e-chip-del__act,
.e-chip-del.is-busy .e-chip-del__x { pointer-events: none; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.e-chip-del--in .e-chip-del__act { background: transparent; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.e-chip-del--in .e-chip-del__x { background: transparent; }
@keyframes e-chip-spin { to { transform: rotate(360deg); } }

/* Poster thumb */
.poster-thumb       { height: 42px; width: auto; object-fit: cover; }


.spots { font-family: var(--font-mono); font-size: var(--font-xs); font-weight: 700; padding: 0 7px; border-radius: var(--radius-sm); line-height: 1.8; white-space: nowrap; flex-shrink: 0; }


/* (.dancer-lbl = definită mai sus, în secțiunea form; dup scos. `.pc-form-title` a fost ștearsă
   2026-08-21 odată cu formularul inline de înscriere.) */

/* Autocomplete dancer name */
.ac-wrap { position: relative; }
.ac-dropdown {
    display: none; position: absolute; top: 100%; left: 0; right: 0;
    background: var(--card); border: 2px solid var(--border-strong);
    border-top: none; border-radius: 0 0 var(--radius-sm) var(--radius-sm);
    max-height: 210px; overflow-y: auto; z-index: 1050;
    box-shadow: var(--shadow-hard);
}
.ac-dropdown.ac-open { display: block; }
/* Autocomplete în rânduri inline (jurat/staff): wrap lățime fixă ca inputul direct (nu full-width,
   altfel butonul +/× sare pe rândul următor); dropdown lăsat mai lat ca să încapă numele. */
.judge-inline-row .ac-wrap,
.staff-inline-row .ac-wrap { display: inline-block; width: 150px; vertical-align: middle; }
/* Câmpul de nume e și .form-field → trebuie flex column-reverse (label DEASUPRA), nu inline-block. */
.judge-inline-row .ac-wrap.form-field,
.staff-inline-row .ac-wrap.form-field { display: flex; flex-direction: column-reverse; width: auto; }
.judge-inline-row .ac-wrap .form-control,
.staff-inline-row .ac-wrap .form-control { width: 100%; }
.judge-inline-row .ac-dropdown,
.staff-inline-row .ac-dropdown { right: auto; min-width: 230px; }
.ac-item {
    padding: 5px 8px; font-size: var(--font-md); cursor: pointer;
    border-bottom: 1px solid var(--border-hair); line-height: 1.45;
    background: var(--card);
}
.ac-item:last-child { border-bottom: none; }
.ac-item:hover, .ac-item.ac-item--hover { background: var(--volt); color: var(--volt-ink); }
/* Eticheta de rol în sugestia de persoană unificată (contribuitor / ↔ legat). */
.ac-tag {
    display: inline-block; margin-left: 4px; padding: 0 5px;
    font-size: var(--font-2xs); font-weight: 600; line-height: 1.5;
    border: 1px solid var(--border-hair); border-radius: var(--radius-sm); color: var(--fg4);
}
.ac-item:hover .ac-tag, .ac-item.ac-item--hover .ac-tag { color: inherit; border-color: currentColor; }
/* Badge per-rol (dansator / jurat / DJ / Host) în sugestie — accent flash subtil, distinct de .ac-tag. */
.ac-role {
    display: inline-block; margin-left: 4px; padding: 0 5px;
    font-size: var(--font-2xs); font-weight: 700; line-height: 1.5; text-transform: lowercase;
    border: 1px solid var(--accent-soft); border-radius: var(--radius-sm); color: var(--primary); background: var(--accent-soft);
}
.ac-item:hover .ac-role, .ac-item.ac-item--hover .ac-role { color: inherit; border-color: currentColor; background: transparent; }
.ac-hint { margin-top: 2px; margin-bottom: 4px; }

/* ---- BUTONUL DE TRIMITERE AL FORMULARULUI: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* Ramase AICI la B159 L10 (F1), cand restul formularului a plecat in components.css: selectorul
     atarna de familia `.btn-ink`, inca in `@layer components`. Mutate, ar fi fost umbrite pe toate
     cele 12 declaratii — adica moarte. Pleaca odata cu butoanele. */
  .entry-form .btn-ink {
    border: 0; border-color: var(--sm-btn-fill); border-radius: var(--radius-control, 0);
    font-family: var(--kf-font-mono); font-weight: 700; padding: .65rem 1.5rem;
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .06em);
    transition: none;
  }


/* ---- FORMULAR DE INSCRIERE: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* ---- Formular înscriere inline (flat) ----
     Cutia `.ccat-form` a dispărut 2026-08-21 (formularul s-a mutat în modal — chrome-ul îl dă
     `.brand-modal` din base-layout). Ce era scopat pe ea și ține de FORMULAR, nu de cutie, a
     coborât pe `.entry-form` — singurul ancoraj corect, fiindcă el e pe `<form>`. */
  .ac-hint, .dancer-lbl {
    font-family: var(--kf-font-mono); font-size: var(--font-xs); text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, .04em); color: var(--kf-text-variant);
  }

  /* Formular înscriere — strip „left-strip" DASHED pe stânga + colțuri rotunjite (nu cutie completă).
     Bordura e doar pe stânga; border-radius rotunjește boxul (fundal + capetele strip-ului) = look cool.
     SSOT prin clasa .entry-form pe <form> (_entry_form.html.twig) → identic pe ambele suprafețe
     publice, fiindcă amândouă randează acum ACELAȘI modal (_enroll_modal.html.twig).
     background-color, NU background: nu șterge săgeata select-ului. */
  .entry-form .form-control,
  .entry-form .form-select {
    border: 0; border-color: var(--sm-line); border-left: var(--entry-strip) dashed transparent;
    background-color: var(--kf-surface-bright);
    color: var(--kf-text); font-family: var(--kf-font-mono); font-size: var(--font-md);
    transition: border-left-color .12s;
  box-shadow: inset 0 0 0 1px var(--sm-line); border-left-color: transparent; }
  .entry-form .form-control::placeholder { color: var(--kf-outline); }
  .entry-form .dob-row select { font-family: var(--kf-font-mono); }
  /* Regula de `:focus` a disparut intreaga: amandoua declaratiile ei (`border-left-color`,
     `box-shadow`) erau umbrite de skin, care da focusul prin conturul lui + inelul `:focus-visible`. */

  /* Starea câmpului = derivată din DOM, ZERO JS (UI_RULES „starea în CSS, nu în JS"). Standardul UX
     „reward early, punish late" (Baymard/Smashing): un câmp gol NEATINS nu e o eroare, deci nu e roșu.
     GOL = stripul punctat de mai sus (calm) · COMPLETAT = strip SOLID accent (confirmare pozitivă)
     · GREȘIT = roșu, dar numai după interacțiune/submit (`:user-invalid`, niciodată la încărcare).
     Grosimea nu se schimbă între stări (`--entry-strip`) → zero salt de layout la tastare.
     Select-ul n-are `:placeholder-shown` → „gol" = opțiunea fără valoare e cea bifată.
     Ordinea contează (specificitate egală): completat, apoi eroarea care îl bate. */
  .entry-form .form-control:not(:placeholder-shown),
  .entry-form .form-select:not(:has(option[value=""]:checked)) {
    border-left-style: solid;   /* `border-left-color` sters: skinul da culoarea fasiei */
  box-shadow: inset 0 0 0 1px var(--sm-text-hi); }
  .entry-form .form-control:user-invalid,
  
  /* submit → olive solid plat, hover invert instant. Scopat pe `.entry-form` (era pe `.ccat-form`):
     acum e ACELAȘI formular pe ambele suprafețe, deci și submitul arată la fel — înainte, pe pagina
     categoriei, butonul rămânea `.btn-ink` implicit. */
  /* `.entry-form .btn-ink` si `:hover` au coborat incoace odata cu familia `.btn-ink` (B159 L10, F3).
     Cat timp familia statea in `@layer components`, o regula de aici le-ar fi umbrit TOATE cele 12
     declaratii — de-aia au plecat impreuna, nu pe rand. */
  .entry-form .btn-outline-secondary {
      color: var(--kf-text-variant);
    font-family: var(--kf-font-mono); text-transform: var(--type-control-case, uppercase); font-size: var(--font-xs);
  box-shadow: inset 0 0 0 1px var(--sm-danger); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-secondary { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }


/* Tabel categorii în card */
.pub-cat-table { font-size: var(--font-md); width: 100%; border-collapse: collapse; }
.pub-cat-table td { vertical-align: top; border: none; }
.pub-cat-table tr + tr td { border-top: 1px solid var(--border-hair); }

/* ---- VOCABULARUL DE SCOREBOARD (mono + majuscule): venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* MONO pe label-uri/taguri care erau sans/display (semnal kf — nu vine din token) */
  /* `.cat-tabs .nav-link` a IEȘIT din lista asta 2026-08-15: familia lui o deține acum rolul de
     ETICHETĂ (components.css § 18b). Regula de aici, fiind în @layer components, ar fi bătut rolul
     indiferent de specificitate — adică rolul n-ar fi făcut nimic, tăcut. */
  .bk-col-label,
  .bgc__hdr, .tbl-th, .stat-value, .sc-totals__v,
  .sc-round__hd, .sc-totals__lbl, .sc-jt__jr,
  .bk-col-count,
  .tab-now,
  .adj-chip, .sc-jt__eq,
  /* Coloana juratului (nume + pastile) trece INTEGRAL pe mono — al treilea canal care o desparte de
     numele concurenților (2026-08-27, „ori alt font ori altă culoare"). Mărimea și `--fg3` fac
     diferența de ierarhie, mono o face de VOCE: jurații mono, concurenții Archivo 900. Fără font nou
     — Space Mono e deja încărcat și deja pe pastile.
     ⚠️ Ținta e SPANUL, nu celula: `.sc-jg__judge > span` își declară propriul `font-family` în
     `@layer app`, iar o declarație proprie bate MOȘTENIREA indiferent de layer (layerele arbitrează
     doar între declarații pe ACELAȘI element). Pe celulă regula ar fi fost inertă — capcana B105. */
  .sc-jg__pill, .sc-jg__judge > span,
  .bgc__ord, .bgc__state, .bgc__crew { font-family: var(--kf-font-mono); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-2col--in .stat-value { color: var(--sm-accent-text); font-weight: 800; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-row--out .stat-value { color: var(--sm-text-mid); font-weight: 600; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-2col--in .kf-catrow:has(.e-chip--out) .stat-value { color: var(--sm-text-mid); font-weight: 600; }

  /* UPPERCASE scoreboard pe nume (fontul rămâne) — și capul grilei jurat × rundă (B115) */
  .bk-entry__name, .sc-jt__jn { text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .02em); }
  /* `letter-spacing` sters de aici: il da intreg rolul de ETICHETA, mai jos in acelasi bloc.
     Ramane majuscula — capul grilei e ETICHETA, numele e TITLU (L0.8b). */
  .sc-jg__judge { text-transform: var(--type-label-case, uppercase); }


/* ---- TABEL DE CALIFICARE: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* Tabel categorii (folosit de _qualification_section) */
  .pub-cat-table td { padding: .4rem .5rem; }
  .pub-cat-table tr + tr td { border-top: 1px solid var(--kf-line); }
  /* ⚠️ Rezultatul calificării (iter. 2o) trebuie SCRIS AICI, nu în `@layer app`. Regulile de mai sus
     stau în `@layer components`, care bate `@layer app` prin ORDINEA layerelor, indiferent de
     specificitate — deci un `font-size`/`padding` pus pe `.qual-col .e-chip` în scoreboard.css ar fi
     fost INERT și ar fi trecut toate gate-urile (exact cazul B51 din UI_RULES, și cauza D1 a acestei
     iterații). Userul a cerut litere MARI și margini mici: pe proiector numele trebuie citit din
     fundul sălii, iar aerul dintre rânduri e spațiu furat de la nume. */
  .qual-col .pub-cat-table td { padding: clamp(.12rem, 1.1cqi, .5rem) clamp(.2rem, 1.4cqi, .6rem); }
  .qual-col .e-chip {
    font-size: clamp(var(--font-xs), 3.8cqi, var(--font-3xl));
    padding: 0 clamp(.2rem, 1.2cqi, .5rem);
    line-height: 1.35;
  }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.ccat .e-chip { background: var(--sm-s1); color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--sm-hair); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-2col--in .qual-name-cell .e-chip { color: var(--sm-text-hi); font-weight: 600; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-row--out .qual-name-cell .e-chip { color: var(--sm-text-mid); font-weight: 400; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.qual-2col--in .qual-name-cell .e-chip--out { color: var(--sm-text-mid); font-weight: 400; }


/* Rândul de entries */
.cat-entries-row { display: flex; flex-wrap: wrap; align-items: center; gap: 2px; margin-top: 3px; }
.cat-entries-lbl { font-family: var(--font-display); font-size: var(--font-xs); font-weight: var(--type-label-weight, 900); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .08em); white-space: nowrap; margin-right: 3px; }
/* Culoarea, venita din kf la B159 L10 (F1). Regula separata, NU contopita: cea de mai sus are
   `margin-right: 3px`, iar un `margin` shorthand din alta parte l-ar sterge. */
.cat-entries-lbl { color: var(--kf-text-variant); }

/* Badge format battle (1v1, 2v2) */
/* ⚠️ INSIGNELE DE PE RÂNDUL DE CATEGORIE (`.ccat__l1` pe /concursuri) folosesc ACELAȘI tracking .04em
   și aceeași familie. Audit 2026-08-16: aveau trei valori diferite (.04 / .05 / .03), iar două dintre
   ele erau pe familia sans într-un rând altfel integral mono — pe același rând, la aceeași mărime,
   ochiul citește diferențele astea ca „alt fel de informație", deși nu sunt.
   Regula de tăiere: familia, cazul și tracking-ul vin din ROL (components.css § 18b); MĂRIMEA rămâne
   decizie de componentă.
   ⚠️ CORECȚIE 2026-08-18: alinierea de mai sus era INERTĂ pe /concursuri. Insignele aveau atunci
   reguli `body.kf` în @layer components (kf-skin.css), care băteau orice de aici, din @layer app,
   indiferent de specificitate — deci pe pagina pentru care s-a făcut auditul rămăseseră .05 / .03.
   De la B159 L10 (F1) totul stă în fișierul ăsta, într-un singur layer. Istoric păstrat fiindcă
   explică de ce valorile arată așa; orice atingere viitoare de tracking se face în AMBELE
   locuri, altfel schimbi doar suprafețele legacy. */
.badge-battle-fmt { white-space: nowrap; }
/* Capul de panou de categorie: tracking-ul, venit din kf la B159 L10 (F1). */
.cat-panel-hdr { letter-spacing: var(--type-label-tracking, .04em); }
/* Coborata din `@layer skin` la B159 L10 (F2).
   ⚠️ PREFIXUL `.qual-tab ` A FOST SCOS pe 2026-09-28 (owner: „randul de inscrisi nu arata ca celelalte
   randuri"). Regula era scopata pe `.qual-tab`, care exista intr-un SINGUR loc
   (`_qualification_section.html.twig:29`) — dar componenta se randeaza in CINCI, iar doua sunt in
   afara lui: „Inscrisi" (`public/category.html.twig`, ramura de dinainte de start, unde `.qual-tab`
   nici nu exista pe pagina) si „Program" (modalul de detalii, plasat dinadins in afara oricarui
   container). Alea doua au ramas pe infatisarea legacy: 11.2px/600 pe fundal PLIN, fata de
   22.52px/800 transparent la fratii lor. Acelasi component, doua registre.
   ✅ Largirea e sigura pe MARIME fiindca `--sm-fs-title` e `clamp(…, 0.97rem + 0.7vw, …)` — `vw`, nu
   `cqi`, deci se rezolva identic inauntrul si in afara containerului. Daca era `cqi`, valoarea ar fi
   sarit afara din `.qual-tab` (capcana „o regula scopata mutata isi schimba rezultatul"). */
.cat-panel-hdr { background: transparent; color: var(--sm-text-hi); border-top-color: var(--sm-hair); border-bottom-color: transparent; border-left-color: transparent; font-size: var(--sm-fs-title); font-weight: 800; letter-spacing: .02em; --bs-success-rgb: var(--sm-accent-fill-rgb); --bs-danger-rgb: var(--sm-live-rgb); }

/* ---- Accente, hover-uri si tab-uri de stare: venite din kf-skin.css la B159 L10 (F1) ----
   Destinatia e components.css, nu scoreboard.css: aceea se incarca INAINTE, deci regulile
   de aici pastreaza precedenta pe care le-o dadea layerul. */
  /* Accente lime PUNCTUALE (legacy folosea alt token → nu vin din remap) */
  /* Categoria activă = clar evidențiată: bordură accent groasă + bold + bulină lime (e clar pe ce categorie ești) */
  /* DOAR umplerea. Fostul `border: 2px solid` era un SHORTHAND: reseta și muchia colorată cu hue-ul
     stilului, și marcajul aprins de jos (UI_RULES, capcana „shorthand reset"). `font-weight: 700`
     a ieșit și el — greutatea o deține rolul de SUBTITLU (900).
     Bulina pulsantă `::after` = ȘTEARSĂ: pulsul înseamnă LIVE în restul aplicației
     (`.page-state__dot`), nu „selectat" — pe un concurs arhivat tabul activ pulsa degeaba. Tabul are
     deja trei indicatori de selecție, al patrulea era zgomot. */
  /* Toate trei regulile de aici erau MOARTE si au plecat: tabul de categorie activ, bulina LIVE si
     totalul evidentiat isi luau culorile din skin, care le rescria integral — iar regulile ALEA au
     coborat in fisierul asta la F2. Notele de deasupra raman: explica de ce umplerea e scrisa
     singura, fara scurtatura `border`. */

  /* Arheologie: aici stăteau override-urile kf pentru barele de magnitudine din celulele de scor.
     Barele au căzut pe 2026-09-01 odată cu `--sc-fill`/`--sc-edge` (arătau net/max lângă o cifră
     raw/max). `is-fav`/`is-tie` sunt acum MUCHII care iau `--success`/`--warning`, deja remapate kf
     în tokens.css — deci nu mai e nevoie de niciun override aici. */

  /* WINNER = lime: vine acum din tokenul global --winner-* (legacy :root + remap kf),
     citit de TOATE regulile is-win/winner (bk-entry, sc-hd__sub/__nm, bracket-entry).
     Zero override per-clasă aici → fără spoturi ratate. */

  /* Hover left-strip: `box-shadow` sters de aici — umbra vine intreaga de pe `.bk-battle`, a carei
     regula de skin a coborat in fisierul asta la F2. */
  .sc-jt__row:hover { background: var(--kf-surface-2); }

  /* Chip-ul „ce e acum": fundalul lime remapat dădea text olive-pe-lime (mohorât, ilizibil).
     → text near-black pe lime = contrast brutalist curat. */
  .tab-now { color: var(--volt-ink); }

  /* Butoanele de acțiune: olive-solid avea text olive (on-primary, gândit pt LIME) →
     olive-pe-olive ilizibil. → text alb (on-solid) pe olive-solid = contrast clar.
     Scopate pe `.tab-actions` din iter. 2n (bara volt separată a dispărut). */
  .tab-actions .btn-warning, .tab-actions .btn-warning:hover {
    background: var(--kf-primary-solid); border-color: var(--kf-primary-solid); color: var(--kf-on-solid);
    text-transform: var(--type-control-case, uppercase); border-radius: var(--radius-control, 0);
  }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-warning { --bs-btn-color: var(--sm-on-btn-fill); --bs-btn-bg: var(--sm-btn-fill); --bs-btn-border-color: var(--sm-btn-fill); --bs-btn-hover-color: var(--sm-on-btn-fill); --bs-btn-hover-bg: var(--sm-btn-fill-hover); --bs-btn-hover-border-color: var(--sm-btn-fill-hover); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill-hover); --bs-btn-active-border-color: var(--sm-btn-fill-hover); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: var(--sm-s2); --bs-btn-disabled-border-color: var(--sm-s2); }
  .tab-actions .btn-success, .tab-actions .btn-success:hover { text-transform: var(--type-control-case, uppercase); border-radius: var(--radius-control, 0); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-success { --bs-btn-color: var(--sm-on-btn-fill); --bs-btn-bg: var(--sm-btn-fill); --bs-btn-border-color: var(--sm-btn-fill); --bs-btn-hover-color: var(--sm-on-btn-fill); --bs-btn-hover-bg: var(--sm-btn-fill-hover); --bs-btn-hover-border-color: var(--sm-btn-fill-hover); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill-hover); --bs-btn-active-border-color: var(--sm-btn-fill-hover); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: var(--sm-s2); --bs-btn-disabled-border-color: var(--sm-s2); }


/* ---- INSIGNE DE FORMAT / TOP-N / DISCRIMINATOR: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* Aceeași pastilă pe linia de timp a programului, strânsă la lățimea unei coloane de stop.
     Declarată AICI, nu lângă restul lui `.tline` din components.css: regula de mai sus e în
     @layer components, deci ar bate orice variantă din @layer app pe `display`/`font-size`,
     indiferent de specificitate (UI_RULES, „capcana la variante/modifiere"). */
  .tline__cat {
    font-size: var(--font-2xs);
    border-left-width: 3px;
    padding: .1rem .34rem .1rem .26rem;
    letter-spacing: var(--type-label-tracking, .02em);
  }
  /* Tracking .04em (era .05): B51 aliniase grupul meta la o singură valoare, dar scrisese în
     @layer app — iar regulile de aici, din @layer components, îl băteau. Livrarea era inertă exact
     pe /concursuri, pagina pentru care fusese făcută. Cu titlul la .02em, o a patra valoare pe
     același rând ar fi readus fix problema pe care B51 o închidea. */
  .badge-battle-fmt {
    border: 0; border-radius: var(--radius-chip, 0);
    background: var(--kf-surface-inset); color: var(--kf-text-variant);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-xs);
    padding: .2rem .5rem; text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em);
  }
  /* `background` si `color` nu sunt aici: le da intregi regula generala a lui `.badge-top-n` de mai
     jos in fisier (`--sm-s1` / `--sm-text-hi`), coborata din skin la F2. */
  .badge-top-n {
    border: 0; border-radius: var(--radius-chip, 0);
    font-family: var(--kf-font-mono); font-weight: 700; font-size: var(--font-xs);
    padding: .2rem .5rem; letter-spacing: var(--type-label-tracking, .04em);
  }

  /* `color` schimbat → `background` RESTATUIT (regula perechii jumătate-rescrisă din UI_RULES):
     ink plin pe --kf-surface-inset = 13.3:1 light / 10.8:1 dark, și formatul citește ca parte din
     titlu, nu ca meta gri. */
  .ccat__l1 > .badge-battle-fmt {
    font-family: var(--kf-font-display); font-weight: var(--type-label-weight, 900);
    font-size: clamp(var(--font-xs), 2.4cqi, var(--font-sm));
    letter-spacing: var(--type-label-tracking, .02em);
    background: var(--kf-surface-inset); color: var(--kf-text);
  }
  /* Discriminatorul (UNDER 18, Feminin) pe rândul de identitate = cel mai TARE element de pe rând,
     intenționat. El e singura diferență între „Breaking 1v1" și „Breaking 1v1 UNDER 18"; până în
     2026-09 era scris cel mai mărunt de pe rând, iar concurenți s-au înscris în categoria greșită.
     Rețeta de bază (`components.css` .cat-disc, @layer app) e mono --font-2xs pe fundal soft cu
     contur — potrivită în arhivă și pe banda de taburi, prea discretă exact aici.
     Umplere PLINĂ, nu contur: accentul e umplere. Perechea `--kf-primary-solid` / `--kf-on-solid` e
     definită pe ambele teme (tokens.css: olive/alb pe light, lime-dim/olive-ink pe dark), deci
     `color` și `background` se schimbă ÎMPREUNĂ — regula perechii jumătate-rescrise. `border-color`
     urmează umplerea, altfel conturul din @layer app ar desena o ramă de altă culoare peste ea.
     Combinator COPIL ca la cei doi vecini de mai sus: un wrapper viitor în L1 nu trebuie să tragă
     alte insigne de discriminator înăuntru. */
  .ccat__l1 > .cat-disc {
    font-family: var(--kf-font-display); font-weight: var(--type-label-weight, 900);
    font-size: clamp(var(--font-xs), 2.4cqi, var(--font-sm));
    letter-spacing: var(--type-label-tracking, .02em);
    background: var(--kf-primary-solid); color: var(--kf-on-solid);
    border-color: var(--kf-primary-solid); border-radius: var(--radius-chip, 0);
    padding: .2rem .5rem;
  }


/* Discriminatorii categoriei (U18, Feminin) — `_discriminators.html.twig`, aceeași insignă oriunde
   apare o categorie: tab-uri, antet, carduri, arhivă, admin. Din familia lui .badge-battle-fmt, dar pe
   accent: ele sunt exact diferența dintre două categorii cu același nume, deci trebuie să sară în
   ochi mai tare decât formatul. */
.cat-disc {
    font-family: var(--font-mono);
    font-size: var(--font-2xs); font-weight: 700; text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em);
    color: var(--primary); background: var(--primary-soft);
    border: 1px solid var(--primary); border-radius: var(--radius-sm);
    padding: 0 5px; white-space: nowrap;
}
/* Variantă fără insignă — pentru contexte unde fundalul din jur E deja rama (linia de context a
   tabului activ din banda de categorii). DUPĂ regula de bază: aceeași specificitate, deci ordinea
   în sursă e cea care decide. Culoarea vine prin moștenire, nu se re-declară. */
.cat-disc--plain { background: transparent; border: 0; padding: 0; color: inherit; }

/* Badge TOP N */
.badge-top-n { text-transform: var(--type-label-case, uppercase); white-space: nowrap; box-shadow: inset 0 0 0 1px var(--sm-hair); color: var(--sm-text-hi); background: var(--sm-s1); }

/* Separator inline */
.cat-sep { color: var(--border-hair); margin: 0 3px; font-size: var(--font-sm); }

/* Label număr dansator */

/* Sloturile dancer în formular multi-dansator */


/* === 12b. CATEGORY ROW (formular creare concurs) =========== */

/* ×-ul stă în ANTETUL rândului (.category-row__hdr), nu absolut în colț. Varianta absolută cerea o
   rezervă de padding-right cât ținta de atingere, altfel câmpurile din flex-wrap ajungeau SUB buton
   și un tap pe „Vârstă" ștergea toată categoria. Cu antet, pericolul dispare din construcție. */
.category-row { border-radius: var(--radius-md); margin-bottom: 0.5rem; position: relative; padding: 8px 10px; border: 2px solid var(--border-strong); border-color: var(--sm-card-edge); }
/* Antetul rândului: identitatea categoriei, completată din selecturile de stil/format pe măsură ce
   omul le alege (category-builder.js). Fără el, la patru categorii deschise nu știi pe care ești. */
.category-row__hdr { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .35rem; }
/* Cerneala e fixă (--c-kf-on-var), ca tot textul de pe pastelurile rândului: pastelurile sunt
   deschise în AMBELE teme, deci un token tematic ar da text deschis pe fundal deschis pe dark. */
.category-row__title {
    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, .03em);
    font-size: var(--font-sm); color: var(--c-kf-on-var);
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.category-row--0 {  border-inline-start: 3px solid color-mix(in srgb, var(--style3-bg) 60%, var(--sm-text-hi)); }
.category-row--1 {  border-inline-start: 3px solid color-mix(in srgb, var(--style1-bg) 60%, var(--sm-text-hi)); }
.category-row--2 {  border-inline-start: 3px solid color-mix(in srgb, var(--style2-bg) 60%, var(--sm-text-hi)); }
.category-row--3 {  border-inline-start: 3px solid color-mix(in srgb, var(--style4-bg) 60%, var(--sm-text-hi)); }
.category-row--4 {  border-inline-start: 3px solid color-mix(in srgb, var(--style5-bg) 60%, var(--sm-text-hi)); }
.category-row--5 {  border-inline-start: 3px solid color-mix(in srgb, var(--style0-bg) 60%, var(--sm-text-hi)); }

/* Pastelurile de mai sus sunt FIXE (deschise) în AMBELE teme → textul de pe ele se pinuiește la
   ink închis fix, nu la tokeni tematici (pe dark deveneau gri/roșu deschis → ilizibil pe pastel). */
.category-row .form-field > label,
.category-row .jgrid__h,
.category-row .fgroup__h,
.category-row .exhint,
.category-row .frow-gloss,
.category-row .judges-section__label { color: var(--sm-text-mid); }
/* ✂️ SPART din lista de deasupra la B159 L10 (F2): cutia de explicație e singura din grup care ia
   cerneala TARE — e conținut la cerere, nu etichetă. Restul rețetei rămâne moștenit. */
.category-row .fhint__box { color: var(--sm-text-hi); }
/* Utilitarele Bootstrap au `!important` în layer(vendor), deci un `!important` de aici PIERDEA (reguli inerte
   de la ADR 0023: pe dark `.text-muted` ieșea 1.5:1 pe pastel). Se schimbă prin variabilele lor, moștenite. */
.category-row { --bs-secondary-color: var(--sm-text-mid); --bs-danger-rgb: var(--c-kf-error-rgb); background: var(--sm-s2); }
.category-row .exhint--ok   { color: var(--sm-ok); }
.category-row .exhint--warn { color: var(--sm-warning); }
.category-row .exhint--err  { color: var(--sm-danger); }
/* Pickerul de copiere stă tot pe pastel → aceeași cerneală fixă; hover = ink și mai închis
   (fostul --fg1 era alb pe dark → ilizibil pe pastel). */
.category-row .copy-picker > summary,
.category-row .copy-picker__pick,
.category-row .copy-picker__note { color: var(--sm-text-mid); }
.category-row .copy-picker > summary:hover { color: var(--sm-text-hi); }
[data-theme="dark"] .category-row .btn-close {  }  /* X-ul alb (dark) dispărea pe pastel */

.judges-section { border-top: 1px solid var(--border-hair); padding-top: 6px; margin-top: 6px; }
.judges-section__label { font-family: var(--font-sans); font-weight: 600; font-size: var(--font-sm); color: var(--fg3); }

/* Jurați + DJ/Host UNUL LÂNGĂ ALTUL (2 coloane desktop, stivuit pe mobil). Rândurile interioare
   rămân pe O linie: numele se întinde/strânge (flex), restul fixe, butonul × pe aceeași linie. */
/* Jurații au 4 coloane (nume · țară · sistem · ×), staff-ul doar 3 → 1fr/1fr le dădea la fel de mult. */
.judges-staff-grid { display: grid; grid-template-columns: 3fr 2fr; gap: 0 14px; }
.judges-staff-grid .judges-section { margin-top: 0; }
/* Rândul de jurat/staff se ține pe O linie prin coloane ELASTICE, nu prin wrap.
   DE CE nu se ținea: `.input-w-system` are `min-width: 110px !important` și NICIUN `max-width`, iar
   wrapperul lui e shrink-to-fit ⇒ selectul creștea cât cea mai lungă opțiune din listă (nemărginit);
   plus țara rigidă la 116px. Rândul cerea ~458px într-o coloană de ~333px, deci sistemul și × cădeau
   pe linia a doua. Al doilea vinovat: niciun copil n-avea `min-width: 0`, deci nimic nu putea micșora
   (minimul automat al unui flex item e min-content, nu 0).
   Țara e acum selectul COMPACT (`.cs--flag`, doar steagul) — 116px → 54px. */
.judges-staff-grid .judge-inline-row,
.judges-staff-grid .staff-inline-row { flex-wrap: wrap; row-gap: 4px; }
.judges-staff-grid .judge-inline-row > *,
.judges-staff-grid .staff-inline-row > * { min-width: 0; }
.judges-staff-grid .ac-wrap { flex: 2 1 5.5rem; width: auto; min-width: 0; }   /* numele = coloana mare */
.judges-staff-grid .jr-sys  { flex: 1 1 4.5rem; }
.judges-staff-grid .jr-sys > .form-select { min-width: 0 !important; }          /* bate .input-w-system */
.judges-staff-grid .input-w-name { max-width: none !important; }
/* `.form-field > label` e `white-space: nowrap` — acum că wrapperul poate micşora, eticheta ar ieşi
   din coloana ei. Se taie cu elipsă în loc să împingă rândul. */
.judges-staff-grid .form-field > label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; }
/* Listele jurați/staff: intrările curg UNA LÂNGĂ ALTA când e loc (nu stivuiesc pe rânduri noi degeaba —
   ex. DJ/Host la edit, col lat). Fiecare intrare = lățime de conținut. */
.judges-list, .staff-list { display: flex; flex-wrap: wrap; gap: .5rem .75rem; align-items: flex-end; }
.judges-list > .judge-inline-row, .staff-list > .staff-inline-row { flex: 0 1 auto; }
/* MOMENTELE DE PROGRAM se citesc ca ÎNREGISTRĂRI SEPARATE, nu ca o listă de chipsuri: regula de mai
   sus le punea două pe același rând când era loc, iar câmpurile fiecăreia se rupeau între ele —
   „se întind una după alta, nu văd unde începe următorul" (owner). Un moment = un rând întreg, cu
   hairline deasupra. Separatorul e pe FIECARE rând, nu pe `+ .staff-inline-row`: pe /edit momentele
   stau în DOUĂ containere (salvate + noi), iar cusătura dintre ele e exact locul care se pierdea.
   Prima linie a primului container și-l scoate — n-are ce despărți. */
.staff-list--stacked > .staff-inline-row { flex: 1 1 100%; border-top: 1px solid var(--border-hair); padding-top: .4rem; }
.form-panel__body > .staff-list--stacked:first-of-type > .staff-inline-row:first-child { border-top: 0; padding-top: 0; }
@media (max-width: 640px) {
    .judges-staff-grid { grid-template-columns: 1fr; }
    .judges-staff-grid .judge-inline-row,
    .judges-staff-grid .staff-inline-row { flex-wrap: wrap; }
}

/* Staff DJ/Host — line-up dens grupat pe rol (compact skill: line-up pe roluri, info puțină). */
/* (.staff-groups / .staff-grp / .staff-grp__lbl șterse 2026-08-17 — gruparea pe rol a DJ/Host a
   fost înlocuită de grila .jgrid, cu rolul pe coloană; ultimul consumator era edit.html.twig.) */

/* === 12c. FORM ELEMENT WIDTHS ============================== */

.input-w-country  { width: 116px !important; }  /* select țară: steag + nume RO */
/* Select țară COMPACT (rândul de înscriere): îngust cât un steag. Mecanismul e „customizable select"
   (`appearance: base-select`, Chrome/Edge 135+): browserul randează `<selectedcontent>` = o copie a
   opțiunii alese, din care ascundem numele → în starea închisă rămâne doar steagul, iar lista deschisă
   arată numele întregi. Fără suport, tot blocul `@supports` e ignorat → select nativ îngust cu numele
   trunchiat (fallback funcțional; codul ISO ajunge la server la fel). */
.cs--flag {
    width: 3.4rem !important;
    /* FĂRĂ săgeată: steagul spune singur că se alege ceva, iar o săgeată într-un control de 3,4rem
       fură jumătate din lățime. `background-image`, nu `background:` — shorthand-ul ar reseta și
       culoarea (UI_RULES, disciplina cascadei pct. 4). */
    background-image: none !important;
    padding-right: .35rem !important;
    padding-left: .35rem !important;
    text-align: center;
}
@supports (appearance: base-select) {
    /* În modul base-select controlul e desenat de UA și își pune propria `::picker-icon` — a doua
       săgeată, peste cea a Bootstrapului. Se scoate, iar înălțimea se aliniază la restul rândului
       (`.form-field > .form-select`), altfel controlul iese mai înalt decât vecinii lui. */
    .cs--flag { appearance: base-select; min-height: 1.95rem; padding-block: .32rem; }
    .cs--flag::picker-icon { display: none; }
    .cs--flag selectedcontent .cs-name { display: none; }
}
.input-w-role     { width: 56px !important; }   /* DJ / Host */
.input-w-spots    { width: 48px !important; }   /* 4/8/16/32 — max 2 cifre */
.input-w-age      { width: 62px !important; }
.input-w-gender   { width: 80px !important; }
.input-w-tie      { width: 100px !important; }   /* „Rundă extra" / „Scor total" */
.input-w-group    { width: 96px !important; }    /* opțiuni „2/grup" + eticheta „Format calif." (it. 5) */
.input-w-name     { max-width: 112px !important; }  /* nume jurat ≈ 15 caractere */
.input-w-system   { min-width: 110px !important; }
.input-w-skill    { width: 90px !important; }
.input-w-score-sm { width: 50px !important; }

.input-w-format   { width: 70px !important; }   /* „5vs5" / „crew" */
.input-w-prize    { width: 72px !important; }
/* Momentul de program: ORA și ZIUA, nu un `datetime-local`. Fostul `.input-w-when` cerea 168px
   (`zz.ll.aaaa --:--` încape fix), iar panoul stă într-o coloană de ~350px — rândul se rupea mereu. */
.input-w-hour     { width: 88px !important; }   /* `<input type="time">`: „--:--" + iconița nativă */
.input-w-day      { width: 96px !important; }   /* „Zi 2 · 02.06"; ascuns la concursul de o zi */
.input-w-schedcat { width: 108px !important; }  /* categoria momentului: se taie nativ, nu lățește rândul */
.input-w-dancers  { width: 55px !important; }
.input-w-style    { min-width: 120px !important; }

/* Săgeată compactă pe selecturi înguste — SVG mai mic + padding-right redus, ca textul scurt
   (Gen / Zi / format / rol) să nu fie acoperit de săgeata Bootstrap. Aplicat prin clasă (scoped). */
.sel-tight { background-size: 8px 8px !important; padding-right: 1.05rem !important; background-position: right .3rem center !important; }



/* === 14. RANKING & TIER ==================================== */

/* Coloane cu procent + mini bar */
.col-stat-pct   { width: 90px; text-align: right; }
.lb-col-rank    { width: 26px; }
.stat-pct-value { font-variant-numeric: tabular-nums; font-size: var(--font-md); font-weight: 700; }
.pct-high       { color: var(--success); }
.pct-low        { color: var(--danger); }

.stat-pct-bar   { height: 3px; margin-top: 2px; overflow: hidden; }
/* lățimea vine din template ca --w (singura formă de style inline permisă) */
.stat-pct-bar .bar-fill { height: 100%; width: var(--w, 0%); }

/* Δ ELO (evoluția la ultimul concurs) + formă (ultimele 5 battle-uri, V/Î) — /clasament */
.lb-delta       { font-variant-numeric: tabular-nums; font-size: var(--font-sm); font-weight: 700; white-space: nowrap; }
.lb-form        { display: inline-flex; gap: .2rem; justify-content: flex-end; }
.form-pip       { font-family: var(--font-mono); font-size: var(--font-xs); font-weight: 700; line-height: 1; }


/* Entity display */
.col-entity   { min-width: 160px; }


.entity-name { font-size: var(--font-md); }


/* Banda de tab-uri — dashboardul de admin (secțiuni) și clasamentul (stiluri de dans).
   E un RAIL (poartă `.rail` în markup): la 360px cele patru etichete de admin cer ~546px din 342
   disponibili, iar clasamentul are un tab per stil de dans, deci nemărginit.

   ⚠️ RIGLA E FUNDAL, nu `border-bottom` pe bandă + `margin-bottom: -2px` pe tab. Într-un container
   care scrollează, marja negativă iese din cutia de padding și e TĂIATĂ de `overflow` — exact
   capcana din UI_RULES. Fundalul unui container care scrollează NU se derulează
   (`background-attachment: scroll` ≠ `local`), deci tab-urile alunecă peste o linie fixă, iar
   muchia lor de jos cade exact peste ea: inactivele o lasă să se vadă prin borderul lor
   TRANSPARENT, activul o acoperă cu al lui. Aceiași pixeli ca înainte, fără marjă negativă. */
/* Cele patru marcaje `layer-shadow-ok` de aici au fost STERSE la B159 L10 F1, ca MARCAJE ORFANE:
   justificau o umbra data de `body.kf`, iar regula kf a coborat in acelasi layer — umbra nu mai exista.
   Motivul lor era oricum stins: „paginile fara kf (admin-*)" sunt azi `?tab=` pe admin/dashboard, care
   poarta `kf admin`. Singurele trei template-uri fara clasa sunt contest/edit, contest/new si
   _confirm_page, si niciunul n-are tab-uri de rol. */
.role-tabs {
    display: flex; gap: 0; margin-bottom: 1rem;
    background: linear-gradient(to top, var(--border-strong) 2px, transparent 2px);
}
.role-tab {
    padding: 0.35rem 0.9rem; font-family: var(--font-display); font-size: var(--font-md); font-weight: 700;
    text-transform: var(--type-tab-case, uppercase); letter-spacing: var(--type-tab-tracking, 0.06em);
    color: var(--fg3); border-bottom: 3px solid transparent;
    text-decoration: none; white-space: nowrap;
    scroll-snap-align: start;
    transition: color .1s, border-color .1s;
}
.role-tab:hover  { color: var(--primary); }
.role-tab.active { color: var(--fg1); border-bottom-color: var(--primary); }

/* ---- TAB-URI DE ROL (admin, clasament): venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  .role-tabs {
    border-bottom: 0; gap: 0;
    background: linear-gradient(to top, var(--kf-line) 1px, transparent 1px);
  }
  .role-tab {
    font-family: var(--kf-font-mono); text-transform: var(--type-tab-case, uppercase);
    letter-spacing: var(--type-tab-tracking, .04em); font-size: var(--font-xs); color: var(--kf-text-variant);
    border-bottom: 2px solid transparent;
  }
  .role-tab:hover { color: var(--kf-text); background: var(--kf-surface-2); }
  .role-tab.active {
    color: var(--kf-text); border-bottom-color: var(--kf-marker);
  }


/* .tier-header / .tier-badge / .tier-S..D + tokenii --tier-* = ȘTERSE 2026-08-03: zero referințe în
   templates/, src/, assets/. Erau pattern-ul de tier list din skill, niciodată adoptat — /clasament
   folosește doar .tier-table + .role-tabs + .rank-*. Dacă revine, se ia din
   .claude/skills/compact-dance-events-skill/references/tier-list-pattern.html.twig. */

.tier-table      { font-size: var(--font-md); width: 100%; border-collapse: collapse; }
.tier-table td   { padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--border-hair); vertical-align: middle; }
/* `border-bottom-style`, nu prescurtarea: aceea resetează ȘI culoarea la `currentColor`, iar
   skinul o dădea pe a lui prin layer. Linia nu se desenează oricum (stil `none` ⇒ lățime 0),
   deci pe ecran e identic — dar dump-ul de stiluri nu mai minte. B159 L10 (F2). */
.tier-table tbody tr:last-child td { border-bottom-style: none; }
/* Jurnalul de corectii (`/cont/{id}/edit`): DOUA coloane care nu se rup — data completa si numele
   autorului — impuneau impreuna un minim mai mare decat ecranul. Tabelul iesea la 389px pe un ecran
   de 320 (B173, masurat: 389/320 · 390/360 · 391/390 · exact 414/414, adica la limita).
   ⚠️ Nowrap-ul sta AICI, nu pe utilitarul `.text-nowrap`: acela are `!important` in `@layer vendor`,
   iar la `!important` ordinea layerelor se INVERSEAZA — vendorul bate `@layer app`, deci utilitarul
   nu se poate retrage din CSS-ul nostru. Singura cale e sa nu fie pus in markup.
   Sub pragul de telefon al casei (640px) se retrage: data se rupe intre zi si ora, numele intre
   cuvinte. `normal` nu FORTEAZA ruperea, o permite — deci pe ecranele unde incape, nimic nu se misca. */
.tier-table .tt-when,
.tier-table .tt-who { white-space: nowrap; }
@media (max-width: 640px) {
    .tier-table .tt-when,
    .tier-table .tt-who { white-space: normal; }
}
.tier-table tbody tr:hover { background: var(--volt); color: var(--volt-ink); }

/* ---- Clasament: venit din kf-skin.css la B159 L10 (F1) --------------------------------
   Neprefixat, aceeasi specificitate ca regulile de baza de mai sus → decide ordinea in text,
   exact precedenta pe care o dadea layerul inainte. */
  /* Panou leaderboard = plat, hairline, 0-radius */
  .lb-panel {
    /* `--kf-border` era `1px solid var(--kf-line)`: ramane 1px, culoarea vine din paleta de azi
       (coborata din `@layer skin` la B159 L10, F2). */
    border: 1px solid var(--sm-card-edge); border-radius: var(--radius-card, 0);
  }
  .lb-panel__head {
    font-family: var(--kf-font-display); font-weight: var(--type-label-weight, 900);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em);
    color: var(--kf-text);
    /* Latimea si stilul riglei de sub cap sunt de aici; culoarea a coborat din skin (B159 L10, F2).
       `background` a plecat — pe ala il da intreg. */
    border-bottom: 1px solid var(--sm-hair);
  }
  /* Cardurile primilor 3 (ranking/_top3): rama kf, ca panoul; medalia rămâne pe tokenii FILL (theme-independent). */
  .lb-top3__card { /* `--kf-border` era `1px solid var(--kf-line)`: ramane 1px, culoarea vine din paleta de azi. */
    border: 1px solid var(--sm-card-edge); }
  .lb-top3__name { font-family: var(--kf-font-display); font-weight: var(--type-title-weight, 900); letter-spacing: var(--type-title-tracking, .01em); color: var(--kf-text); }
  .lb-panel__head .lb-panel__count {
    font-family: var(--kf-font-mono); color: var(--kf-accent);
  }

  /* Tabel ELO — celule hairline, header mono, hover lime (NU volt-galben legacy) */
  /* ⚠️ `:last-child` e SCRIS aici, explicit. Regula de baza `.tier-table tbody tr:last-child td
     { border-bottom: none }` (mai sus) pierdea inainte din cauza LAYERULUI; in acelasi layer ar fi
     castigat la specificitate si ultimul rand al clasamentului si-ar fi pierdut linia — prins de
     ui-probe, 36 de grupuri schimbate pe /clasament, tot ce e sub tabel urcat cu 1px.
     Selectorul cu `:last-child` e (0,3,3) vs (0,2,3), deci castiga fara sa depinda de ordinea liniilor. */
  .lb-panel .tier-table td,
  .lb-panel .tier-table tbody tr:last-child td { border-bottom: 1px solid var(--kf-line); }
  /* zebra: skinul o da intreaga (4% cerneala), regula de aici era moarta. */
  .lb-panel .tier-table tbody tr:hover td {
    background: var(--kf-surface-2); color: var(--kf-text);
  }
  .lb-panel .tier-table tbody tr:hover td:first-child {
    box-shadow: var(--shadow-hover-strip, inset 4px 0 0 var(--kf-marker));
  }
  /* ⚠️ `background` OBLIGATORIU aici. Regula de bază (@layer app) pune
     `background: var(--surface-inverse)` = bandă neagră cu text alb. Override-ul ăsta schimba DOAR
     `color`, la un gri de text-variant → gri închis pe negru în light (1.83:1) și gri deschis pe
     alb în dark (1.70:1), fiindcă `--surface-inverse` se întoarce cu tema. Jumătate de pereche
     rescrisă = cel mai ușor bug de scăpat: ambele valori sunt „corecte", dar nu una lângă alta. */
  .tier-table-header th {
    font-family: var(--kf-font-mono); text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, .04em); font-size: var(--font-xs);
    background: var(--kf-surface-inset); color: var(--kf-text-variant);
    border-bottom: 1px solid var(--kf-line);
  }

  .entity-name {
    font-family: var(--kf-font-display); font-weight: var(--type-title-weight, 900);
    letter-spacing: var(--type-title-tracking, .01em); color: var(--kf-text);
  }
  .lb-elo {
    font-family: var(--kf-font-display); font-weight: var(--type-number-weight, 900); color: var(--kf-accent);
  }
  .lb-meta { color: var(--kf-text-variant); }
  .lb-meta__cc { font-family: var(--kf-font-mono); color: var(--kf-text); }
  .lb-empty { font-family: var(--kf-font-mono); color: var(--kf-text-variant); }

  /* Statistici V–Î / Win% */
  .stat-pct-value { font-family: var(--kf-font-mono); color: var(--kf-text); }
  .stat-pct-value.pct-high { color: var(--kf-accent); }
  .stat-pct-value.pct-low { color: var(--kf-text-variant); }
  /* Δ + formă pe skin kf */
  .lb-delta { font-family: var(--kf-font-mono); color: var(--kf-text-variant); }
  .lb-delta--up { color: var(--kf-accent); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.lb-delta--down { color: var(--sm-text-mid); }
  /* `.lb-delta--down { color }` sters: skinul il da intreg. */
  .form-pip--w { color: var(--kf-accent); font-weight: 800; }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.form-pip--l { color: var(--sm-text-mid); font-weight: 400; }
  /* `.form-pip--l { color }` sters: skinul il da intreg. */
  .stat-pct-bar { background: var(--kf-surface-inset); border-radius: var(--radius-chip, 0); }
  .stat-pct-bar .bar-fill, .bar-win {
    background: var(--kf-marker); border-radius: var(--radius-chip, 0);
  }

.tier-table-header th {
    font-weight: var(--type-label-weight, 900); padding: 0.25rem 0.5rem; white-space: nowrap; text-align: right;
}
.tier-table-header th:first-child,
.tier-table-header th:nth-child(2) { text-align: left; }

.col-rank-small { width: 28px; text-align: center; font-family: var(--font-mono); font-size: var(--font-sm); font-weight: 700; color: var(--fg4); }

/* Medalii loc 1/2/3 — utilitar GLOBAL, cerut de trei tabele fără legătură între ele: /clasament
   (dansatori + crew-uri, `.tier-table`), clasamentul calificărilor (`.pub-cat-table`) și clasamentul
   final de smoke (`.smoke-standings`). Toate trei compun clasa dinamic (`'rank-' ~ rank`), de-aia
   auditul de dead-code din 2026-08-06 n-a văzut-o și a șters-o: greppul după `rank-1` literal nu
   prinde un nume construit în Twig. Reînviată global 2026-08-14 (varianta scopată pe smoke lăsa
   celelalte două clasamente cu clasă fără reguli). Rank partajat ⇒ o medalie poate colora 2 rânduri. */
.rank-1 { color: var(--rank-gold-ink);   font-weight: 900; }
.rank-2 { color: var(--rank-silver-ink); font-weight: 900; }
.rank-3 { color: var(--rank-bronze-ink); font-weight: 900; }

/* -- Panoul de leaderboard (`/clasament`) ---------------------------------------------------
   MUTAT AICI din base-layout.css S17 pe 2026-08-23 (ADR 0023: S14 RANKING = components.css).
   Familia `.lb-*` era despicata intre doua fisiere - `.lb-col-rank`/`.lb-delta`/`.lb-form` aici,
   `.lb-panel`/`.lb-elo`/`.lb-meta` dincolo - deci cine cauta „stilurile de clasament” pe harta
   nu le gasea pe toate si risca sa scrie un duplicat.

   ⚠ ORDINEA in fisier e parte din contract, nu cosmetica: `.lb-rate` si `.stat-pct-value` au
   AMANDOUA specificitate (0,1,0) si stau pe ACELASI element (`<span class="stat-pct-value lb-rate">`).
   Cat timp blocul statea in base-layout.css, `.lb-rate` castiga prin ordinea `<link>`-urilor. Aici
   castiga doar pentru ca e scris DUPA `.stat-pct-value` (sectiunea de sus). Nu muta blocul mai sus.
   La fel, `@media` de la coada blocului trebuie sa ramana dupa `.lb-panel .tier-table td` - aceeasi
   specificitate (0,2,1), decide ordinea. Restul suprascrierilor din media sunt (0,2,0) peste (0,1,0),
   deci sunt sigure indiferent de pozitie. -- */
/* Leaderboard (clasament) — panou înrămat scoreboard, rânduri dense pe o linie */
.lb-panel { overflow: hidden; box-shadow: var(--sm-elev-float); background: var(--sm-card-surface); }
.lb-panel__head {
    display: flex; align-items: center; gap: .4rem; padding: .4rem .65rem;
    font-size: var(--font-sm);
background: var(--sm-s2); }
/* Culoarea și fontul contorului vin din regula `.lb-panel__head .lb-panel__count` de mai sus în
   fișier (coborâtă din kf la B159 L10, F1). Regula de contrast rămâne: accentul temei, NU `--volt`
   ca text pe suprafața light (~1.1:1). Vezi UI_RULES, blocul de contrast, punctul 8. */
.lb-panel__head .lb-panel__count { margin-left: auto; font-size: var(--font-xs); }

/* --- Primii 3 ca CARDURI (ranking/_top3.html.twig, ADR 0053 it. 3) — pe /clasament deasupra
   tabelului (care continuă de la locul 4) și pe pagina de start (doar ei). Trei pe rând de la
   ~36rem; pe telefon unul sub altul, întregi (minmax(min(100%, …))). Medalia = BANDĂ în capul
   cardului pe tokenii FILL --rank-* (theme-independent) cu cerneala --on-rank; restul cifrelor
   refolosesc .lb-elo / .stat-pct-value / .lb-form ca să arate identic cu tabelul. --- */
.lb-top3 {
    list-style: none; margin: 0; padding: clamp(.6rem, 2vw, .9rem);
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); gap: clamp(.5rem, 2vw, .9rem);
}
.lb-top3__card {
    display: flex; flex-direction: column; min-width: 0;
    border-radius: var(--radius-lg); overflow: hidden;
    --_medal: var(--rank-4);
background: var(--sm-card-surface); }
.lb-top3__card--1 { --_medal: var(--rank-gold); }
.lb-top3__card--2 { --_medal: var(--rank-silver); }
.lb-top3__card--3 { --_medal: var(--rank-bronze); }
.lb-top3__medal {
    display: flex; align-items: baseline; gap: .45rem; padding: .3rem .7rem;
    background: var(--_medal); color: var(--on-rank);
    font-family: var(--font-display); font-weight: var(--type-label-weight, 900); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .08em);
}
.lb-top3__rank { font-size: var(--font-xl); line-height: 1; }
.lb-top3__lbl { font-size: var(--font-2xs); }
.lb-top3__body { display: flex; flex-direction: column; gap: .3rem; padding: clamp(.6rem, 2vw, .9rem) .7rem; min-width: 0; }
.lb-top3__name { font-size: var(--font-lg); line-height: 1.1; overflow-wrap: anywhere; }
.lb-top3__nums { display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .7rem; margin-top: .2rem; }
.lb-top3__nums .lb-elo { font-size: var(--font-2xl); }
.lb-top3 .lb-form { justify-content: flex-start; }
.lb-top3__flags { display: flex; flex-wrap: wrap; gap: .3rem; }
.lb-panel .tier-table { margin: 0; }
.lb-panel .tier-table td { padding: .3rem .55rem; }
/* ELO = numărul-vedetă (fontul display și culoarea vin din regula `.lb-elo` de mai sus în fișier) */
.lb-elo { font-size: var(--font-lg); line-height: 1; }
/* meta inline pe ACEEAȘI linie cu numele (crew · oraș · țară) — nu pe rând nou.
   ⚠️ FĂRĂ `white-space: nowrap`: pe telefon el ținea min-content-ul coloanei de identitate la
   lungimea ÎNTREGULUI șir („Street Kids Crew · București · RO" ≈ 190px), deci tabelul nu se putea
   strânge deloc și restul coloanelor ieșeau din panou. Separatorii au spații în jur (` · `), deci
   ruperea cade natural între grupuri, nu în mijlocul unui nume. */
.lb-meta { font-size: var(--font-xs); }
/* a doua linie meta (N concursuri · ultima participare) — pe rând propriu sub nume */
.lb-meta--sub { display: block; }
.lb-meta__cc { white-space: nowrap; }
.lb-empty { padding: 1rem .65rem; font-size: var(--font-sm); }

/* ── Coloanele de cifre ale clasamentului (2026-08-23) ──────────────────────────────────────
   Redesign de ARHITECTURĂ A INFORMAȚIEI, nu doar de încadrare: erau 7 coloane pentru 5 fapte.
   ELO și Δ sunt aceeași cifră plus mișcarea ei (pattern ATP/FIFA), iar V–Î + Win% + bara sunt
   același record (pattern op.gg, referința declarată a esteticii dense a proiectului). Contopite,
   tabelul ajunge la 5 coloane și ÎNCAPE la 360px — la 7 coloane min-content-ul era ~480px într-un
   panou de ~340px, iar `overflow: hidden` de pe `.lb-panel` tăia ultimele două coloane FĂRĂ
   scrollbar: informație pierdută tăcut, nu derulabilă.

   Lățimile sunt PREFERINȚE în `rem` (urmează treapta de tipografie), una singură pentru ambele
   mărimi de ecran: în layout automat conținutul poate depăși preferința, deci pe desktop celula
   crește singură când cifra e mai mare. Fără prag dur = fără salt de aranjament. */
.lb-col-elo, .lb-col-form, .lb-col-rec, .lb-col-mem { text-align: right; white-space: nowrap; }
.lb-col-elo  { width: 3.6rem; }
.lb-col-form { width: 3.6rem; }
.lb-col-rec  { width: 3.4rem; }
.lb-col-mem  { width: 2.8rem; }

/* Celulele contopite se citesc pe verticală, ancorate la dreapta: cifra principală sus, calificatorul
   ei dedesubt. NU `display: flex` pe `td` — asta îl scoate din algoritmul de tabel și browserul îi
   fabrică o celulă anonimă în jur (lățimile de coloană devin sugestii ignorate). Copiii devin blocuri;
   alinierea rămâne treaba `text-align` de pe celulă. */
.lb-col-elo > *, .lb-col-rec > * { display: block; }
/* Procentul e calificatorul lui V–Î, nu un al doilea titlu — o treaptă mai jos, ca ochiul să nu
   ezite între două cifre de aceeași greutate. */
.lb-rate { font-size: var(--font-sm); }

/* ── „STRÂNGE apoi TRAGE" pe telefon (UI_RULES, decizie user 2026-08-17) ───────────────────
   Pasul 1 (strânge): o treaptă de tipografie mai jos DIN TOKENI + padding de celulă redus.
   `@media` e legitim aici — e comutatorul de DENSITATE, nu o prăbușire de aranjament.

   ⚠️ Podeaua stă DOAR pe coloana de identitate. Regula generală („`th { min-width: 3.5rem }`")
   există fiindcă `overflow-wrap: anywhere` coboară min-content-ul până la o literă, deci fără podea
   tabelul ar încăpea MEREU, pulverizat. Aici numai `.col-entity` are proză și numai ea primește
   `anywhere`; celelalte patru sunt `nowrap` cu lățime proprie, deci nu se pot pulveriza. O podea de
   3.5rem pe ELE ar fi adăugat ~90px inutili și ar fi reintrodus tragerea laterală pe care contopirea
   tocmai a eliminat-o.
   Pasul 2 (trage) e în template: `.table-responsive.rail-fade` + `data-controller="rail"` pe panou —
   plasa de siguranță pentru 320px și numele foarte lungi. */
@media (max-width: 640px) {
    .lb-table { font-size: var(--font-sm); }
    .lb-panel .lb-table td { padding: .25rem .3rem; }
    /* Doar paddingul: restul tipografiei de antet vine din `body.kf .tier-table-header th`
       (@layer components), care bate orice scriem noi în @layer app — `/clasament` e mereu `kf`. */
    .lb-panel .lb-table th { padding: .25rem .3rem; }
    .lb-table .col-entity { min-width: 7rem; overflow-wrap: anywhere; }
    /* ELO rămâne cifra-vedetă, dar la scara telefonului: --font-lg (16px) lângă un nume de 12px
       dezechilibra rândul și cerea singur ~20px în plus de coloană. */
    .lb-table .lb-elo { font-size: var(--font-md); }
    .lb-table .form-pip { font-size: var(--font-2xs); }
    .lb-table .lb-form { gap: .15rem; }
}

/* -- Antet de tabel LIPICIOS: incercat 2026-08-23, RESPINS. Nu-l reincerca. ----------------
   Ideea era `position: sticky` pe `.lb-table thead th`, cu blocantele desfacute prin
   `overflow: clip` (care clipeaza fara sa creeze container de derulare) pe `.lb-panel` si
   `overflow-y: clip` pe `.table-responsive`.

   REZULTAT MASURAT, nu presupus (raport user): antetul chiar s-a lipit - dar de capul cutiei
   `.table-responsive`, nu de ecran. Adica statea impins cu exact `--nav-h` in jos PESTE primele
   randuri, iar locul lui din tabel ramanea gol. Ambele panouri, dansatori si crew-uri.

   CONCLUZIA, care e regula generala, nu un detaliu al paginii asteia: un element cu
   `overflow-x: auto` ramane scrollportul si pentru axa Y chiar daca ii dai explicit
   `overflow-y: clip` - deci `sticky` pe verticala se rezolva fata de EL, nu fata de viewport.
   `overflow: clip` desface blocantul doar cand e pe AMBELE axe.

   Prin urmare antetul lipicios si tragerea laterala se exclud reciproc aici. Tragerea ramane:
   ea impiedica pierderea TACUTA de coloane (bugul original al lui B86), pe cand antetul e confort.
   Singura varianta care le-ar impaca e `max-height` pe container, adica o zona de derulare
   imbricata - pe telefon inseamna capcana de scroll sub deget, deci nu.

   Daca cineva chiar il vrea candva: scoate `.table-responsive` din lant si accepta ca la 320px cu
   nume foarte lungi coloanele din dreapta se pierd iar. Nu merita. */

/* === 14b. SEMNUL DE BRAND ================================== */
/* Markup-ul (_brand_logo.html.twig) randează toate variantele; cascada alege UNA. Comutarea nu se
   poate face cu <picture media="(prefers-color-scheme: dark)"> fiindcă aplicația are comutator
   MANUAL de temă ([data-theme] pe <html>): cine alege dark pe un OS light ar primi logo-ul ink pe
   bară întunecată. Perechea de reguli e deci aceeași ca la tokeni — [data-theme="dark"] explicit
   PLUS varianta de sistem pentru cine n-a ales nimic.
   Dimensiunea vine din --brand-logo-h, setat de containerul care folosește semnul. */
.brand-logo { display: inline-flex; align-items: center; line-height: 0; }

/* AXA 1 — mărimea, decisă pe `__set`. Sub 420px lockup-ul (broască + wordmark) devine ilizibil,
   deci rămâne doar wordmarkul. Doar bara cere asta; pe login și în subsol e spațiu. */
.brand-logo__set { display: inline-flex; }
.brand-logo__set--wordmark { display: none; }
@media (max-width: 420px) {
    .brand-logo--responsive .brand-logo__set--lockup { display: none; }
    .brand-logo--responsive .brand-logo__set--wordmark { display: inline-flex; }
}

/* AXA 2 — tema, decisă pe `__img`, independent de axa 1. Perechea de reguli e aceeași ca la
   tokeni: [data-theme="dark"] explicit PLUS varianta de sistem pentru cine n-a ales manual.
   Înălțimea vine din --brand-logo-h, setat de containerul care folosește semnul. */
.brand-logo__img { display: block; height: var(--brand-logo-h, 32px); width: auto; }
.brand-logo__img--dark { display: none; }
[data-theme="dark"] .brand-logo__img { display: none; }
[data-theme="dark"] .brand-logo__img--dark { display: block; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme]) .brand-logo__img { display: none; }
    :root:not([data-theme]) .brand-logo__img--dark { display: block; }
}

/* === 15. AUTH / LOGIN ====================================== */
/* Scoped pe .login-page (clasă pe <body>), NU pe `body` global.
   Regula `body { background }` inline din template scurgea fundalul negru
   în celelalte pagini la navigarea Turbo (head-ul rămânea în DOM). */
/* Fundalul = ACELAȘI cu restul site-ului (--paper), NU --surface-inverse. `inverse` se întoarce
   cu tema (bandă închisă în light, DESCHISĂ în dark) — pe o suprafață de mărimea unei pagini asta
   dădea un login negru în light și alb în dark, iar textele scrise pe el erau crem hardcodat, deci
   dispăreau exact în tema opusă. Un fundal de pagină urmează tema, nu o inversează. */
/* `--page-bg` (fundalul PAGINII) cu `--paper` ca fallback: regula din `html, body` nu ajunge aici, clasa de
   pagina are specificitate mai mare. Fallbackul tine documentele care nu incarca paleta. Vezi
   `tokens.css` (`html, body`) si `site-palette.css` (blocul de tokeni). */
.login-page { background: var(--page-bg, var(--paper)); font-family: var(--font-sans); }
/* Brandul de pe login e imagine (ca în bară), deci nu mai are tipografie — doar mărimea semnului. */
.login-brand { --brand-logo-h: 52px; }
.login-sub { font-size: var(--font-sm); color: var(--text-muted); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .1em); font-family: var(--font-mono); }
.login-error { font-size: var(--font-md); border-radius: var(--radius-sm); border: 2px solid var(--danger); }
/* --surface-2, nu --card: pagina e acum tot --paper, iar --card e ACEEAȘI valoare → cardul s-ar fi
   dizolvat în fundal (1.06:1) și n-ar mai fi rămas decât umbra care să-l țină. */
/* Umbra dura `6px 6px 0 0 var(--primary)` era ultimul idiom brutalist ramas pe o pagina publica;
   sub skin `--primary` e text-hi, deci ar fi iesit o lespede aproape neagra langa card. Chenarul se
   subtiaza la 1px ca la orice card (uniform pe toate laturile, n-are latura cu rol).
   Contopita din `@layer skin` la B159 L10 (F2). */
.login-card { background: var(--sm-card-surface); border: 1px solid var(--sm-card-edge); border-radius: var(--radius-sm); box-shadow: var(--sm-elev-card); }
.login-card .form-label { font-family: var(--font-display); font-size: var(--font-xs); font-weight: var(--type-label-weight, 900); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .1em); color: var(--fg2); }
/* Cutia cu pictograma din fata campului: `--paper` o facea alba pe card alb. Pe s2 ramane o cutie,
   nu o pata. `border-right: none` e cusatura grupului — se scriu doar laturile care se recoloreaza. */
.login-card .input-group-text { background: var(--sm-s2); border: 2px solid var(--sm-line); border-right: none; color: var(--sm-text-mid); }
/* ⚠️ `border-left` se scrie in LONGHAND-uri, nu ca prescurtarea `border-left: none`: prescurtarea
   reseteaza si CULOAREA la `currentColor`, iar pana la B159 L10 culoarea laturii venea de la regula
   generica a skinului (`.form-control { border-color }`), care batea prin layer. Dupa fold regula
   generica e mai putin specifica decat asta, deci latura si-ar fi pierdut culoarea — cusatura dintre
   cutia cu pictograma si camp s-ar fi vazut altfel. A treia oara aceeasi capcana (vezi
   `.bk-entry:last-of-type` si `.folder-tab:last-child`). Prins de sonda pe `/login`.
   `--border-strong` se remapeaza sub skin la `--sm-line`, deci valoarea ramane a ROLULUI. */
.login-card .form-control { border: 2px solid var(--border-strong); border-left-style: none; border-left-width: 0; font-family: var(--font-sans); }
.login-card .form-control:focus { box-shadow: none; border-color: var(--primary); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.entry-form .form-control:focus { box-shadow: inset 0 0 0 1px var(--sm-text-hi); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-login { font-family: var(--font-display); font-weight: var(--type-control-weight, 900); text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .08em); background: var(--sm-btn-fill); color: var(--sm-on-btn-fill); border: 2px solid var(--sm-btn-fill); box-shadow: none; border-radius: var(--radius-control, var(--radius-sm)); padding: .6rem 1rem; transition: background .1s, color .1s; }
/* Contopite din `@layer skin` la B159 L10 (F2). */
.btn-login:hover { background: var(--sm-btn-fill-hover); border-color: var(--sm-btn-fill-hover); color: var(--sm-on-btn-fill); }
.btn-login:disabled { background: var(--btn-disabled-bg); color: var(--btn-disabled-fg); border-color: var(--btn-disabled-bg); }
/* Blocul de sub cardul de login. Pagina e standalone (fără bară, fără subsol), deci și linkurile
   legale trăiesc aici. Prima frază e cea care taie confuzia raportată („nu îmi este clar cum îmi fac
   cont"): răspunsul corect e că cei mai mulți vizitatori NU au nevoie de unul — de aceea e scoasă în
   evidență față de restul. */
.login-help { margin-top: 1.25rem; text-align: center; }
.login-help p { margin: 0 0 .5rem; font-size: var(--font-sm); color: var(--fg3); line-height: 1.5; }
.login-help__lead { color: var(--fg2); }
.login-help a { color: var(--accent); }
.login-help__legal { display: flex; justify-content: center; gap: clamp(.5rem, 3vw, 1.25rem); margin-top: 1rem; font-family: var(--font-mono); font-size: var(--font-2xs); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .12em); }
/* 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. */
.login-help__legal a { color: var(--text-muted); text-decoration: none; }
.login-help__legal a { display: inline-flex; align-items: center; min-height: var(--tap-aa); }

.login-help__legal a:hover { color: var(--accent); text-decoration: underline; }

/* Pagini de eroare (error403/404) — scoped pe .error-page, NU pe `body` global. */
.error-page { background: var(--page-bg, var(--paper)); font-family: var(--font-sans); }
.err-code { font-size: var(--font-10xl); font-weight: var(--type-display-weight, 900); color: var(--text); line-height: 1; text-transform: var(--type-display-case, uppercase); letter-spacing: var(--type-display-tracking, -.04em); }
.err-code--alert { color: var(--sm-danger); }
.err-title { font-weight: var(--type-display-weight, 900); font-size: var(--font-2xl); text-transform: var(--type-display-case, uppercase); letter-spacing: var(--type-display-tracking, .04em); color: var(--text); }
.err-msg { color: var(--fg3); font-size: var(--font-md); }
/* .btn-ink* → base-layout.css „CHROME PARTAJAT" (amendament ADR 0023): modalul de confirmare e
   construit în JS (confirm-action.js) și apare pe TOATE audiențele, inclusiv pe pagina juratului,
   care nu încarcă components.css. */

/* Pagină de confirmare server-side pentru acțiuni ireversibile (B13, _confirm_page.html.twig) */
.confirm-page { display: flex; justify-content: center; padding: 2rem 1rem; }
.confirm-page__card { max-width: 34rem; width: 100%; padding: 1.25rem 1.5rem; border-left: 4px solid var(--danger); border-inline-start: 4px solid var(--sm-danger); }
.confirm-page__lines { margin: .75rem 0 0; padding-left: 1.1rem; font-size: var(--font-sm); }
.confirm-page__lines li { margin-bottom: .25rem; }
.confirm-page__warn { font-weight: 700; color: var(--danger); font-size: var(--font-sm); margin: .5rem 0 1rem; }
.confirm-page__actions { display: flex; gap: .6rem; align-items: center; }

/* .brand-modal* (chrome-ul comun al modalelor) a VENIT INAPOI in fisierul asta la B159 L10 (F2) —
   vezi blocul de la coada. Nota de mai jos e istorica (amendament
   ADR 0023), împreună cu .btn-ink*. Aici stă doar ce e specific modalului de înscriere (.enroll-modal). */

/* === Pagina de gestionare a înscrierii (B70, public/entry_manage.html.twig) ==================
   Aceeași familie cu .confirm-page: pagină utilitară, card centrat, citită o singură dată de un
   om care a venit dintr-un email. Fiecare acțiune = un bloc cu titlu, hint SCURT și butonul ei,
   ca omul să știe ce pățește ÎNAINTE să apese. Spațierea e fluidă (clamp), fără @media dur. */
.manage-page { display: flex; justify-content: center; padding: clamp(1rem, 3vw, 2rem) clamp(.75rem, 3vw, 1rem); }
.manage-page__card { max-width: 42rem; width: 100%; padding: clamp(1rem, 3.5vw, 1.75rem); }
.manage-page__lead { font-size: var(--font-sm); margin: .5rem 0 0; }
.manage-sec { margin-top: clamp(1.25rem, 3vw, 1.75rem); }
.manage-sec__h { font-family: var(--font-display); font-size: var(--font-xs); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .1em); color: var(--text-muted); margin: 0 0 .4rem; }
.manage-hint { font-size: var(--font-xs); color: var(--text-muted); margin: .35rem 0 0; }
.manage-state { font-size: var(--font-sm); margin: 0 0 .5rem; }
/* Blocul unei PERSOANE — tot ce o privește (email, numele pe site, ștergerea datelor) într-un
   singur loc, delimitat de o linie subțire. Nivelurile se disting prin MECANISM (spațiere + o
   hairline), nu prin rame groase suprapuse. */
.manage-person { margin-top: clamp(1.25rem, 3vw, 1.75rem); padding-top: clamp(.75rem, 2vw, 1rem); border-top: 1px solid var(--border-hair); }
.manage-person__name { font-family: var(--font-display); font-size: var(--font-md); text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .04em); margin: 0 0 .6rem; }

/* Acțiunile persoanei stau pe UN RÂND și pică UNA CÂTE UNA la îngustare (aranjament FLUID, nu
   @media dur). Fiecare acțiune e o coloană îngustă: butonul sus, explicația SUB el — omul citește
   „ce fac" și „ce pățesc" în aceeași privire, fără să caute textul în altă parte a paginii. */
.manage-acts { display: flex; flex-wrap: wrap; gap: clamp(.6rem, 2vw, 1rem); align-items: start; }
.manage-act { flex: 1 1 13rem; min-width: 0; display: flex; flex-direction: column; gap: .35rem; }
/* Emailul cere mai mult loc decât un buton: câmp + „Schimbă" pe același rând. */
.manage-act--email { flex: 2 1 19rem; }
.manage-act__row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: end; }
.manage-act__row .form-field { flex: 1 1 12rem; min-width: 0; }
/* Butonul umple coloana → pe telefon devine țintă de atingere pe toată lățimea, gratis. */
.manage-act > .btn-ink { width: 100%; }
.manage-act__hint { font-size: var(--font-xs); color: var(--text-muted); margin: 0; }

/* Cele două acțiuni cu consecințe stau JOS, în ordinea gravității, și se disting prin MECANISME
   DIFERITE (regula „Gruparea vizuală" pct. 1 și 5): retragerea = spațiu + hairline, ca `.manage-person`;
   ștergerea datelor = bara roșie, SINGURA de pe pagină.

   ⚠️ NU pune bară roșie și pe retragere. Accentul e o resursă limitată: două linii din aceeași
   familie de culoare, la niveluri diferite, se citesc ca ACELAȘI nivel — exact ierarhia pe care
   blocul ăsta o construiește. Retragerea e recuperabilă (te înscrii din nou cât înscrierile sunt
   deschise); ștergerea nu se poate anula. Ordinea corectă a fost cerută de owner după testul real,
   când cea ireversibilă era prezentată mai ușor decât cea recuperabilă. */
.manage-exit { margin-top: clamp(1.5rem, 4vw, 2.25rem); padding-top: clamp(.75rem, 2vw, 1rem); border-top: 1px solid var(--border-hair); }
.manage-danger { margin-top: clamp(1.25rem, 3vw, 1.75rem); padding: clamp(.75rem, 2vw, 1rem) clamp(.75rem, 2vw, 1.1rem); border-left: 4px solid var(--danger); background: var(--sm-strip-surface); }
.manage-danger__what { font-size: var(--font-sm); margin: 0 0 .6rem; }


/* --- „7 to Smoke" (ADR 0026, layout v4) — vederea publică a sesiunii de coadă (_smoke_section) ---
   Derivat vizual din vocabularul existent (tier-table, badge-status, battle-timer); ecranul e
   gândit pentru proiector (/ecran): totul fluid din clamp/cqi, fără @media dur. Blocul principal =
   grila headerului de scoreboard (REȚETA celor 3 coloane simetrice, ca .sc-totals — NU clasele ei;
   smoke nu atinge sc__*). Stările
   (verdict verde / handover / egalitate portocalie) sunt 100% server-randate — zero JS de stare. */

/* Perechea de pe ring — cel mai mare text de pe ecran; nume | mijloc | nume. */
.smoke-vs { --smoke-dot: clamp(.8rem, 1.8vw, 1.3rem); display: grid; grid-template-columns: 1fr minmax(7rem, 16rem) 1fr; align-items: center; gap: .5rem clamp(.75rem, 3vw, 2rem); margin: .5rem 0 0; text-align: center; }
/* container-type: numele (COPIL) se mărește din lățimea coloanei (cqi), nu din viewport. */
.smoke-vs__side { display: flex; flex-direction: column; align-items: center; gap: .15rem; min-width: 0; padding: .4rem clamp(.4rem, 1.5vw, 1rem) .55rem; border-radius: var(--radius-sm); container-type: inline-size; }
/* Stările laturilor — VERDE/PORTOCALIU LITERAL (--success/--warning, nu --winner-* — sub body.kf
   familia winner e remapată la cerneala kf; cerința e culoarea în orice skin). Text rămâne --text:
   tentele -soft sunt din aceeași temă. --win urmează CÂȘTIGĂTORUL: la verdict pe latura votată,
   la handover pe stânga (King = câștigătorul, garantat de builder). --in = challengerul proaspăt
   intrat (handover). --tie = ambele laturi la flash-ul de egalitate. */
.smoke-vs__side--win { background: var(--success-soft); box-shadow: inset 0 0 0 3px var(--success); }
.smoke-vs__side--in { background: var(--surface-2); box-shadow: inset 0 0 0 3px var(--primary); }
.smoke-vs__side--tie { background: var(--warning-soft); box-shadow: inset 0 0 0 3px var(--warning); }
/* Auto-fit pe O linie: bucket de lungime din builder (smokeNames.size) × lățimea containerului.
   ≈0.62em/caracter (Archivo uppercase): ≤10 car. plin, 11-16 mediu, ≥17 mic — la maximul
   bucket-ului numele umple ~o linie; overflow-wrap rămâne plasa de siguranță. */
.smoke-vs__name { font-family: var(--font-display); font-weight: var(--type-name-weight, 900); text-transform: var(--type-name-case, uppercase); line-height: 1.02; font-size: clamp(1.6rem, 15cqi, 5rem); overflow-wrap: break-word; max-width: 100%; }
.smoke-vs__name--m { font-size: clamp(1.3rem, 10cqi, 3.75rem); }
.smoke-vs__name--l { font-size: clamp(1.1rem, 6.7cqi, 2.5rem); }
.smoke-vs__x { color: var(--fg3); font-weight: 700; font-size: clamp(.9rem, 2.5vw, 2rem); }
/* Tag-ul + rândul juraților se randează MEREU (min-height rezervă rândul) → numele celor două
   laturi stau la aceeași înălțime, nimic nu sare la schimbarea de mod. */
.smoke-vs__tag { font-size: clamp(var(--font-2xs), 1.5vw, var(--font-md)); font-weight: var(--type-label-weight, 900); letter-spacing: var(--type-label-tracking, .08em); color: var(--fg2); min-height: 1.3em; }
.smoke-vs__judges { display: block; font-size: clamp(var(--font-sm), 1.8vw, var(--font-lg)); font-weight: 700; min-height: 1.2em; }
/* Mijlocul — verdict: tally-ul juraților MARE; tie: EGALITATE / RUNDĂ EXTRA; handover/live:
   PE RING + numărătoarea; next: VS + URMEAZĂ. Ceasul de SESIUNE stă MIC aici, în toate modurile
   (inversarea am.4 — cronometrul de rundă e cel MARE, sub grilă, pe .battle-timer de bază). */
.smoke-vs__mid { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .3rem; min-width: 0; }
.smoke-vs__tally { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.4rem, 5vw, 3.5rem); line-height: 1; font-variant-numeric: tabular-nums; color: var(--success); }
.smoke-vs__ties { font-size: var(--font-sm); color: var(--fg2); }
.smoke-vs__eq { font-family: var(--font-display); font-weight: 900; font-size: clamp(1.2rem, 4vw, 2.6rem); line-height: 1.05; color: var(--warning); }
.smoke-vs__eq-sub { font-size: clamp(var(--font-sm), 2vw, var(--font-lg)); font-weight: 900; letter-spacing: .06em; color: var(--warning); }
/* Badge-ul persistent al rundei extra (combinat cu .badge-status) — ține cât ține runda. */
.smoke-vs__extra { background: var(--warning-soft); color: var(--on-warning-soft); }
.smoke-now__votes { font-size: var(--font-xs); color: var(--fg2); font-weight: 700; }

/* Bilele verzi = victoriile din sesiune, sub nume oriunde apare omul. Containerul se randează
   MEREU (spațiu rezervat) ca itemele vecine să rămână aliniate. VERDE LITERAL (--success) în orice
   skin — vezi nota de la .smoke-vs__side--win; în perechea uriașă cresc din --smoke-dot. */
.smoke-dots { display: flex; flex-wrap: wrap; justify-content: center; gap: .25rem; min-height: var(--smoke-dot, .7em); }
.smoke-dots__dot { width: var(--smoke-dot, .7em); height: var(--smoke-dot, .7em); border-radius: 50%; background: var(--success); }

/* Coada — SUS, centrată; ordinea e informația-cheie, primul (evidențiat) intră următorul.
   Inelul lui vine din box-shadow, nu din border 2px — lățimea itemelor nu se mișcă între morphs. */
.smoke-queue { display: flex; flex-wrap: wrap; justify-content: center; align-items: flex-start; gap: clamp(.35rem, 1.2vw, .75rem); margin: .4rem 0; }
.smoke-queue__lbl { font-size: var(--font-2xs); font-weight: 700; text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em); color: var(--fg3); align-self: center; }
.smoke-queue__item { display: inline-flex; flex-direction: column; align-items: center; gap: .2rem; font-family: var(--font-display); text-transform: var(--type-label-case, uppercase); font-size: clamp(var(--font-sm), 2vw, var(--font-lg)); font-weight: 700; background: var(--sm-s1); border: 1px solid var(--sm-edge); border-radius: var(--radius-sm); padding: clamp(2px, .5vw, 6px) clamp(6px, 1.2vw, 14px); }
/* `background` ȘTERS la B159 L10 (F2): skinul dădea fundalul cutiei în toate stările, deci
   `--surface-2` de aici nu se randa de mult. Fără ștergere s-ar fi întors, prin ordinea din fișier. */
.smoke-queue__item--next { border-color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--primary); }
/* Challengerul care TOCMAI intră (faza „verdict"): puls periodic VIZIBIL de departe (scale +
   inel gros) — restartul la morph e invizibil pe o animație infinită. */
@keyframes smoke-queue-pulse { 50% { box-shadow: inset 0 0 0 4px var(--primary); transform: scale(1.06); } }
.smoke-queue__item--enter { animation: smoke-queue-pulse .9s ease-in-out infinite; }
/* Pierzătorul fazei de verdict (am.5) CLIPEȘTE sincron cu challengerul din coadă — aceeași durată
   și curbă, pornite la același morph — ca sala să vadă cine PLEACĂ și cine îl înlocuiește, în
   același ritm. Tenta e NEUTRĂ (inel --fg3 + scădere de prezență), nu danger: plecarea nu e o
   pedeapsă, iar roșul ar concura cu verdele verdictului de alături. Opacitatea nu coboară sub .6 —
   textul rămâne lizibil pe tot ciclul. */
@keyframes smoke-leave-pulse { 50% { box-shadow: inset 0 0 0 3px var(--fg3); opacity: .6; } }
.smoke-vs__side--out { background: var(--surface-2); box-shadow: inset 0 0 0 1px var(--border-hair); animation: smoke-leave-pulse .9s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .smoke-queue__item--enter, .smoke-vs__side--out { animation: none; } }

/* Finalul sesiunii (am.5): clasament | desfășurător — coloane FLUIDE, pică una sub alta pe
   îngust (flex-wrap + basis, fără @media dur). */
.smoke-final { display: flex; flex-wrap: wrap; align-items: flex-start; gap: clamp(.5rem, 2vw, 1.25rem); }
.smoke-final > * { flex: 1 1 22rem; min-width: 0; }

/* Desfășurătorul: primele ~8 rânduri (≈ înălțimea clasamentului) MEREU la vedere; restul în
   <details> nativ (zero JS). `open`-ul e apărat de garda de morph din live-refresh.js. */
.smoke-log { margin: .5rem 0; border: 1px solid var(--sm-card-edge); border-radius: var(--radius-sm); background: var(--sm-card-surface); }
.smoke-log__head { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; padding: .45rem .7rem .2rem; }
.smoke-log__lbl { font-size: var(--font-2xs); font-weight: var(--type-label-weight, 700); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em); color: var(--sm-text-mid); }
.smoke-log__count { font-size: var(--font-2xs); color: var(--fg2); }
.smoke-log__list { margin: 0; padding: .2rem .7rem .45rem; list-style: none; display: flex; flex-direction: column; gap: .3rem; }
/* Un rând = fapte pe o linie care se împachetează natural pe îngust (flex-wrap). */
.smoke-log__row { display: flex; flex-wrap: wrap; align-items: baseline; gap: .35rem; font-size: var(--font-sm); }
.smoke-log__no { font-variant-numeric: tabular-nums; color: var(--fg3); font-size: var(--font-xs); min-width: 2.2ch; }
.smoke-log__win { font-weight: var(--type-name-weight, 900); text-transform: var(--type-name-case, uppercase); }
.smoke-log__def { color: var(--fg3); font-size: var(--font-xs); }
.smoke-log__lose { color: var(--fg2); text-transform: var(--type-name-case, uppercase); }
.smoke-log__tally { font-variant-numeric: tabular-nums; font-weight: 800; color: var(--success); }
.smoke-log__tie { font-size: var(--font-2xs); font-weight: 900; letter-spacing: .05em; color: var(--on-warning-soft); background: var(--warning-soft); border-radius: 4px; padding: 0 .3rem; }
.smoke-log__summary { padding: .3rem .7rem .45rem; cursor: pointer; font-size: var(--font-xs); font-weight: 700; color: var(--fg2); list-style: none; }
.smoke-log__summary::-webkit-details-marker { display: none; }
.smoke-log__summary:hover { color: var(--text); }
/* Eticheta summary-ului se schimbă cu starea (deschide ↔ restrânge) — două span-uri, text în
   markup (nu în `content:` — ar scăpa de traduceri), comutate din [open]. */
.smoke-log__more:not([open]) .smoke-log__close { display: none; }
.smoke-log__more[open] .smoke-log__open { display: none; }

.smoke-standings { margin: .5rem 0; }
/* Medaliile clasamentului final vin din utilitarul global `.rank-1/2/3` (secțiunea tabelelor);
   duplicatul scopat a fost scos 2026-08-14 — aceeași declarație în două locuri. */

/* === 18. RESULTS — pagina /rezultate (concurs terminat, cu chrome) =====
   Identică cu /live (tab-uri Calificare|Bracket, partiale read-only), DOAR că în sistem (cu nav) +
   antet de concurs LIZIBIL. Datele dense (bracket/scoruri) rămân compacte; titlurile + detaliile
   publice = mărite/citibile (NU stil compact-data) — pagina e exact ce caută vizitatorul. */
/* AȘEZAREA (revizuită 2026-08-15): afiș STÂNGA · identitate+meta MIJLOC · program DREAPTA.
   Afișul stătea în dreapta ca să nu împingă titlul înăuntru și să nu rupă linia verticală cu ce vine
   dedesubt (banda de categorii). Argumentul a căzut când marginea din dreapta a primit un ocupant
   real: programul. Cu afiș la dreapta ȘI program la dreapta, unul din ele ar fi trebuit să treacă pe
   un rând propriu, adică exact înălțimea pe care antetul o pierdea. Afișul rămâne îngust
   (clamp(4.5rem, 11vw, 9rem)), deci indentarea titlului e mică și constantă. */
/* ⚠️ FARA `border-bottom` (owner, 2026-09-28). Antetul avea propriul fir despartitor, iar la 1rem
   sub el venea linia de baza a benzii de categorii: doua linii orizontale aproape lipite, care nu
   separau lucruri diferite. Separatorul e acum UNUL singur, al benzii, prelungit pana in capat
   (vezi `.cat-strip` in scoreboard.css). Marja RAMANE: ea tine respiratia dintre antet si banda. */
.res-hero { display: flex; align-items: stretch; gap: clamp(.6rem, 2vw, 1.1rem); padding: .25rem 0 1rem; margin-bottom: 1rem; }
/* Textul + programul, unul lângă altul, cu wrap PROGRESIV: programul cade sub text când nu mai
   încape, fără breakpoint dur. `flex: 1 1 <bază>` pe fiecare = pragul de rupere e lățimea de citire
   a fiecăruia, nu o cifră de viewport. */
.res-hero__main { flex: 1 1 0; min-width: 0; display: flex; flex-wrap: wrap; align-items: flex-start; gap: .35rem clamp(.9rem, 3vw, 2rem); }
/* Coloana de text ia tot restul; min-width: 0 ca un nume lung să se rupă, nu să împingă afișul. */
.res-hero__body { flex: 1 1 22rem; min-width: 0; }
/* `.res-hero__aside` (coloana de program din antet) a fost ȘTEARSĂ în iter. 2o odată cu markupul:
   programul s-a mutat în modalul „Detalii & program", unde are lățime întreagă. O regulă rămasă
   fără consumator e junk care minte la următorul grep. */

/* Butonul „Detalii & program" din rândul de meta — SINGURUL punct de intrare către descrierea
   evenimentului și program. Trebuie să CHEME la click, nu să pară o etichetă: de-aia are umplere
   inversă și muchie, nu doar text subliniat. Aceeași rețetă ca `.live-sign__go` (celălalt CTA
   compact din produs): display 900 UPPER, treaptă mică, volt la hover — un singur vocabular pentru
   „apasă aici", nu două.
   Înălțimea iese din padding (~28px) — peste pragul AA de 24px (`--tap-aa`), sub cel implicit de
   44px: e un control AJUTĂTOR (deschide un strat de citit, se închide dintr-un tap), iar în rândul
   de meta o pastilă de 44px ar rupe linia. Vezi UI_RULES „Pragul de țintă touch se alege pe ROL". */
.hero-info {
    display: inline-flex; align-items: center; gap: .35rem;
    appearance: none; cursor: pointer;
    /* ⚠️ ȚINTĂ DE TAP: „Detalii & program" e un <button>. Măsurat cu `ui-probe tap` pe 2026-09-17:
       165×27 la 390px, 169×28 la 800px — peste `--tap-aa` (24px, WCAG 2.5.8). Chenarul de 1px și
       `--sm-fs-label` sunt cele măsurate; nu le micșora fără să măsori din nou. Nota venea din
       allowlist-ul de controale al skinului și a coborât aici odată cu valorile (B159 L10, F2). */
    padding: .28rem .6rem;
    border: 1px solid var(--sm-line); border-radius: var(--sm-r-pill);
    background: var(--sm-s1); color: var(--sm-text-hi);
    font-family: var(--font-display); font-weight: 600;
    text-transform: var(--type-control-case, uppercase); letter-spacing: var(--type-control-tracking, .06em); font-size: var(--sm-fs-label);
    white-space: nowrap;
    transition: background .1s, color .1s, border-color .1s;
}
.hero-info:hover { background: var(--sm-s2); border-color: var(--sm-text-mid); color: var(--sm-text-hi); }
/* Săgeata = afordanța „duce undeva"; sare puțin la hover, ca să se citească ca deschidere, nu ca
   încă o iconiță decorativă. */
.hero-info__go { transition: transform .1s; }
.hero-info:hover .hero-info__go { transform: translateX(2px); }
/* Zona liberă de la BAZA coloanei de text (sub nume + meta), unde cade banda „acum se întâmplă aici".
   A stat o rundă ca a treia coloană, lângă program — greșit: se citea ca o CONTINUARE a liniei de
   timp, adică drept încă un moment din program.
   `:not(:has(*))`, nu `:empty`: pe paginile fără categorie blocul primește spații albe din Twig, deci
   `:empty` n-ar prinde — iar un bloc gol ar lăsa un rând mort sub detalii. */
.res-hero__extra { min-width: 0; margin-top: .45rem; }
.res-hero__extra:not(:has(*)) { display: none; }
.res-hero__extra .live-sign { margin: 0; }
/* ── Modalul „Detalii & program" (iter. 2o) ──
   Un singur loc pentru ce descrie EVENIMENTUL: anvergura, descrierea (care până acum se salva și nu
   se afișa nicăieri) și linia de timp. Lățime maximă pentru că programul e o bandă orizontală. */
.info-modal__meta {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .3rem clamp(.75rem, 3vw, 2rem);
    font-size: var(--font-base); color: var(--text-secondary);
    padding-bottom: .6rem; border-bottom: 1px solid var(--border-hair);
}
.info-modal__meta i { margin-right: .35rem; color: var(--text-muted); }
/* Lățime de CITIRE, nu lățimea modalului: un paragraf întins pe 1400px nu se citește (~65-75 de
   caractere e norma tipografică). Programul de dedesubt, în schimb, folosește toată lățimea. */
.info-modal__desc {
    margin: .9rem 0 0; max-width: 70ch;
    font-size: clamp(var(--font-base), 1.1vw, var(--font-lg)); line-height: 1.65;
    color: var(--text);
}
.info-modal__empty { margin: .9rem 0 0; font-size: var(--font-sm); color: var(--text-muted); }
.info-modal__sec { margin-top: 1.1rem; }
/* Linia de timp intră aici fără marginea ei de sus (o dă secțiunea) și fără plafonul de coloană. */
.info-modal__sec .tline { margin-top: 0; }
/* Rândul de identitate: stare + nume + acțiuni de owner. Clasă proprie, nu `d-flex`: utilitarele
   Bootstrap sunt `!important` și nu pot fi ascunse/rearanjate de modul proiector. Gap fluid, wrap
   progresiv — badge-urile și grupul de butoane cad pe rând nou pe măsură ce se îngustează. */
.res-hero__id { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem clamp(.5rem, 2vw, 1rem); }
/* Acțiunile ownerului la capătul rândului. `auto` pe margine, nu `justify-content: space-between`:
   cu wrap, space-between ar împrăștia și badge-ul de stare față de titlu când rândul se rupe. */
.res-hero__acts { margin-inline-start: auto; }
/* Afișul: înălțimea NU e ghicită — o dă coloana de text (align-self: stretch), deci afișul e exact
   „cât încape" în antet. Lățimea e fluidă (clamp), iar `cover` pe o cutie mai îngustă decât un A4
   taie doar marginile; afișul întreg se vede la click, în lightbox.
   Afișul e PRIMUL și în DOM, și vizual (fostul `order: 2` a picat odată cu mutarea la stânga), deci
   pe telefon devine bandă deasupra titlului fără nicio inversare de ordine. */
.res-hero__poster {
    flex: 0 0 auto; align-self: stretch;
    width: clamp(4.5rem, 11vw, 9rem); min-height: 6rem;
    overflow: hidden; cursor: zoom-in;
    /* ⚠️ TINTA DE TAP: afisul e clicabil (zoom). `border: 0` scoate chenarul de 2px, dar `width` si
       `min-height` sunt border-box (reboot), deci cutia ramane ≥ 72×96px pe desktop, iar pe telefon
       e banda pe toata latimea cu aspect 16/6. Static, nemasurat — nota a coborat din allowlist-ul
       de controale al skinului odata cu valoarea (B159 L10, F2). */
    border: 0; background: var(--surface-2);
box-shadow: var(--sm-elev-card); border-radius: var(--sm-r-card); }
.res-hero__poster img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* MOBIL (de unde vine majoritatea vizitatorilor) — afișul devine BANDĂ lată deasupra titlului, nu
   fâșie subțire pe margine: pe 360px o coloană de 4.5rem nu arată nimic din afiș și fură din lățimea
   textului. Bandă tăiată la o fâșie (aspect-ratio), NU afișul întreg: altfel antetul s-ar lungi cât
   un ecran și ar împinge informația reală (program, categorii, calificări) sub linia de plutire.
   Aici `@media` e corect: nu strânge un grup de informații, ci COMUTĂ axa layoutului (side-by-side →
   stivuit) — asta nu se poate exprima din flex-wrap. */
@media (max-width: 640px) {
    .res-hero { flex-direction: column; }
    .res-hero__poster { width: auto; min-height: 0; aspect-ratio: 16 / 6; }
    /* Puțin peste centru: afișele își pun titlul/logo-ul în treimea de sus. */
    .res-hero__poster img { object-position: center 35%; }
}
/* ROL 1 — TITLU. Un singur element pe pagină. Fluid, deci NU poate refolosi `.page-head` (fix la
   `--font-8xl`): titlul stă într-un rând care se înfășoară, lângă un badge și butoane. `.page-head`
   îi e frate, nu duplicat. Trepte din TOKENI în clamp: literalele treceau de gate doar fiindcă
   regexul `css-no-literal-font-size` nu se uită în interiorul lui `clamp()`. */
.res-hero__name {
    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(--sm-fs-display); line-height: 1.15; color: var(--text); margin: 0;
}
/* Meta PLAT: un grup după altul pe același rând, wrap progresiv. Fostele `.res-hero__meta-col`
   (locație+dată pe o coloană, anvergură+stiluri pe alta) forțau DOUĂ rânduri chiar și când era loc
   de unul singur — două coloane de câte două intrări sunt o grupare inventată, nu una din date.
   `align-items: baseline` ca iconițele și textul să stea pe aceeași linie de scris. */
.res-hero__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: .2rem clamp(.9rem, 3vw, 2rem); font-size: var(--font-lg); color: var(--fg2); }
.res-hero__meta i { color: var(--sm-text-mid); margin-right: .4rem; }
.res-hero__meta b { color: var(--text); font-weight: 700; }

/* Banda de categorii (`.cat-nav` + `.cat-strip`) și blocul de conținut al categoriei (`.cat-block`)
   sunt definite în scoreboard.css, lângă restul componentelor paginii de concurs. */

/* `.res-cat-name` / `.res-cat-fmt` / `.res-cat-title` = ȘTERSE 2026-08-15. Titlul categoriei repeta
   exact ce scrie în tabul activ din bandă (stil + format + discriminatori): aceeași informație în
   două locuri, cu două rețete de text. Subtitlul paginii E acum tabul activ (`.cat-strip__item--on`),
   care poartă și badge-urile categoriei. Antetul de categorie păstrează doar Line-up + Juriu + acțiuni. */

/* Antetul categoriei = Line-up + Juriu unul lângă altul (.res-cat-cluster), acțiunile ownerului în
   dreapta (.res-cat-top = space-between, wrap). Titlul a plecat de aici în tabul activ, deci rândul
   ăsta nu mai plătește o linie de titlu doar ca să repete banda de deasupra.
   Gap orizontal FLUID (clamp) → aerisit pe ecran mare, strâns pe mic; grupurile pică pe rând nou
   PROGRESIV (flex-wrap, fără breakpoint dur). Fiecare grup e aliniat la STÂNGA în div-ul lui.
   ALINIEREA celor două etichete NU cere subgrid aici: sunt amândouă pe UN rând, în același rol
   tipografic, deci au aceeași înălțime și pornesc din același `align-items: flex-start`. Subgridul
   se plătește când înălțimile pot să difere (vezi `.tline__stops`), nu preventiv. */
.res-cat-top { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: .5rem 1rem; }
/* ⚠️ NU pune `container-type: inline-size` aici (încercat și retras în iter. 2o, raportat de user ca
   „line-up și juriu aliniate prost"). `container-type` implică `contain: inline-size`, adică lățimea
   cutiei se calculează IGNORÂND conținutul. `.res-cat-cluster` e însă un FLEX ITEM cu `flex: 0 1 auto`
   în `.res-cat-top` (space-between), deci baza lui vine tocmai din conținut → cu containment se
   prăbușește la ~0 și pastilele se rup pe rânduri. Regula generală: un element care se
   auto-dimensionează din conținut (flex item `auto`, `fit-content`, `inline-block`) NU poate fi
   container de interogare. Aici n-avea nici alt candidat: `.res-cat-head` e `width: fit-content` în
   modul proiector (vezi regula `.live-projector .res-cat-head` mai sus), deci ar fi avut exact
   aceeași problemă, dar numai pe ecranul de sală — adică o regresie care s-ar fi văzut abia în sală. */
.res-cat-cluster { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .5rem clamp(1.25rem, 4vw, 3rem); min-width: 0; }
/* RÂND, nu coloană (iter. 2j.1): eticheta s-a redus la ICONIȚĂ (cuvintele „Line-up"/„Juriu"
   au fost scoase — cererea userului), deci stă în dreptul pastilelor, pe aceeași linie. */
.res-jury { display: flex; align-items: center; gap: .35rem; min-width: 0; }
/* ⚠️ Mărimea pastilelor de juriu/line-up s-a scris multă vreme în ALT fișier: regulile care contau
   (`body.kf .j-pill`, `body.kf .j-pill__m`) stăteau în kf-skin.css, în `@layer components`, iar
   layerul acela bătea `@layer app` INDIFERENT de specificitate — deci `.res-jury .j-pill`
   (0,2,0) pierdea în fața lui `body.kf .j-pill` (0,2,0, dar layer superior). De la B159 L10 (F1)
   familia `.j-pill` stă tot aici, într-un singur layer; nota rămâne fiindcă explică valorile.
   Consecința reală, găsită în iter. 2o: regula de aici zicea `--font-lg` (16px) din iter. 2k.1, dar
   pe ecran pastilele erau de 11px. Livrare INERTĂ, verde la toate gate-urile — al doilea caz după
   B51. De la B159 L10 (F1) rețeta trăiește tot aici, în familia `.j-pill` de mai sus: un singur loc,
   un singur layer. `.res-jury__lbl` a stat aici tot timpul — n-a avut niciodată pereche `body.kf`. */
/* Măsoară ECRANUL, deliberat: antetul categoriei e un rând de PAGINĂ, care ocupă toată lățimea
   disponibilă (nu trăiește într-un panou), deci `vw` chiar spune ceva despre el. Vezi UI_RULES
   „spune explicit ce măsoară o regulă de layout și de ce". */
.res-jury__lbl { font-size: clamp(var(--font-xs), 1.1vw, var(--font-lg)); text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .04em); color: var(--sm-text-mid); font-weight: 700; }

/* === 18b. TIPOGRAFIE — CINCI ROLURI (pagina de concurs) ====================
   Pagina amesteca ~9 rețete de text (display 900 / mono / sans × 6 trepte de mărime × uppercase ×
   trei valori de tracking). NU e o problemă de FAMILII — proiectul are exact două fonturi reale
   (Archivo variabilă + Space Mono), adică fix regula standard „două fonturi"; problema erau
   COMBINAȚIILE. SSOT: cinci roluri, fiecare declarat O SINGURĂ DATĂ ca listă de selectori. Un
   element nou se ADAUGĂ la lista rolului lui, nu-și scrie a zecea rețetă.

   ⚠️ Cât timp un selector avea ȘI o regulă `body.kf` în @layer components, rolul de aici nu-l putea
   controla (ordinea layerelor bate specificitatea). Cele două cazuri — `.cat-tabs .nav-link` și
   `.cat-strip__item--on` — au fost curățate de proprietățile pe care le deține rolul, iar de la
   B159 L10 (F1) totul stă într-un singur layer: rolul chiar decide.

   ROL 1 (TITLU) stă lângă `.res-hero__name`, unde se citește împreună cu mărimea lui fluidă.
   ⚠️ `.home-sec__h` a IEȘIT din rolul ETICHETĂ (B159 L4.4, ADR 0054 decizia 7): capetele de pe pagina de start
   numesc exact conținutul căutat („Concursuri deschise", „Ultimul concurs"), deci sunt TITLURI. Rețeta lui stă
   în base-layout.css, lângă restul blocului de home, ca `.home-cta-band__t`. */

/* ROL 3 — ETICHETĂ: tot ce NUMEȘTE ceva (etichete de grup, tab-uri INACTIVE, sub-tab-uri).
   Declarat ÎNAINTEA rolului 2 ca ordinea în sursă să nu-l poată bate: rolul 2 e scopat cu `--on`,
   deci are specificitate mai mare oricum. */
.res-jury__lbl,
.rail-nav__lbl,
.cat-strip__item:not(.cat-strip__item--on) .cat-strip__l1,
.cat-tabs .nav-link,
.cat-entries-lbl,
.home-stats__l {
    font-family: var(--font-mono); font-weight: 700;
    font-size: var(--font-xs); line-height: 1.3;
}
/* ⚠️ SPARTA din lista de deasupra la B159 L10 (F2). Skinul dadea 600 DOAR tabului de categorie
   inactiv; rescrierea greutatii in regula comuna ar fi subtiat si `.res-jury__lbl`, `.rail-nav__lbl`,
   `.cat-tabs .nav-link`, `.cat-entries-lbl` si `.home-stats__l` — prins de sonda pe sapte pagini.
   Restul retetei (familie, marime, line-height) ramane mostenit din regula comuna. */
.cat-strip__item:not(.cat-strip__item--on) .cat-strip__l1 { font-weight: 600; }
.cat-tabs .nav-link { font-weight: 500; }
/* Majusculele și tracking-ul, pe ROL (B159 L0.8b): etichetele și tab-urile inactive au azi aceeași rețetă, dar skinul
   le remapează separat (`--type-label-*` / `--type-tab-*`) — de aceea două reguli, lipite de lista de mai sus. */
.res-jury__lbl, .rail-nav__lbl, .cat-entries-lbl, .home-stats__l { text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .06em); }
.cat-strip__item:not(.cat-strip__item--on) .cat-strip__l1, .cat-tabs .nav-link { text-transform: var(--type-tab-case, uppercase); letter-spacing: var(--type-tab-tracking, .06em); }
/* Excepție de MĂRIME, nu de rol: titlul unui tab trebuie citit de la distanța de braț pe telefon.
   ⚠️ URCAT la `--font-lg` pe 2026-09-28 (owner: „când nu e activ tre să facem să arate mai bine
   cumva, mai lizibil"), adică EXACT mărimea tabului activ. Diagnosticul a venit din măsurătoare, nu
   din ochi: contrastul era 6.14:1, PESTE prag — problema nu era culoarea, ci că inactivul se randa
   la 12px față de 16px, adică pe trei sferturi din mărime, deci se citea ca alt fel de obiect.
   Standardul (Material 3, Carbon, Apple HIG) cere ca selecția să schimbe culoarea și greutatea, NU
   mărimea: altfel banda se rearanjează la fiecare click și scanarea o ia de la capăt.
   Activul rămâne distins prin trei semnale, peste minimul de două cerut de NN/g: umplere de cerneală
   · muchie de jos deschisă spre riglă · greutate 750 vs 600. Familia/tracking-ul rămân ale rolului. */
.cat-strip__item:not(.cat-strip__item--on) .cat-strip__l1 { font-size: var(--font-lg); }
/* Aceeași excepție pentru taburile Calificare|Bracket (iter. 2k, cererea userului: „mărim și mai
   mult fontul") — navigarea PRINCIPALĂ a categoriei urcă la --font-lg (1rem): două taburi mari,
   citite de la distanță, cu țintă de deget decentă. Familia/tracking-ul rămân ale rolului. */
.cat-tabs .nav-link { font-size: var(--font-lg); }

/* ---- TAB-URILE DE CATEGORIE: venit din kf-skin.css la B159 L10 (F1) ----
   Neprefixat, aceeasi specificitate ca regulile de baza → decide ordinea in text, exact
   precedenta pe care o dadea layerul inainte. */
  /* `.cat-tabs { border-bottom }` sters: skinul ii da rigla intreaga (1px `--sm-hair`).
     La fel `background` si `margin-bottom` de pe `.nav-link`, si hover-ul — toate intregi la skin. */
  /* `border: 0` stinge celelalte trei laturi; `border-bottom` tine GROSIMEA riglei de sub tab —
     3px, coborata din `@layer skin` la B159 L10 (F2) (era 2px inainte de skin).
     ⚠️ TINTA DE TAP: 3px LARGESTE tab-ul, nu-l stramteaza (static, nemasurat) — de-aia n-a cerut
     masuratoare. Daca vreodata scade sub 2px, masoara cu `ui-probe tap`. */
  .cat-tabs .nav-link {
    border: 0; border-bottom: 3px solid transparent; border-radius: 0;
  background: transparent; margin-bottom: -1px; }
  /* ⚠️ REVENIRE 2026-08-15: pusesem `var(--volt)` aici „ca să fie același verde ca la nivelul 1".
     Era o regresie invizibilă pe tema LIGHT: volt = lime #ccff00, iar ca LINIE pe suprafața deschisă
     dă 1.10:1 — marcajul tabului activ dispărea pur și simplu. `--kf-marker` E tokenul făcut pentru
     asta (tokens.css: „un marcaj trebuie să contrasteze cu ce e SUB el, deci urmează tema"):
     olive #506600 pe light (6.06:1), lime-dim pe dark (9.75:1). Traseul verde de la nivelul 1
     (linia benzii + șina lui `.cat-block`) folosește `--primary`, care se rezolvă la ACELAȘI olive —
     deci culoarea rămâne unitară, doar că acum se și vede. */
  /* `border-bottom-color` sters: skinul da marcajul tabului activ. Lectia din nota de deasupra
     ramane valabila pentru oricine il rescrie — un marcaj trebuie sa contrasteze cu ce e SUB el. */


/* ROL 2 — SUBTITLU: un singur element pe pagină, tabul de categorie ACTIV. A înlocuit
   `h2.res-cat-name`, deci moștenește rețeta lui (display 900 uppercase).
   `--font-lg`, nu `--font-xl`: de când TOATE tab-urile au două linii, saltul de mărime al activului
   se plătește în înălțimea întregii benzi (toate cutiile se întind la cea mai înaltă). Raportul
   1.33× față de tab-urile inactive e suficient — activul mai are umplerea volt și muchia aprinsă. */
.cat-strip__item--on .cat-strip__l1 {
    font-family: var(--font-display); font-weight: 750;
    text-transform: var(--type-title-case, uppercase); letter-spacing: var(--type-title-tracking, .02em);
    font-size: var(--font-lg); line-height: 1.15;
}

/* ROL 4 — CORP: singura zonă de text „normal" din antet. */
.res-hero__meta {
    font-family: var(--font-sans); font-weight: 400;
    font-size: var(--sm-fs-body); line-height: 1.35; color: var(--text-secondary);
}

/* ROL 5 — CIFRE / META. Regula de aici avea UN SINGUR selector, `.cat-strip__l2` — linia de context
   a tabului de categorie (TOP N · diviziuni · stare). Linia a fost ȘTEARSĂ pe 2026-09-28 (banda trece
   pe un singur rând), deci regula rămânea fără subiect — a plecat odată cu ea.
   ⚠️ Rolul êsi păstrează locul în registru: `.badge-top-n` și `.cat-disc` sunt candidații evidenti,
   dar sunt componente GLOBALE (/concursuri, /arhiva, admin) — promovarea lor la rol e o schimbare
   separată, mai mare decât livrarea asta. */


/* === DOCUMENT LEGAL (confidențialitate / termeni) =========================================
   O SINGURĂ clasă cu reguli de element scopate sub ea — singurul loc din casă unde selectorii de
   element sunt justificați: e text lung scris de mână, iar alternativa (utilitare Bootstrap
   presărate prin Twig) ar face ca a treia pagină legală să arate altfel decât primele două.
   Măsura 65ch = pragul de lizibilitate (45-75 caractere pe rând); paginile NU poartă `page-wide`,
   deci containerul e 1200px, iar coloana se centrează în el.
   Regulile stau în @layer app → bat Bootstrap (@layer vendor) fără `!important`. */
.legal-doc { max-width: 65ch; margin-inline: auto; }
.legal-doc__meta {
    font-family: var(--font-mono); font-size: var(--font-2xs); color: var(--text-muted);
    text-transform: var(--type-label-case, uppercase); letter-spacing: var(--type-label-tracking, .08em); margin: -.5rem 0 2rem;
}
.legal-doc h2 {
    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: var(--sm-fs-title); margin: 2.25rem 0 .5rem;
}
.legal-doc h3 { font-size: var(--font-lg); font-weight: 700; margin: 1.25rem 0 .35rem; }
.legal-doc p, .legal-doc li { font-size: var(--font-base); line-height: 1.65; color: var(--fg2); }
.legal-doc ul, .legal-doc ol { margin: 0 0 1rem; padding-left: 1.2rem; }
.legal-doc li { margin-bottom: .3rem; }
.legal-doc a { color: var(--accent); }
.legal-doc strong { color: var(--fg1); }
/* Tabelele (temeiuri, cookie-uri) trag lateral pe telefon în loc să spargă pagina — aceeași regulă
   ca tabelele dense: se strâng întâi, trag după. Podeaua stă pe COLOANĂ, nu pe tabel. */
.legal-doc__table { overflow-x: auto; margin: .5rem 0 1.25rem; }
.legal-doc table { width: 100%; border-collapse: collapse; font-size: var(--font-sm); }
.legal-doc th, .legal-doc td {
    border-bottom: 1px solid var(--border-hair); padding: .4rem .6rem;
    text-align: left; vertical-align: top; min-width: 6rem;
}
/* Coborata din `@layer skin` la B159 L10 (F2). */
.smoke-standings td { border-bottom-color: var(--sm-hair); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.lb-panel .tier-table tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--sm-text-hi) 4%, transparent); }
/* TENTA DE RANG pe locurile 1-2-3 (owner, 2026-09-28: „doar să le colorăm puțin în culorile de 1 2 3").
   Scrisă în ACEEAȘI formă ca zebra de deasupra — `color-mix` cu un token care se întoarce singur pe
   temă — ca să nu inventez un al doilea mecanism de tentă în același tabel.
   ⚠️ Varianta `-ink`, NU culoarea medaliei: `--rank-gold/silver/bronze` sunt culori DESCHISE, făcute
   pentru UMPLERE. Ca tentă pe alb ar fi fost aproape invizibile pe light și stridente pe dark —
   capcana măsurată la podium (1.87 pe light vs 9.11 pe dark). Familia `-ink` e „rangul ca SEMN pe o
   suprafață" și are pereche pe fiecare temă (`tokens.css:410-414` / `:697`).
   ⚠️ ORDINEA: regula asta are aceeași specificitate ca zebra, deci trebuie să stea DUPĂ ea ca s-o bată.
   Nu o muta mai sus. `:hover` (`.lb-panel .tier-table tbody tr:hover td`) rămâne deasupra amândurora.
   ⚠️ `:has()` pe rând, fiindcă clasa de rang stă pe `<span>`-ul din prima celulă, nu pe `<tr>` — iar
   markupul ăla e partajat cu `_dancer_row` și cu tabelul de crew-uri, deci NU se atinge.
   ⚠️ PROCENTUL E 6, ALES PRIN MĂSURARE, nu din ochi — și e un plafon, nu o preferință. Fundalul fiind
   TRANSLUCID, `getComputedStyle` întoarce culoarea NEcompusă și minte; singura măsurătoare onestă e pe
   pixeli. Citite din randare, cifra pe tenta ei, cele șase perechi (3 locuri × 2 teme):
       12%  →  4.47 pe light loc 1 și 4.17 pe dark loc 3  — SUB prag
        8%  →  4.44 pe dark loc 3                         — SUB prag
        7%  →  toate trec, dar cel mai strâns e 4.52      — 0.02 marjă, prea puțin
        6%  →  toate trec, cel mai strâns 4.65            — ALES
   Cifra e 12px/900, deci pragul aplicabil e 4.5, nu 3 (nu se califică drept „text mare").
   ⚠️ `contrast-audit` NU POATE prinde perechea asta: fundalurile translucide sunt în categoria lui
   „nemăsurabil". Dacă se umblă la procent, se remăsoară pe pixeli — gate-ul verde nu e dovadă aici. */
.lb-panel .tier-table tbody tr:has(.rank-1) td { background: color-mix(in srgb, var(--rank-gold-ink) 6%, transparent); }
.lb-panel .tier-table tbody tr:has(.rank-2) td { background: color-mix(in srgb, var(--rank-silver-ink) 6%, transparent); }
.lb-panel .tier-table tbody tr:has(.rank-3) td { background: color-mix(in srgb, var(--rank-bronze-ink) 6%, transparent); }
.legal-doc th {
    font-family: var(--font-mono); font-size: var(--font-2xs); text-transform: var(--type-label-case, uppercase);
    letter-spacing: var(--type-label-tracking, .06em); color: var(--text-muted); font-weight: 400;
}
/* Coborata din `@layer skin` la B159 L10 (F2). */
.smoke-standings thead th { color: var(--sm-text-mid); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; border-bottom: 1px solid var(--sm-edge); }


}

/* ⚠️ Regulile de mai jos au venit din `@layer skin` la B159 L10 (F2) si TREBUIE sa stea intr-un
   layer: o regula NELAYERIZATA bate toate layerele, deci ar fi capatat o precedenta pe care skinul
   n-o avea. `@layer app` e acelasi layer ca regulile de baza pe care le acopera; `components.css`
   se incarca dupa `tokens.css` si `base-layout.css`, deci ordinea din lant face restul. */
@layer app {
   /* END @layer app */

  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-2col--in .col-rank-narrow { --bs-secondary-color: var(--sm-text-hi); font-weight: 800; }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-row--out .col-rank-narrow { --bs-secondary-color: var(--sm-text-mid); font-weight: 600; }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-2col--in .kf-catrow:has(.e-chip--out) .col-rank-narrow { --bs-secondary-color: var(--sm-text-mid); font-weight: 600; }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-tab .cat-panel-hdr .badge.bg-danger { color: var(--sm-on-live); }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-tab .cat-panel-hdr .badge.bg-success { color: var(--sm-on-accent); }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .qual-tab .cat-panel-hdr .badge { border-radius: var(--sm-r-pill); letter-spacing: 0; }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .cat-tabs .nav-link:hover { background: transparent; color: var(--sm-text-hi); border-bottom-color: var(--sm-line); }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .alert-success::before { content: "✓"; content: "✓" / ""; margin-inline-end: .35em; font-weight: 700; }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .alert-info { background: var(--sm-s2); border-color: transparent; color: var(--sm-text-hi); --bs-alert-link-color: var(--sm-text-hi); }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .alert-secondary { background: var(--sm-s2); border-color: var(--sm-line); color: var(--sm-text-mid); }
  /* Coborata din `@layer skin` la B159 L10 (F2). */
  .cat-disc:not(.cat-disc--plain) { background: transparent; color: var(--sm-text-hi); border-color: var(--sm-text-hi); box-shadow: inset 0 0 0 1px var(--sm-text-hi); }
  /* ⚠️ Regula de deasupra deseneaza cipul cu CERNEALA pe suprafata deschisa. Tabul ACTIV e insa umplut
     cu cerneala (`--sm-paper`), deci acolo cipul iesea cerneala-pe-cerneala: masurat 1.00:1, adica
     INVIZIBIL, pe ambele teme. De cand diviziunile au intrat in randul unic al tabului (2026-09-28),
     cazul asta chiar se randeaza. Perechea se inverseaza o data, aici: 16.34 pe light, 14.93 pe dark. */
  .cat-strip__item--on .cat-disc:not(.cat-disc--plain) {
      color: var(--sm-on-paper); border-color: var(--sm-on-paper); box-shadow: inset 0 0 0 1px var(--sm-on-paper);
  }
  /* `.podium__step--4 .podium__medal { color }` a fost STEARSA 2026-09-28: exista ca sa faca CIFRA
     lizibila pe fundalul de medalie al locului 4. Medalia n-are fundal de cand cercurile au disparut
     (vezi `.podium__medal` in scoreboard.css), deci regula ramanea fara subiect. */


    /* ══════════ VALORI DE SITE pentru reguli care traiesc in foi PARTAJATE CU JURATUL ══════════
       `.alert-*` si `.brand-modal` isi au regula de baza in `tokens.css` / `base-layout.css`, pe care le
       incarca si ecranul juratului — iar acolo tokenii `--sm-*` nu exista. Regulile de baza raman ale
       juratului, neatinse; astea de aici sunt valorile de SITE si castiga prin ordinea din lant
       (`components.css` se incarca dupa amandoua). Coborate din `@layer skin` la B159 L10 (F2).
       ⚠️ Nu muta declaratiile astea „mai aproape de regula lor": acolo ar strica ecranul juratului. */
    .alert-danger { background: var(--sm-danger-soft); border-color: transparent; color: var(--sm-on-danger-soft); --bs-alert-link-color: var(--sm-on-danger-soft); }
    .alert-warning { background: var(--sm-warning-soft); border-color: transparent; color: var(--sm-on-warning-soft); --bs-alert-link-color: var(--sm-on-warning-soft); }
    .alert-info { background: var(--sm-s2); border-color: transparent; color: var(--sm-text-hi); --bs-alert-link-color: var(--sm-text-hi); }
    /* Succesul e un TOAST: pluteste, deci are nevoie de o suprafata — s1 + contur, ca modalul (nu umplere
       lime). Bifa vine din `::before`, ascunsa cititoarelor de ecran. */
    .alert-success { background: var(--sm-s1); border-color: var(--sm-line); color: var(--sm-ok); }
    /* Iconița „+" de la „Concurs nou", din meniul de cont. Regula de bază (greutatea) stă în
       `base-layout.css`, care e partajat cu juratul; CULOAREA e valoare de site, deci aterizează aici.
       Juratul nici nu randează bara, dar convenția fișierului rămâne convenție. */
    .app-nav__menu .app-nav__menu-cta .bi { color: var(--sm-accent-text); }

/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-danger { --bs-btn-color: var(--sm-danger); --bs-btn-bg: transparent; --bs-btn-border-color: var(--sm-danger); --bs-btn-hover-color: var(--sm-on-danger-soft); --bs-btn-hover-bg: var(--sm-danger-soft); --bs-btn-hover-border-color: var(--sm-danger); --bs-btn-active-color: var(--sm-on-danger-soft); --bs-btn-active-bg: var(--sm-danger-soft); --bs-btn-active-border-color: var(--sm-danger); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-danger { --bs-btn-color: var(--sm-on-danger); --bs-btn-bg: var(--sm-danger); --bs-btn-border-color: var(--sm-danger); --bs-btn-hover-color: var(--sm-on-danger); --bs-btn-hover-bg: var(--sm-danger-hover); --bs-btn-hover-border-color: var(--sm-danger-hover); --bs-btn-active-color: var(--sm-on-danger); --bs-btn-active-bg: var(--sm-danger-hover); --bs-btn-active-border-color: var(--sm-danger-hover); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-success { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-warning { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-primary { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-outline-info { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-secondary { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-primary { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.btn-info { --bs-btn-color: var(--sm-text-hi); --bs-btn-bg: var(--sm-s3); --bs-btn-border-color: var(--sm-line); --bs-btn-hover-color: var(--sm-text-hi); --bs-btn-hover-bg: color-mix(in srgb, var(--sm-text-hi) 12%, var(--sm-s3)); --bs-btn-hover-border-color: var(--sm-line); --bs-btn-active-color: var(--sm-on-btn-fill); --bs-btn-active-bg: var(--sm-btn-fill); --bs-btn-active-border-color: var(--sm-btn-fill); --bs-btn-disabled-color: var(--sm-text-mid); --bs-btn-disabled-bg: transparent; --bs-btn-disabled-border-color: var(--sm-edge); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
/* Coborata din `@layer skin` la B159 L10 (F2). */
div.ccat__cta { background: transparent; color: var(--sm-text-mid); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
button.ccat__cta { background: var(--sm-btn-fill); color: var(--sm-on-btn-fill); }
  /* Avertismentul de dublura de la inscriere — VENIT din base-layout.css la B159 L10 (F2).
     Construit din JS de `entry_autocomplete_controller.js`, deci e strict al site-ului; foaia
     de unde a venit e incarcata si de ecranul juratului. */
  /* Warning fuzzy „ești tu?" la înscriere (near-duplicate nudge) — bloc full-width SUB rând */
  .dup-warn { width: 100%; margin: 0 0 .5rem; padding: .35rem .5rem; background: var(--warning-soft); border: 1px solid var(--warning-border); border-radius: var(--radius-md); font-size: var(--font-xs); color: var(--sm-on-warning-soft); border-color: transparent; }
  .dup-warn__head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .25rem; }
  .dup-warn__q { color: var(--sm-on-warning-soft); font-weight: 600; }
  .dup-warn__close { flex-shrink: 0; border: 1px solid var(--border-hair); background: var(--card); border-radius: var(--radius-sm); padding: .05rem .5rem; font-size: var(--font-xs); cursor: pointer; white-space: nowrap; color: var(--fg2); }
  .dup-warn__close:hover { background: var(--surface-inverse); color: var(--text-on-inverse); border-color: var(--border-strong); }
  .dup-warn__pick { display: inline-block; margin: .12rem .2rem .12rem 0; padding: .12rem .5rem; background: var(--card); border: 1px solid var(--border-hair); border-radius: var(--radius-sm); font-size: var(--font-xs); cursor: pointer; }
  .dup-warn__pick:hover { background: var(--volt); color: var(--volt-ink); border-color: var(--volt-ink); }

  /* ---- HOVER, coborat din `@layer skin` la B159 L10 (F2) ----
     ⚠️ Blocul sta INADINS la coada layerului: regulile de aici au aceeasi specificitate ca cele de
     baza pe care le acopera, deci ordinea decide — exact precedenta pe care le-o dadea layerul.
     Garda `(hover: hover)` ramane: pe touch `:hover` e lipicios, iar starea de repaus trebuie sa fie
     cea care se vede. */
  @media (hover: hover) {
    .kf-catrow:hover td { background: color-mix(in srgb, var(--sm-text-hi) 8%, transparent); color: var(--sm-text-hi); }
    .ccat:hover::before { opacity: 0; }
    .ccat:hover { background: color-mix(in srgb, var(--sm-text-hi) 8%, var(--sm-s1)); }
    button.ccat__cta:hover { background: var(--sm-btn-fill-hover); color: var(--sm-on-btn-fill); }
    .lb-panel .tier-table tbody tr:hover td { background: color-mix(in srgb, var(--sm-text-hi) 8%, transparent); color: var(--sm-text-hi); }
  }

  /* Partea de FORCED COLORS a taburilor de categorie: sublinierea e singurul semn al selectiei, iar
     modul cu culori fortate sterge culorile de chenar. Coborata din `@layer skin` la B159 L10 (F2). */
  @media (forced-colors: active) {
    .cat-tabs .nav-link:not(.active) { border-bottom-color: Canvas; }
    .cat-tabs .nav-link.active { border-bottom-color: Highlight; }
  }

/* Coborata din `@layer skin` la B159 L10 (F2). */
.entry-form .form-select:user-invalid { box-shadow: inset 0 0 0 1px var(--sm-danger); }
/* Coborata din `@layer skin` la B159 L10 (F2). */
.entry-form .form-select:focus { box-shadow: inset 0 0 0 1px var(--sm-text-hi); }
  /* Raza campurilor e o valoare de SITE: regula de baza sta in `tokens.css`, pe care il incarca si
     ecranul juratului, iar acolo `--sm-r-row` nu exista. `components.css` se incarca dupa, deci
     override-ul asta castiga pe site si lasa juratul cu raza lui Bootstrap. B159 L10 (F3). */
  .form-control, .form-select { border-radius: var(--sm-r-row); }

  /* Cifrele cu latime fixa pe toata pagina (coborat din `@layer skin` la B159 L10, F2). Foaia asta e
     incarcata doar de site, deci `body` neprefixat NU ajunge la ecranul juratului. */
  body { font-variant-numeric: tabular-nums; }
  /* Ultima declaratie care a mai purtat o valoare din `kf-skin.css` (B159 L10, F4). Body-ul acopera
     fereastra si cu continut scurt. Celelalte sase ale regulii `body.kf` erau identice cu ce da deja
     `html, body` din `tokens.css`, fiindca paleta remapeaza rolul kf si rolul de baza la acelasi
     token (`--kf-bg`/`--page-bg`, `--kf-text`/`--fg1`, `--kf-font`/`--font-sans`) — masurat, apoi
     sonda a confirmat pe 132 de variante. Neprefixata: foaia asta e doar de site. */
  body { min-height: 100dvh; }
  /* Confirmarile care au bifa in markup: ramane bifa, pleaca verdele brut. */
  .reg-done,
  .enf-opt.is-onfile > .enf-onfile .bi,
  .e-chip-del__tick { color: var(--sm-ok); }
}
