/* ════════════════════════════════════════════════════════════
   02-primitives.css

   Neonova primitiva: .puls, .neon-frame, .pill. Vse ostatni se z nich sklada.

   Cast verejneho stylu. Soubory se nactou v poradi podle nazvu
   (`assets.PUBLIC_CSS`) a kaskada zavisi na tom poradi - nemenit ho
   a nove pravidlo psat do souboru, kam patri obsahove.
   ════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════
   NEONOVÁ PRIMITIVA
   Tři prvky, ze kterých se skládá celý vzhled. Nekopírovat je do
   jednorázových pravidel — když se změní tloušťka linky v manuálu, musí
   stačit sáhnout na token v theme.css.
   ════════════════════════════════════════════════════════════ */

/* ── PULS ──
   Manuál: „Používá se jako sekundární grafický prvek za logem, buď ve spodku
   grafiky a nebo jako dělící čára nahoře. Šířka by měla být dvojnásobek
   podtržení loga."  V `PULS.svg` je to doslova: linka stroke 13, mezera 26.
   Proto `--puls-gap` v theme.css počítá z `--puls-w` a ne naopak.

   Skládá se z rovných úseků (.puls-rail, roztahují se) a pevného srdečního
   kmitu (.puls-beat). Mezera je JEDNA na celou čáru — proto je na
   .puls-break jako margin, ne `gap` na kontejneru, který by ji dal i před
   kmit a čára by se rozpadla na kusy.

   Poměry --f i geometrie kmitu jdou 1:1 z `PULS.svg` (viz makro
   partials/puls.html): zlom ve ~43 % šířky, kmit mezi 73 % a 84 %.
   Nejsou to hezká kulatá čísla schválně — vystředěný zlom vypadá jako chyba
   tisku, tenhle vypadá jako signál. */
.puls {
  display: flex; align-items: center; width: 100%;
  pointer-events: none;
}
.puls-rail {
  flex: var(--f, 1) 1 0;
  height: var(--puls-w);
  border-radius: var(--puls-w);
  background: var(--accent);
  box-shadow: var(--glow);
}
.puls-break { margin-left: var(--puls-gap); }
/* Strana privracena ke kmitu je NAPOJENI, ne konec linky: zaobleny konec by
   se od sikme cary odtahl a v miste spoje by zustaly dva vyrezy — presne ta
   mezera, kterou `PULS.svg` nema (tam je to jeden tah se `stroke-linejoin`). */
.puls-join-r { border-start-end-radius: 0; border-end-end-radius: 0; }
.puls-join-l { border-start-start-radius: 0; border-end-start-radius: 0; }
/* Kmit je ~27,7x vyssi nez linka (stroke 13 a viewBox 360,66 v `PULS.svg`).
   Drive tu bylo 22, cimz byl kmit o petinu mensi, nez ho kresli manual.
   Je o dost vyssi nez linka, takze VE VYCHOZIM STAVU si tu vysku
   v rozvrzeni vyhradi — jinak by prerostl pres text nad sebou a pod sebou. */
/* --beat-w / --beat-h nastavuje makro z `PULS.svg` (app/puls.py): vyska je
   pomer viewBoxu kmitu k tloustce tahu, sirka dopocet ze stejneho pomeru.
   Sirka nesmi byt `auto`, aby se dala ANIMOVAT (odjezd kmitu z hlavicky ho
   sraz k nule a linka se za nim zavre). */
/* Sirka jde pres `--beat-render-w`, ne primo z `--beat-w`: tutez hodnotu
   potrebuje i animace odjezdu, ktera misto po kmitu zavira zapornym okrajem.
   Dve mista s vlastnim vypoctem by se rozesla a v lince by po slozeni
   hlavicky zustala dira nebo prekryv. */
.puls-beat {
  flex: none;
  display: inline-block;
  --beat-render-w: calc(var(--puls-w) * var(--beat-w));
  height: calc(var(--puls-w) * var(--beat-h));
  width: var(--beat-render-w);
  color: var(--accent);
  /* Zar je tady, na obalovem <span>u, NE na <svg> pod nim. CSS `filter` na
     SVG prvku Chrome oriznul hranou jeho vlastniho viewBoxu bez ohledu na
     `overflow: visible` na tom SVG — mekky doznivajici stin zare tak konci
     ostrou hranou presne v obdelniku kmitu (viditelny "box" kolem nej).
     Na HTML prvku (span) tenhle orez neplati. */
  filter: var(--glow-filter);
}
/* SVG standardne obsah orizne hranou viewBoxu. Ten je vodorovne TESNY na
   krajni body kmitu (jinak by u napojeni vznikla mezera), takze kulaty
   konec tahu je presne na hrane a orez by ho sriznul na plocho — spoj s
   rovnou linkou by mel viditelnou hranu. `visible` ho necha prekryt pres
   linku, cimz vznikne presne to, co v `PULS.svg` kresli stroke-linejoin.
   Na rozvrzeni to nema vliv (presah se nepocita do sirky). */
.puls-beat-svg {
  display: block; width: 100%; height: 100%;
  overflow: visible;
}
/* `non-scaling-stroke` drží tloušťku kmitu přesně na --puls-w bez ohledu na
   to, jak je SVG vysoké. Bez něj by se stroke škáloval s viewBoxem a kmit by
   byl na desktopu tlustší než linka, na kterou navazuje. */
.puls-beat polyline {
  fill: none; stroke: currentColor; stroke-width: var(--puls-w);
  stroke-linecap: round; stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}
/* Puls jako dělič mezi sekcemi — tenčí, bez záře, aby nekradl pozornost
   tomu nahoře v hlavičce. */
.puls-divider { --puls-w: 3px; margin: clamp(2rem, 6vh, 3.5rem) 0; }
.puls-divider .puls-rail { box-shadow: var(--glow-2); }

/* ── RÁMEČEK S PŘERUŠENÍM ──
   Manuál: linka --frame-w, na každé straně jedno přerušení dlouhé dvojnásobek
   tloušťky linky. Kreslí se jako obyčejný `border` a čtyři „gumy" v ::after,
   ne jako čtyři gradienty — guma zvládne i zaoblený roh a je čitelná.

   Guma má barvu pozadí ZA rámečkem, takže ji prvek na jiné ploše musí
   přenastavit: `--frame-bg: var(--surface)`. Přerušení jsou schválně
   v různých místech hran (viz --frame-*-at) — čtyři vystředěné by z prvku
   udělaly ornament.

   KONCE LINKY U PŘERUŠENÍ JSOU KULATÉ — designér (kolo 6): „ty obrysy tech
   panelu aby to preruseni v nich bylo opravdu zakulaceny". Je to tentýž konec
   tahu, jaký má PULS (`border-radius` na `.puls-rail`) a `stroke-linecap:
   round` v `PULS.svg`; rámeček je ale `border`, a ten zaoblit nejde.
   Guma se proto kreslí o CELOU tloušťku linky delší (`--frame-hl` je její
   půlka) a do obou jejích konců se vrátí kotouč v barvě linky o poloměru
   `--frame-w / 2` = přesně kulatý konec. Dvě věci, na kterých to stojí:
   1. Vzdálenost VRCHOLŮ kotoučů zůstane `--frame-gap`, takže rozměr
      z manuálu (mezera = 2× tloušťka) se nemění, jen se jinak kreslí.
   2. Kotouče musí stát v `background` PŘED gumami — první vrstva je nahoře.
   Každá vrstva je celý pruh hrany (`100% var(--frame-w)`) a poloha přerušení
   je až v zarážkách gradientu: vrstvy různé velikosti by se při procentní
   `background-position` nedaly zarovnat na společný bod. */
.neon-frame {
  --frame-bg: var(--bg);
  /* Půlka díry v lince: viditelná mezera + dva kulaté konce (2 × poloměr). */
  --frame-hl: calc((var(--frame-gap) + var(--frame-w)) / 2);
  position: relative;
  border: var(--frame-w) solid var(--accent);
  border-radius: var(--radius-lg);
  box-shadow: 0 0 6px rgba(105, 188, 68, .55),
              0 0 20px rgba(105, 188, 68, .28),
              inset 0 0 8px rgba(105, 188, 68, .18);
}
.neon-frame::after {
  content: ''; position: absolute; pointer-events: none;
  inset: calc(var(--frame-w) * -1);
  background-repeat: no-repeat;
  background-image:
    /* osm kulatých konců (nad gumami) */
    radial-gradient(circle closest-side at calc(var(--frame-top-at) - var(--frame-hl)) 50%, var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-top-at) + var(--frame-hl)) 50%, var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) - var(--frame-hl)), var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) + var(--frame-hl)), var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) - var(--frame-hl)) 50%, var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) + var(--frame-hl)) 50%, var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) - var(--frame-hl)), var(--accent) 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) + var(--frame-hl)), var(--accent) 96%, transparent),
    /* čtyři gumy v barvě podkladu */
    linear-gradient(to right, transparent calc(var(--frame-top-at) - var(--frame-hl)), var(--frame-bg) 0, var(--frame-bg) calc(var(--frame-top-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-right-at) - var(--frame-hl)), var(--frame-bg) 0, var(--frame-bg) calc(var(--frame-right-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to right, transparent calc(var(--frame-bottom-at) - var(--frame-hl)), var(--frame-bg) 0, var(--frame-bg) calc(var(--frame-bottom-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-left-at) - var(--frame-hl)), var(--frame-bg) 0, var(--frame-bg) calc(var(--frame-left-at) + var(--frame-hl)), transparent 0);
  background-position:
    0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 0 0, 0 0,
    0 0, 100% 0, 0 100%, 0 0;
  background-size:
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%;
}
/* Tlumená varianta (`--soft`, 2px bez záře) tady bývala pro mřížky karet —
   z obavy, že šest zářících rámečků v jednom pohledu nevyzdvihne nic.
   Designér ji zamítl („ten neonovej rámeček na všechny ty panely"), takže
   mřížky i formulářové kroky jedou na `--open` a soft variantu už nic
   nepoužívá. Smazána, ať v souboru nezůstane pravidlo, které nikdo nevolá. */

/* Obsah rámečku se musí ořezat na jeho vnitřní poloměr, jinak roh fotky
   vykoukne přes zaoblení linky. */
.neon-frame > img,
.neon-frame > picture,
.neon-frame > .neon-frame-body {
  display: block; width: 100%;
  border-radius: calc(var(--radius-lg) - var(--frame-w));
  overflow: hidden;
}

/* ── PILULKA ──
   Základní interaktivní tvar celého webu: navigace, filtry, přepínače.
   Výplň je „Neon OFF" (--surface-2), text svítí. Plný zelený stav je
   vyhrazený AKTIVNÍ položce — když svítí všechny, nesvítí žádná. */
.pill {
  display: inline-flex; align-items: center; justify-content: center;
  gap: .4rem; height: 38px; padding: 0 1.15rem;
  border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--accent);
  border: 1px solid transparent;
  font: inherit;
  font-size: .93rem; font-weight: 500; line-height: 1;
  text-decoration: none; cursor: pointer; white-space: nowrap;
  /* Height/padding/font-size jsou tu kvuli SKLADANI HLAVICKY: pilulka se pri
     scrollu zmensi (32px / .84rem). Bez prechodu je to skok. */
  transition: background var(--d-hover) var(--ease-out),
              border-color var(--d-hover) var(--ease-out),
              box-shadow var(--d-hover) var(--ease-out),
              color var(--d-hover) var(--ease-out),
              transform var(--d-press) var(--ease-out),
              height .38s var(--ease-out),
              padding .38s var(--ease-out),
              font-size .38s var(--ease-out);
}
.pill:hover { background: var(--surface-3); border-color: var(--line-neon); }
.pill:active { transform: scale(.97); }
.pill.active,
.pill[aria-current="page"] {
  background: transparent; border-color: var(--accent);
  color: var(--accent); box-shadow: var(--glow-2);
}
/* Kruhová varianta (ikona bez textu) — sociální sítě, přepínač jazyka. */
.pill-icon { width: 38px; height: 38px; padding: 0; transition: width .38s var(--ease-out), height .38s var(--ease-out); }

/* PRŮHLEDNÁ VARIANTA — karta, která leží PŘÍMO na pozadí stránky.
   V návrhu (Figma, „design v2") mají karty sekcí zářící rámeček a ŽÁDNOU
   vlastní plochu: laserové paprsky pozadí jim prosvítají skrz. Plný
   `.neon-frame` to neumí — jeho přerušení kreslí čtyři „gumy" v barvě
   `--frame-bg`, takže by nad paprskem zůstaly čtyři viditelné obdélníčky
   (past popsaná v docs/claude/design.md). Tady se proto přerušení VYŘEZÁVÁ MASKOU, což
   je skutečná díra, a je jedno, co pod kartou leží.

   Dvě věci, na kterých to stojí:
   1. Záře jde přes `filter: drop-shadow` na kresbě rámečku, ne přes
      `box-shadow`. Maska ořezává i stín, takže box-shadow by se uřízl na
      hraně prvku a záře by zmizela úplně; filtr se počítá AŽ PO maskování,
      takže září přesně obrys s dírami.
   2. Rámeček kreslí `::before` (ne `border` prvku), aby ho maska mohla
      dostat samostatně — maska na samotném prvku by vyřezala i jeho obsah.
   `::after` s gumami se tím pádem nesmí kreslit vůbec.

   VSECHNY RAMECKY MAJI JEDNU TLOUSTKU LINKY (designer, kolo 7: „jeste bych
   je nejak unifikoval aby vsechny mely tu stejnou sirku … aby ta cara byla
   stejne siroka jako je na tom prvnim dva tymy, jedna arena"). Tahle varianta
   mela vlastni `--frame-w: 3px` a `--frame-gap: 16px`, takze karty sekci mely
   tenci linku a zaroven DELSI preruseni nez hero panel — dve odchylky v tom,
   co ma byt jeden tvar. Oboji je smazane a dedi se z `:root` (5px, mezera
   2x tloustka), vcetne `--frame-hl` pocitaneho na `.neon-frame`, se kterym se
   tahle trida vzdycky nosi. Nova varianta si tloustku NEPREPISUJE; jedina
   povolena odchylka je responzivni krok v `theme.css` (pod 760 px 3px), ktery
   plati na vsechny ramecky naraz, takze zustavaji unifikovane i tam.
   Rozdil proti plnemu ramecku tedy uz neni sila linky, ale ZPUSOB PRERUSENI:
   maska misto gum, tedy skutecna dira — proto ho nosi panely lezici primo na
   pozadi stranky. */
.neon-frame--open {
  position: relative;
  border: 0;
  background: transparent;
  box-shadow: none;
}
.neon-frame--open::after { content: none; }
.neon-frame--open::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  border: var(--frame-w) solid var(--frame-color, var(--accent));
  border-radius: inherit;
  filter: var(--glow-filter);
  /* NAD VŠÍM, co je v panelu. Panely s médiem přes celou plochu
     (`.occasion-photo`, `.gear-photo` a `.arena-photo` jsou `inset: 0`) by
     rámeček jinak překryly — kreslil by se, ale nebyl by vidět; `.gear-photo`
     má dokonce vlastní `z-index: 2`, takže by při shodě vyhrálo pořadí v DOM.
     Hodnota je schválně vyšší než cokoli uvnitř panelů (HUD chipy jedou na 3
     a 4): rámeček je HRANA panelu a nic uvnitř přes ni kreslit nemá. Zavírací
     křížky modalů jsou odsazené dovnitř, takže je linka u okraje nemíjí. */
  z-index: 5;
  /* Spodni vrstva je plna plocha, ctyri nad ni jsou diry v mistech preruseni
     (`--frame-*-at` sdili s plnym ramecekem, takze se pozice nemohou rozejit)
     a osm nejvyssich vraci do konců děr kulaté konce tahu — tentýž tvar, jaký
     ma PULS. `exclude` z der udela XOR = plocha bez der, `add` pak kotouce
     prida zpet; XOR by je u konce linky naopak ubral.
     Dira je o CELOU tloustku linky delsi nez viditelna mezera, aby po
     vraceni kotoucu zustala vzdalenost vrcholu presne `--frame-gap`.
     Vsechny vrstvy maji velikost celeho pruhu hrany a poloha preruseni je
     v zarazkach gradientu: vrstvy ruzne velikosti by se pri procentni
     `mask-position` nedaly zarovnat na spolecny bod. */
  -webkit-mask-image:
    radial-gradient(circle closest-side at calc(var(--frame-top-at) - var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-top-at) + var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) - var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) + var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) - var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) + var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) - var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) + var(--frame-hl)), #000 96%, transparent),
    linear-gradient(to right, transparent calc(var(--frame-top-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-top-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-right-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-right-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to right, transparent calc(var(--frame-bottom-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-bottom-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-left-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-left-at) + var(--frame-hl)), transparent 0),
    linear-gradient(#000 0 0);
  mask-image:
    radial-gradient(circle closest-side at calc(var(--frame-top-at) - var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-top-at) + var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) - var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-right-at) + var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) - var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at calc(var(--frame-bottom-at) + var(--frame-hl)) 50%, #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) - var(--frame-hl)), #000 96%, transparent),
    radial-gradient(circle closest-side at 50% calc(var(--frame-left-at) + var(--frame-hl)), #000 96%, transparent),
    linear-gradient(to right, transparent calc(var(--frame-top-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-top-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-right-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-right-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to right, transparent calc(var(--frame-bottom-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-bottom-at) + var(--frame-hl)), transparent 0),
    linear-gradient(to bottom, transparent calc(var(--frame-left-at) - var(--frame-hl)), #000 0, #000 calc(var(--frame-left-at) + var(--frame-hl)), transparent 0),
    linear-gradient(#000 0 0);
  -webkit-mask-position:
    0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 0 0, 0 0,
    0 0, 100% 0, 0 100%, 0 0,
    0 0;
  mask-position:
    0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 0 0, 0 0,
    0 0, 100% 0, 0 100%, 0 0,
    0 0;
  -webkit-mask-size:
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%,
    100% 100%;
  mask-size:
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), 100% var(--frame-w),
    var(--frame-w) 100%, var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%,
    100% var(--frame-w), var(--frame-w) 100%,
    100% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-composite:
    source-over, source-over, source-over, source-over,
    source-over, source-over, source-over, source-over,
    xor, xor, xor, xor,
    source-over;
  mask-composite:
    add, add, add, add, add, add, add, add,
    exclude, exclude, exclude, exclude,
    add;
  transition: filter var(--d-hover) var(--ease-out);
}
/* Obsah karty musí zůstat NAD kresbou rámečku (::before nemá z-index,
   takže by jinak ležel přes text). */
.neon-frame--open > * { position: relative; z-index: 1; }

/* STEJNA MEZERA OD LINKY K TEXTU NAHORE, DOLE I PO STRANACH (kolo 8).
   Padding muze byt stejny na vsech stranach, a presto vyjde nahore vetsi:
   radek textu je vyssi nez pismena (pulradek + misto nad verzalkami), takze
   se k paddingu nahore pricte ~6 px navic a dole dalsi pulradek. Designer to
   zakreslil do karty „Hlidej si zada" — linka→„04" nahore 38 px, vlevo 29.
   `text-box` ten prebytek u prvniho a posledniho radku panelu urizne:
   nahore na vysku verzalek, dole na uctari. Orez se propisuje i do
   vnorenych bloku (overeno v Chrome), takze staci na panelu samotnem —
   neplati jen pres flex/grid, proto flexove panely orezavaji deti (niz).
   Prohlizec bez `text-box` (Firefox) nakresli panel jako dřív: o pulradek
   vyssi nahore, nic se nerozbije. */
.feat-item, .step-cell, .info-cell, .pix-demo, .pix-picker, .vch-page .card {
  text-box: trim-both cap alphabetic;
}
.offer-card > :first-child { text-box: trim-start cap alphabetic; }
.offer-card > :last-child { text-box: trim-end cap alphabetic; }
