/* ═══════════════════════════════════════════════════════════════════════
   HVMBAK DRAPE — warstwa wizualna.

   Design system przeniesiony żywcem z HVMBAK EFFECTS: te same czcionki
   (Archivo Narrow + DM Sans), kliny „Cięcie" na przyciskach i czarna scena
   z fakturą płótna. Pasek i panel sterowania siedzą na „srebrnym" szarym
   z apki wideo (przepis 1:1: #8F8F8F + światło „Sztaba" + skaczące ziarno)
   i zlewają się w jedną taflę — bez linii między pasekiem a kolumną.
   ═══════════════════════════════════════════════════════════════════════ */

:root {
  /* Krój etykiet i przycisków (10.09.2026): ten sam krój (Inter), co przyciski
     i podpisy na landingu hvmbak.pl — wersaliki normalnej szerokości z rozstrzeleniem.
     Bierze go chrom interfejsu: przyciski, nazwy sekcji, nazwy suwaków i (od
     11.10.2026) człon „/DRAPE" przy logo. Zdania pomocy i stopka zostają na swoich.
     font-stretch: normal konieczne — body leci na kroju kondensowanym. */
  --font-etykieta: 'Inter', -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif;
  --blue: #A8C5DA;
  --blue-deep: #94B5CC;
  --black: #0D0D0D;
  --panel: #1A1A1A;
  --panel-lift: #242424;
  --paper: #F5F0EB;
  --paper-dim: rgba(245, 240, 235, 0.6);
  --line-dark: rgba(245, 240, 235, 0.09);
  --grain: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27140%27%20height%3D%27140%27%3E%3Cfilter%20id%3D%27n%27%20filterUnits%3D%27userSpaceOnUse%27%20x%3D%270%27%20y%3D%270%27%20width%3D%27140%27%20height%3D%27140%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.62%27%20numOctaves%3D%273%27%20seed%3D%2711%27%20stitchTiles%3D%27stitch%27%2F%3E%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%3CfeComponentTransfer%3E%3CfeFuncR%20type%3D%27linear%27%20slope%3D%271.35%27%20intercept%3D%27-0.175%27%2F%3E%3CfeFuncG%20type%3D%27linear%27%20slope%3D%271.35%27%20intercept%3D%27-0.175%27%2F%3E%3CfeFuncB%20type%3D%27linear%27%20slope%3D%271.35%27%20intercept%3D%27-0.175%27%2F%3E%3CfeFuncA%20type%3D%27linear%27%20slope%3D%270%27%20intercept%3D%271%27%2F%3E%3C%2FfeComponentTransfer%3E%3C%2Ffilter%3E%3Crect%20width%3D%27140%27%20height%3D%27140%27%20filter%3D%27url(%23n)%27%2F%3E%3C%2Fsvg%3E");
  --grain-size: 120px;
  --sheet: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27300%27%20height%3D%27300%27%3E%20%3Cfilter%20id%3D%27j%27%20x%3D%27-30%25%27%20y%3D%27-30%25%27%20width%3D%27160%25%27%20height%3D%27160%25%27%3E%20%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.012%200.9%27%20numOctaves%3D%272%27%20seed%3D%277%27%20result%3D%27n%27%2F%3E%20%3CfeDisplacementMap%20in%3D%27SourceGraphic%27%20in2%3D%27n%27%20scale%3D%273.2%27%2F%3E%20%3CfeGaussianBlur%20stdDeviation%3D%270.45%27%2F%3E%20%3C%2Ffilter%3E%20%3Cg%20filter%3D%27url%28%23j%29%27%20stroke%3D%27%23F5F0EB%27%20stroke-opacity%3D%270.05%27%20stroke-width%3D%271%27%20fill%3D%27none%27%3E%20%3Cpath%20d%3D%27M0%20150%20H300%27%2F%3E%3Cpath%20d%3D%27M150%200%20V300%27%2F%3E%20%3C%2Fg%3E%3C%2Fsvg%3E");

  /* Srebro paneli — przepis z HVMBAK EFFECTS VIDEO (decyzja Kuby
     06.08.2026): neutralny szary, tusz zamiast papieru na napisach,
     stal jako akcent liczb i aktywnych stanów. */
  --silver: #8F8F8F;
  --silver-lift: #9C9C9C;
  --m-ink: #141414;
  --m-ink-soft: rgba(13, 13, 13, 0.66);
  --m-ink-dim: rgba(13, 13, 13, 0.5);
  --m-line: rgba(13, 13, 13, 0.24);
  --steel: #3E6683;
  --grain-anim: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%27240%27%20height%3D%27240%27%3E%3Cfilter%20id%3D%27n%27%3E%3CfeTurbulence%20type%3D%27fractalNoise%27%20baseFrequency%3D%270.68%27%20numOctaves%3D%272%27%20stitchTiles%3D%27stitch%27%2F%3E%3CfeColorMatrix%20type%3D%27saturate%27%20values%3D%270%27%2F%3E%3C%2Ffilter%3E%3Crect%20width%3D%27100%25%27%20height%3D%27100%25%27%20filter%3D%27url%28%23n%29%27%2F%3E%3C%2Fsvg%3E");

  --bar-h: 60px;
  --col: 340px;

  /* Światło „Sztaba" BEZ filtra — do telefonu. To te same dwie belki co
     w .bar::before / .dock::before, tylko z rozmyciem 24 px policzonym
     z góry i wpisanym w przystanki gradientu (splot profilu z gaussem).
     Zmierzone na trzech szerokościach telefonu i przy obu krańcach dryfu:
     różnica względem wersji z filtrem to najwyżej 3 stopnie z 255.
     Po co: filtr na warstwie, która cały czas się przesuwa, karta graficzna
     telefonu liczy od nowa w każdej klatce; gradient nie kosztuje nic. */
  --sztaba-pieczona:
    linear-gradient(64deg, rgba(255,255,255,0) 29%, rgba(255,255,255,.001) 30%, rgba(255,255,255,.0022) 31%, rgba(255,255,255,.0044) 32%, rgba(255,255,255,.008) 33%, rgba(255,255,255,.0133) 34%, rgba(255,255,255,.0205) 35%, rgba(255,255,255,.0297) 36%, rgba(255,255,255,.0405) 37%, rgba(255,255,255,.0531) 38%, rgba(255,255,255,.0676) 39%, rgba(255,255,255,.0844) 40%, rgba(255,255,255,.1041) 41%, rgba(255,255,255,.1267) 42%, rgba(255,255,255,.1513) 43%, rgba(255,255,255,.1758) 44%, rgba(255,255,255,.197) 45%, rgba(255,255,255,.2116) 46%, rgba(255,255,255,.2168) 47%, rgba(255,255,255,.2116) 48%, rgba(255,255,255,.197) 49%, rgba(255,255,255,.1758) 50%, rgba(255,255,255,.1513) 51%, rgba(255,255,255,.1267) 52%, rgba(255,255,255,.1041) 53%, rgba(255,255,255,.0844) 54%, rgba(255,255,255,.0676) 55%, rgba(255,255,255,.0531) 56%, rgba(255,255,255,.0405) 57%, rgba(255,255,255,.0297) 58%, rgba(255,255,255,.0205) 59%, rgba(255,255,255,.0133) 60%, rgba(255,255,255,.008) 61%, rgba(255,255,255,.0044) 62%, rgba(255,255,255,.0022) 63%, rgba(255,255,255,.001) 64%, rgba(255,255,255,0) 65%),
    linear-gradient(52deg, rgba(255,255,255,0) 65%, rgba(255,255,255,.0004) 66%, rgba(255,255,255,.001) 67%, rgba(255,255,255,.0023) 68%, rgba(255,255,255,.0047) 69%, rgba(255,255,255,.0083) 70%, rgba(255,255,255,.0134) 71%, rgba(255,255,255,.0199) 72%, rgba(255,255,255,.0275) 73%, rgba(255,255,255,.0361) 74%, rgba(255,255,255,.0458) 75%, rgba(255,255,255,.0567) 76%, rgba(255,255,255,.0692) 77%, rgba(255,255,255,.0832) 78%, rgba(255,255,255,.0978) 79%, rgba(255,255,255,.111) 80%, rgba(255,255,255,.1205) 81%, rgba(255,255,255,.1239) 82%, rgba(255,255,255,.1203) 83%, rgba(255,255,255,.1107) 84%, rgba(255,255,255,.0971) 85%, rgba(255,255,255,.082) 86%, rgba(255,255,255,.0673) 87%, rgba(255,255,255,.0539) 88%, rgba(255,255,255,.0422) 89%, rgba(255,255,255,.0319) 90%, rgba(255,255,255,.0229) 91%, rgba(255,255,255,.0154) 92%, rgba(255,255,255,.0095) 93%, rgba(255,255,255,.0053) 94%, rgba(255,255,255,.0027) 95%, rgba(255,255,255,.0012) 96%, rgba(255,255,255,.0005) 97%, rgba(255,255,255,0) 98%);

  /* Geometria klina „Cięcie" — bez tych zmiennych clip-path jest nieważny
     i przyciski wychodzą płaskimi prostokątami. */
  --pinch: 9%;
  --lean: 7%;
  --bw: 1.5px;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

* { margin: 0; padding: 0; box-sizing: border-box; }
[hidden] { display: none !important; }
html { -webkit-text-size-adjust: 100%; }

body {
  background: var(--panel);
  color: var(--paper);
  font-family: 'Archivo Narrow', system-ui, 'DM Sans', sans-serif;
  font-stretch: extra-condensed;
  -webkit-font-smoothing: antialiased;
  height: 100svh;
  overflow: hidden;
}

.label {
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
/* scaleY(1.09) zdjęte 10.09.2026 razem z podmianą kroju: nadrabiało niższe litery
   Archivo Narrow, a nowy krój etykiet ma wersalik tej samej wysokości i na
   landingu stoi nierozciągnięty. */
.label, .control-name {
  display: inline-block;
  transform: none;
  transform-origin: center;
}

/* ═══════════════════════════════ POWŁOKA ════════════════════════════════ */

.workspace {
  height: 100svh;
  overflow: hidden;
  display: grid;
  grid-template-rows: var(--bar-h) minmax(0, 1fr);
  grid-template-columns: minmax(0, 1fr) var(--col);
  grid-template-areas:
    "bar   bar"
    "stage dock";
}
.workspace > * { animation: rise .6s var(--ease-out) backwards; }
.bar   { animation-delay: .02s; }
.stage { animation-delay: .08s; }
.dock  { animation-delay: .16s; }
@keyframes rise { from { opacity: 0; transform: translateY(12px); } }

/* ── pasek ─────────────────────────────────────────────────────────────── */

.bar {
  grid-area: bar;
  background: var(--silver);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  padding: 0 16px 0 14px;
  gap: 16px;
  z-index: 20;
  position: relative;
  overflow: clip;
}
.bar > * { position: relative; z-index: 1; }
.bar-brand {
  justify-self: start;
  display: flex;
  align-items: center;
  gap: 7px;
  text-decoration: none;
  padding: 6px 8px 6px 4px;
  border-radius: 8px;
  transition: background .3s var(--ease-out);
}
.bar-brand:hover { background: rgba(13, 13, 13, 0.08); }
.bar-logo { height: 30px; width: auto; display: block; user-select: none; -webkit-user-drag: none; }
/* Człon produktu ze slashem („/DRAPE"), od 11.10.2026 krojem etykiet (Inter,
   wersaliki) — 1:1 jak „/VIDEO" w apce wideo; mono odpadło (Kuba). Stoi na
   osi logo: wiersz ma wysokość kroju (line-height 1), a wersalik Intera leży
   dokładnie w środku takiego wiersza, więc środek liter wypada na środku
   liter logo bez ręcznych marginesów. To jest wzór na przedrostki rodziny. */
.bar-sub {
  align-self: center;
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-feature-settings: normal;
  font-size: 10px;
  font-weight: 600;
  line-height: 1;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(13, 13, 13, 0.8);
}
.bar-hint {
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--m-ink-dim);
  white-space: nowrap;
}

/* ═══════════════════════════════ SCENA ══════════════════════════════════ */

.stage {
  grid-area: stage;
  position: relative;
  min-width: 0;
  min-height: 0;
  background: var(--sheet) repeat, var(--black);
  overflow: hidden;
}
#gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  touch-action: none;
  cursor: grab;
}
#gl.is-dragging { cursor: grabbing; }

/* Napis na czas ładowania. Pierwszą sekundę jest niewidoczny (postój
   w klatkach kluczowych, nie animation-delay — skrypt zajęty startem
   nie może go opóźnić ani przyspieszyć), więc na dobrym łączu nikt go
   nie zobaczy. Usuwa go pierwsza narysowana klatka (app.js). */
.stage-wait {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  text-align: center;
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  line-height: 1.7;
  text-transform: uppercase;
  color: var(--paper-dim);
  pointer-events: none;
  z-index: 5;
  animation: czekanie 1.6s linear both;
}
@keyframes czekanie { 0%, 62% { opacity: 0; } 100% { opacity: 1; } }
.stage-wait.is-error { color: var(--paper); pointer-events: auto; cursor: pointer; animation: none; }

/* Strefa upuszczania: obrys w klinowym języku, widoczny nad canvasem. */
.stage.is-drop::after {
  content: 'UPUŚĆ PLAKAT TUTAJ';
  position: absolute;
  inset: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--blue);
  background: rgba(13, 13, 13, 0.55);
  color: var(--blue);
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  pointer-events: none;
  z-index: 10;
}

/* Drobna podpowiedź pod kontrolkami. */
.hint {
  font-family: 'Archivo Narrow', system-ui, sans-serif;
  font-size: 9px;
  line-height: 1.6;
  letter-spacing: 0.03em;
  color: rgba(13, 13, 13, 0.55);
}

/* ═══════════════════════════ PRAWA KOLUMNA ══════════════════════════════ */

.dock {
  grid-area: dock;
  min-height: 0;
  overflow: clip;
  background: var(--silver);
  color: var(--m-ink);
  position: relative;
}
.dock-scroll {
  height: 100%;
  overflow-y: auto;
  padding: 20px 18px 26px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  scrollbar-width: thin;
  scrollbar-color: rgba(13, 13, 13, 0.35) transparent;
  position: relative;
  z-index: 1;
}

/* Światło „Sztaba" (przepis 1:1 z apki wideo): dwie ukośne belki jak
   światło z okna — szeroka dryfuje ku lewemu górnemu rogowi, mniejsza
   siedzi przy prawym. Pod treścią, pod ziarnem.
   WAŻNE: obie warstwy mają WYMIAR EKRANU i są zakotwiczone do rogu
   viewportu (pasek: lewy górny; panel: prawy górny minus wysokość paska)
   — gradient liczony per-element dawał na 60-pikselowym pasku inny odcień
   niż na kolumnie i szew kłuł w oczy. Teraz to jedno pole światła,
   przycięte przez overflow obu elementów. */
.bar::before, .dock::before {
  content: '';
  position: absolute;
  width: 140vw;
  height: 140svh;
  z-index: 0;
  pointer-events: none;
  background:
    linear-gradient(64deg,
      transparent 34%, rgba(255,255,255,.10) 42%, rgba(255,255,255,.30) 47%,
      rgba(255,255,255,.10) 52%, transparent 60%),
    linear-gradient(52deg,
      transparent 70%, rgba(255,255,255,.07) 78%, rgba(255,255,255,.17) 82%,
      rgba(255,255,255,.07) 86%, transparent 93%);
  filter: blur(24px);
  animation: sztabaDryf 38s ease-in-out infinite alternate;
}
.bar::before { left: -20vw; top: -20svh; }
.dock::before { right: -20vw; top: calc(-20svh - var(--bar-h)); }
@keyframes sztabaDryf {
  0%   { transform: translate3d(9%, 7%, 0); }
  100% { transform: translate3d(-9%, -7%, 0); }
}

/* Ziarno: warstwa ponad treścią, przezroczysta dla wskaźnika. Skoki co
   klatkę zamiast płynnego ruchu — tak pracuje ziarno filmowe. */
.bar::after, .dock::after {
  content: '';
  position: absolute;
  inset: -60%;
  z-index: 7;
  pointer-events: none;
  background: var(--grain-anim) repeat, var(--grain-anim) repeat;
  background-size: 240px 240px, 460px 460px;
  opacity: 0.24;
  mix-blend-mode: overlay;
  will-change: transform;
  animation: ziarnoPracuje .45s steps(1) infinite;
}
@media (hover: none) {
  .bar::after, .dock::after {
    inset: -30%;
    mix-blend-mode: normal;
    opacity: 0.13;
    backface-visibility: hidden;
  }
}
@keyframes ziarnoPracuje {
  0%    { transform: translate3d(0, 0, 0); }
  12.5% { transform: translate3d(-8%, 5%, 0); }
  25%   { transform: translate3d(5%, -9%, 0); }
  37.5% { transform: translate3d(-4%, -6%, 0); }
  50%   { transform: translate3d(9%, 4%, 0); }
  62.5% { transform: translate3d(-9%, -2%, 0); }
  75%   { transform: translate3d(3%, 8%, 0); }
  87.5% { transform: translate3d(7%, -5%, 0); }
  100%  { transform: translate3d(0, 0, 0); }
}

.block { display: flex; flex-direction: column; gap: 12px; }
.block .label { color: var(--m-ink-dim); }
.block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
}
.block + .block { padding-top: 20px; border-top: 1px solid var(--m-line); }

.row { display: flex; gap: 8px; }
.row .tp { flex: 1; }

.dock-foot {
  margin-top: auto;
  padding-top: 20px;
  border-top: 1px solid var(--m-line);
  font-family: 'Archivo Narrow', system-ui, sans-serif;
  font-size: 9px;
  line-height: 1.7;
  letter-spacing: 0.02em;
  color: rgba(13, 13, 13, 0.45);
}

/* ═══════════ PRZYCISKI — system „Cięcie" (żywcem z EFFECTS) ═════════════ */

.tp {
  position: relative; isolation: isolate;
  border: 0; background: transparent;
  min-height: 36px; padding: 0 20px;
  display: inline-flex; align-items: center; justify-content: center;
  gap: 7px;
  cursor: pointer;
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 10px; font-weight: 600; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--paper); -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition: transform .3s var(--ease-out), color .25s;
}
.tp-label { display: inline-block; pointer-events: none; }   /* scaleY zdjęte 10.09.2026 razem z krojem */
.tp-ico { width: 15px; height: 15px; flex: none; pointer-events: none; }
.tp:active { transform: translateY(1px); }
.tp:focus-visible { outline: 2px solid var(--steel); outline-offset: 3px; }
.tp::before, .tp::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  clip-path: var(--shape);
  transition: background .25s var(--ease-out);
}
.tp::after { clip-path: var(--shape-in, var(--shape)); }

.tp--single {
  --shape: polygon(0 var(--lean), 100% 0, 100% 100%, 0 calc(100% - var(--lean)));
  --shape-in: polygon(var(--bw) calc(var(--lean) + var(--bw)), calc(100% - var(--bw)) var(--bw), calc(100% - var(--bw)) calc(100% - var(--bw)), var(--bw) calc(100% - var(--lean) - var(--bw)));
}
.tp--single-r {
  --shape: polygon(0 0, 100% var(--lean), 100% calc(100% - var(--lean)), 0 100%);
  --shape-in: polygon(var(--bw) var(--bw), calc(100% - var(--bw)) calc(var(--lean) + var(--bw)), calc(100% - var(--bw)) calc(100% - var(--lean) - var(--bw)), var(--bw) calc(100% - var(--bw)));
}
.tp--seg-l {
  --shape: polygon(0 0, 100% var(--pinch), 100% calc(100% - var(--pinch)), 0 100%);
  --shape-in: polygon(var(--bw) var(--bw), calc(100% - var(--bw)) calc(var(--pinch) + var(--bw)), calc(100% - var(--bw)) calc(100% - var(--pinch) - var(--bw)), var(--bw) calc(100% - var(--bw)));
}
.tp--seg-m {
  --shape: polygon(0 var(--pinch), 100% var(--pinch), 100% calc(100% - var(--pinch)), 0 calc(100% - var(--pinch)));
  --shape-in: polygon(var(--bw) calc(var(--pinch) + var(--bw)), calc(100% - var(--bw)) calc(var(--pinch) + var(--bw)), calc(100% - var(--bw)) calc(100% - var(--pinch) - var(--bw)), var(--bw) calc(100% - var(--pinch) - var(--bw)));
}
.tp--seg-r {
  --shape: polygon(0 var(--pinch), 100% 0, 100% 100%, 0 calc(100% - var(--pinch)));
  --shape-in: polygon(var(--bw) calc(var(--pinch) + var(--bw)), calc(100% - var(--bw)) var(--bw), calc(100% - var(--bw)) calc(100% - var(--bw)), var(--bw) calc(100% - var(--pinch) - var(--bw)));
}

/* Kliny na srebrze (przepis z apki wideo): obrys z tuszu, wypełnienie
   panelem; „solid" w kierunku „stary Windows" — delikatny rozbłysk od góry. */
.cut-ghost::before { background: rgba(13, 13, 13, 0.55); }
.cut-ghost::after { background: var(--silver); }
.stage .cut-ghost::after { background: var(--black); }
.cut-ghost { color: rgba(13, 13, 13, 0.85); }
.cut-ghost:hover::before { background: var(--m-ink); }
.cut-ghost:hover { color: var(--m-ink); }
.cut-solid::before { background: rgba(245, 240, 235, 0.45); }
.cut-solid::after {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.30), rgba(255, 255, 255, 0) 55%),
    var(--silver);
}
.cut-solid { color: var(--m-ink); }
.cut-solid:hover::before { background: rgba(245, 240, 235, 0.65); }
.cut-solid:hover::after {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.36), rgba(255, 255, 255, 0) 55%),
    var(--silver-lift);
}
.cut-solid:hover { color: var(--m-ink); }
.cut-seg::before, .cut-seg::after { background: transparent; }
.cut-seg { color: var(--m-ink-dim); }
.cut-seg:hover { color: var(--m-ink); }
.cut-seg.is-on::before, .cut-seg.is-on::after { background: var(--m-ink); }
.cut-seg.is-on { color: var(--paper); }

.tp--sm { min-height: 28px; padding: 0 12px; font-size: 9px; letter-spacing: 0.04em; }
.tp--wide { width: 100%; }
.tp:disabled { opacity: 0.45; cursor: default; pointer-events: none; }

/* Muszka trójdzielna (mocowanie, jakość) — środkowy segment prostokątny,
   skrajne pochylają się jak w dwudzielnym switchu EFFECTS. */
.seg3 { position: relative; isolation: isolate; display: flex; gap: 2px; }
.seg3::before {
  content: ''; position: absolute; inset: 0; z-index: -2;
  clip-path: polygon(0 0, 50% var(--pinch), 100% 0, 100% 100%, 50% calc(100% - var(--pinch)), 0 100%);
  background: rgba(13, 13, 13, 0.10);
}
.seg3 .tp { flex: 1; min-height: 28px; padding: 0 8px; font-size: 9px; }

/* Muszka trybu bierze całą szerokość kolumny. */
.tp-switch { position: relative; isolation: isolate; display: inline-flex; gap: 2px; }
.tp-switch::before {
  content: ''; position: absolute; inset: 0; z-index: -2;
  clip-path: polygon(0 0, 50% var(--pinch), 100% 0, 100% 100%, 50% calc(100% - var(--pinch)), 0 100%);
  background: rgba(13, 13, 13, 0.10);
}
.tp-switch--pelny { display: flex; }
.tp-switch--pelny .tp { flex: 1; }

/* Rząd presetów materiału: małe kliny na zmianę, jak stos warstw. */
.preset-row { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
.preset-row .tp { min-height: 30px; padding: 0 6px; font-size: 9px; }
.preset-row .tp:nth-child(odd)  { --shape: polygon(0 0, 100% var(--lean), 100% calc(100% - var(--lean)), 0 100%); --shape-in: polygon(var(--bw) var(--bw), calc(100% - var(--bw)) calc(var(--lean) + var(--bw)), calc(100% - var(--bw)) calc(100% - var(--lean) - var(--bw)), var(--bw) calc(100% - var(--bw))); }
/* Aktywny preset = baby blue jak w EFFECTS, ale PODŚWIETLONY jak
   cut-solid (zachwyt Kuby 10.08 nad „Wyprostuj plakat"): biała
   obramóweczka + rozbłysk od góry na błękicie — koniec z płaskim kaflem. */
.preset-row .tp.is-on { color: var(--m-ink); }
.preset-row .tp.is-on::before { background: rgba(245, 240, 235, 0.8); }
.preset-row .tp.is-on::after {
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.45), rgba(255, 255, 255, 0) 55%),
    var(--blue);
}

/* ── suwaki (kliny, jak w EFFECTS) ─────────────────────────────────────── */

.controls { display: flex; flex-direction: column; gap: 15px; }
.control { display: flex; flex-direction: column; gap: 7px; }
.control-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 9.5px;
  letter-spacing: 0.05em;
}
.control-name { color: var(--m-ink-soft); font-weight: 600; }
.control-value { color: var(--steel); font-variant-numeric: tabular-nums; }

.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 22px;
  background: transparent;
  cursor: pointer;
  touch-action: none;
}
.slider::-webkit-slider-runnable-track { height: 2px; border-radius: 2px; background: rgba(13, 13, 13, 0.3); }
.slider::-moz-range-track { height: 2px; border-radius: 2px; background: rgba(13, 13, 13, 0.3); }
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 6px; height: 17px;
  margin-top: -7.5px;
  border: 0; border-radius: 0;
  background: var(--steel);
  clip-path: polygon(0 0, 100% 0, 73% 100%, 27% 100%);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
  transition: transform .3s var(--ease-spring), background .25s;
}
.slider::-moz-range-thumb {
  width: 6px; height: 17px; border: 0; border-radius: 0;
  background: var(--steel);
  clip-path: polygon(0 0, 100% 0, 73% 100%, 27% 100%);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
@media (hover: hover) {
  .slider:hover::-webkit-slider-thumb { transform: scaleY(1.12); background: var(--m-ink); }
  .slider:hover::-moz-range-thumb { background: var(--m-ink); }
}
.slider:active::-webkit-slider-thumb { background: var(--m-ink); transform: scaleY(1.2); }
.slider:active::-moz-range-thumb { background: var(--m-ink); }

/* Próbnik koloru: pasek w tym samym rytmie co suwaki. */
.control-color input[type="color"] {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 24px;
  border: 1px solid rgba(13, 13, 13, 0.4);
  border-radius: 0;
  background: none;
  padding: 0;
  cursor: pointer;
}
.control-color input[type="color"]::-webkit-color-swatch-wrapper { padding: 2px; }
.control-color input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 0; }

/* ── komunikat ─────────────────────────────────────────────────────────── */

/* Toast w klinie „Cięcie" jak przyciski (dwuwarstwowy clip-path: ::before
   = obrys, ::after = czarne wypełnienie) — nie pastylka. Cienia nie ma,
   bo clip-path by go uciął; ramkę robi obrys. */
.toast {
  position: fixed;
  left: 50%; bottom: 26px;
  transform: translate(-50%, 24px);
  isolation: isolate;
  color: var(--paper);
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 13px 24px;
  opacity: 0;
  pointer-events: none;
  z-index: 60;
  transition: opacity .35s var(--ease-out), transform .45s var(--ease-spring);
}
.toast { --tlean: 8px; } /* stały skos w px — procent ginie przy szerokim pasku */
.toast::before, .toast::after {
  content: ''; position: absolute; inset: 0; z-index: -1;
  clip-path: polygon(0 var(--tlean), 100% 0, 100% 100%, 0 calc(100% - var(--tlean)));
}
.toast::before { background: rgba(245, 240, 235, 0.55); }
.toast::after {
  clip-path: polygon(var(--bw) calc(var(--tlean) + var(--bw)), calc(100% - var(--bw)) var(--bw), calc(100% - var(--bw)) calc(100% - var(--bw)), var(--bw) calc(100% - var(--tlean) - var(--bw)));
  background: var(--black);
}
.toast.is-on { opacity: 1; transform: translate(-50%, 0); }
.toast.is-error::before { background: var(--blue); }

/* ── nakładka awaryjna WebGL ───────────────────────────────────────────── */

.gl-overlay {
  position: absolute; inset: 0; z-index: 30;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 20px;
  padding: 24px;
  text-align: center;
  background: rgba(13, 13, 13, 0.92);
}
.gl-overlay-msg {
  max-width: 360px;
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.05em;
  line-height: 1.7;
  text-transform: uppercase;
  color: var(--paper);
}

/* ═══════════════════════════════ TELEFON ════════════════════════════════ */

.dock-swiatlo { display: none; } /* okno światła: tylko telefon, opis niżej */

@media (max-width: 720px) {
  /* Pasek 60 → 44 px i logo 30 → 22 px (jak w apce wideo): 16 px wysokości
     wraca do podglądu plakatu — scena ma teraz 52svh + 16 px. */
  :root { --bar-h: 44px; --stage-h: calc(52svh + 16px); }
  body, .workspace { height: auto; overflow: visible; }
  .workspace {
    min-height: 100svh;
    grid-template-columns: 1fr;
    grid-template-rows: var(--bar-h) auto auto;
    grid-template-areas: "bar" "stage" "dock";
  }
  /* Pasek odjeżdża z przewijaniem (jak w apce wideo), a PODGLĄD jest
     przypięty u góry — suwaki w panelu przewijasz POD żywym plakatem
     i cały czas widzisz, co robisz (uwaga Kuby 10.08).
     Pasek MUSI zostać position: relative. Stało tu „static" i przez to
     jego światło i ziarno (::before/::after, pozycjonowane absolutnie)
     traciły pasek jako punkt odniesienia: kładły się na CAŁĄ górę strony,
     NAD przyciskami panelu (belka światła urwana na wysokości „Animacji"),
     a że wystawały 128 px za prawą krawędź, strona dawała się na iPhonie
     przesuwać na boki. overflow: clip paska ich wtedy nie przycinał. */
  .bar { position: relative; }
  .bar-hint { display: none; }
  .bar-logo { height: 22px; }
  .bar-sub { font-size: 8.5px; }
  .stage {
    position: sticky;
    top: 0;
    z-index: 30;
    height: var(--stage-h);
    box-shadow: 0 7px 14px rgba(0, 0, 0, 0.26);
  }
  .dock { overflow: clip; }
  .dock-scroll { height: auto; overflow: visible; padding: 18px 16px 24px; }

  /* Światło na telefonie. Na komputerze panel ma wysokość ekranu, więc pole
     światła (.dock::before, 140svh) kryje go cały. Tu panel jest kolumną
     na kilka ekranów: pole kończyło się w połowie i belka światła była
     widać tylko na jednym odcinku (okolice „Animacji") — wyglądało to jak
     usterka. Teraz pole siedzi w OKNIE przyklejonym do ekranu (.dock-swiatlo,
     sticky): światło stoi w miejscu, a suwaki przesuwają się pod nim —
     tak samo jak na komputerze. Ujemne marginesy: okno zaczyna się na samej
     górze strony (nad panelem, pod paskiem i sceną), więc klei się od
     pierwszego piksela przewijania i nie zabiera miejsca w układzie.
     Okno wystaje po 150 px nad i pod ekran: gdyby iPhone przy szybkim
     przewijaniu spóźnił przyklejenie o klatkę, brzeg okna nie wejdzie
     w kadr. Pole liczone z lvh (największy ekran, pasek przeglądarki
     schowany), żeby belka nie urywała się u dołu po schowaniu paska.
     Pole bez filtra: --sztaba-pieczona. */
  .bar::before { filter: none; background: var(--sztaba-pieczona); }
  .dock::before { content: none; }
  .dock-swiatlo {
    display: block;
    position: sticky;
    top: -150px;
    z-index: 0;
    height: calc(100vh + 300px);
    height: calc(100lvh + 300px);
    margin-top: calc(-150px - var(--bar-h) - var(--stage-h));
    margin-bottom: calc(var(--bar-h) + var(--stage-h) - 150px - 100vh);
    margin-bottom: calc(var(--bar-h) + var(--stage-h) - 150px - 100lvh);
    overflow: clip;
    pointer-events: none;
  }
  .dock-swiatlo::before {
    content: '';
    position: absolute;
    left: -20vw;
    top: calc(150px - 20vh);
    top: calc(150px - 20lvh);
    width: 140vw;
    height: 140vh;
    height: 140lvh;
    background: var(--sztaba-pieczona);
    animation: sztabaDryf 38s ease-in-out infinite alternate;
  }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

/* ═══════════ Wyjścia do rodziny HVMBAK ═══════════
   Błękitna pikseloza (kafel na końcu panelu i belka pod sceną na telefonie,
   z 18.08.2026) wypadła 11.10.2026 — Kuba: „totalnie kasujemy". Zostają
   zwykłe odnośniki, ten sam wzór co w apce foto (.dock-linki): kreska u góry,
   nazwa krojem etykiet, strzałka przy prawej krawędzi. Rusza się tylko
   strzałka. Na komputerze jest jeszcze klin „Wszystkie narzędzia" w pasku. */
.hub-btn { text-decoration: none; justify-self: end; }

.dock-linki { display: flex; flex-direction: column; }
.dock-link {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 14px 0;
  border-top: 1px solid var(--m-line);
  font-family: var(--font-etykieta);
  font-stretch: normal;
  font-size: 11px; font-weight: 600; letter-spacing: 0.08em; line-height: 1.2;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--m-ink-soft);
  -webkit-tap-highlight-color: transparent;
  transition: color .2s;
}
.dock-link:focus-visible { outline: 2px solid var(--steel); outline-offset: 3px; }
.studio-strzalka {
  width: 13px; height: 13px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.5;
  transition: transform .3s var(--ease-out);
}
@media (hover: hover) {
  .dock-link:hover { color: var(--m-ink); }
  .dock-link:hover .studio-strzalka { transform: translateX(4px); }
}

@media (max-width: 720px) {
  .hub-btn { display: none; }
}
