public/css/style.css
Rama main · réplica de lectura del repositorio original.
/* style.css — Ham Radio Control UI v3.0 (kafelkowy layout + wysuwany panel RF) */
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Orbitron:wght@400;700;900&family=Inter:wght@300;400;500;600&display=swap');
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
/* ── Paleta "SDR Studio Pro" (przejete z forka 2026-10-16, na zadanie
uzytkownika "chce uzyskac tamten wyglad z nasza uzytecznoscia") —
gleboki granat/slate, elektryczny cyjan/blekit jako glowny akcent
(VFO, aktywne stany), bursztyn dla pasm/ostrzezen, rose-red dla
TX/PTT. Zastepuje poprzednia "natural sage" (oliwkowo-zielona). Nazwy
zmiennych BEZ ZMIAN — tylko wartosci — zeby wszystkie regulki
korzystajace z var(--green) itp. automatycznie dziedziczyly nowa
paleta. ── */
--bg: #070b14;
--panel: #0d1424;
--panel2: #131c31;
--panel3: #1c2944;
--border: rgba(var(--green-rgb),0.12);
--border2: rgba(var(--green-rgb),0.28);
--green: #38bdf8;
--green2: #0284c7;
--amber: #fbbf24;
--red: #f43f5e;
--dim: rgba(148,163,184,0.65);
--text: #f1f5f9;
/* Czarny tekst/obwodka dla wypelnionych (solid) elementow — przyciski
aktywne, kciuki sliderow, znaczniki — zapewnia kontrast na jasnym
tle akcentu (--green/--amber). */
--on-accent: #070b14;
/* Rozpakowane R,G,B (bez "rgba()") dla --green/--green2/--amber/--red -
pozwala inline-stylom i regulkom nizej budowac rgba(var(--green-rgb),
ALPHA) zamiast wpisywac na sztywno wartosci liczbowe. Dodane
2026-10-15: masowe podmienianie literalow na te zmienne to byl
wlasciwy fix na "kolory... malo widoczne" w jasnym motywie — bez
tego dziesiatki tel/obwodek odznak w ogole nie reagowaly na motyw. */
--green-rgb: 56,189,248;
--green2-rgb: 2,132,199;
--amber-rgb: 251,191,36;
--red-rgb: 244,63,94;
/* Rowek miernikow (S-metr/TX-metr) — celowo ciemniejszy niz --panel w
OBU motywach (wyglada jak wnetrze fizycznego wskaznika), nie po
prostu var(--panel3). */
--meter-track: #020617;
/* Pola tekstowe/liczbowe (RIT Hz, edycja makr CW, numery portow COM itp.)
uzywaly "var(--input-bg,#0a0a0a)" wszedzie - --input-bg nigdy nie byl
jawnie zdefiniowany, wiec kazde z tych pol ZAWSZE dostawalo ten sam
twardy fallback #0a0a0a bez wzgledu na motyw (live report 2026-10-16:
"caly blok frq jest na ciemno" - pole RIT Hz tuz przy VFO to jeden z
tych inputow). */
--input-bg: #0a101d;
/* --fg: uzywany w kilku inline-stylach (COM Bridge, PROFIL audio TEST,
itp.) - domyslnie = --text, chyba ze konkretny motyw (np.
amber-classic nizej) jawnie go nadpisuje. Bez tej linii --fg byl
NIGDZIE niezdefiniowany poza amber-classic, wiec w kazdym innym
motywie/trybie po prostu dziedziczyl --text przez CSS fallback -
dzialalo przypadkiem, ale jawna definicja jest jasniejsza i bezpieczniejsza. */
--fg: var(--text);
--mono: 'JetBrains Mono', 'Share Tech Mono', monospace;
--display: 'JetBrains Mono', 'Orbitron', monospace;
--ui: 'Plus Jakarta Sans', 'Inter', sans-serif;
}
html {
/* body.width jest ustawiane przez JS (updateScale) na scale*REF_W.
Wysokosc NIE jest sztywno ograniczana — strony dluzsze niz 1080px
(np. USTAWIENIA z wieloma sekcjami, CZAT z dluga historia) moga
przewijac sie wertykalnie (scroll na html/body), przy zachowanym
skalowaniu X/Y. Tlo (--bg) wypelnia pillarbox gdy proporcje viewportu
odbiegaja od 16:9.
scrollbar-gutter: stable — rezerwuje miejsce na pionowy scrollbar
ZAWSZE (nawet gdy strona nie przewija sie), zeby clientWidth (uzywane
w updateScale) bylo stale i nie zmienialo sie przy przelaczaniu
zakladek krotszych/dluzszych niz viewport — zapobiega "skakaniu"
calego layoutu o szerokosc scrollbara. */
background: #070b14;
scrollbar-gutter: stable;
}
html[data-mode="light"] { background: #f8fafc; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--ui);
/* width ustawiane dynamicznie przez JS (updateAppScale) na scale*REF_W —
dopasowane do #app-scale po transformacji. Wysokosc auto: pozwala na
scroll gdy zawartosc strony przekracza REF_H. */
}
/* ── #app-scale: caly UI w referencyjnym rozmiarze 1440px szerokosci ──
Wszystkie elementy wewnatrz zachowuja absolutnie identyczne proporcje
i pozycje wzgledem siebie na kazdym ekranie — JS aplikuje
transform:scale() (patrz <script> na koncu index.html) tak, aby
szerokosc #app-scale (1440px) zmiescila sie w viewporcie. Wysokosc
min 1080px, ale moze rosnac (strony z duza iloscia tresci) — w takim
przypadku strona przewija sie wertykalnie. */
#app-scale {
width: 1440px;
min-height: 1080px;
transform-origin: top left;
position: relative;
}
/* Nowoczesne, czyste tlo — bez artefaktow CRT (usuniete 2026-10-16 razem
z przejsciem na paleta "SDR Studio Pro", ktora celowo rezygnuje z
retro-skanlinii na rzecz czystego, nowoczesnego wygladu). */
body::after {
display: none;
}
/* ── TOPBAR ── */
/* "Opcja 2" (2026-10-16, user decision): .topbar zyje TERAZ wewnatrz
#app-scale (skaluje sie razem z reszta UI) i ma STALA wysokosc 52px,
bez position:sticky (i tak nie dzialal poprawnie na potomku
transform:scale — patrz historia tego pliku w git log). Dzieki stalej
wysokosci (gridH w index.html::updateScale() odejmuje dokladnie 52px)
caly widok RADIO/FT8 miesci sie w jednym ekranie bez przewijania
strony. Szerokosc 1440px (= REF_W) zamiast 100% — topbar jest teraz
childem #app-scale, ktory sam ma width:1440px, wiec musi dopasowac sie
do tej samej referencyjnej szerokosci zamiast do (nieskalowanego)
viewportu. */
.topbar {
background: var(--panel);
border-bottom: 1px solid var(--border2);
padding: 0 20px;
height: 52px;
box-sizing: border-box;
width: 1440px;
display: flex; align-items: center; justify-content: space-between; flex-wrap: nowrap; gap: 12px;
}
/* Presence bar usunieta — funkcje przeniesione do panelu OPERATORZY w tile-cw */
/* white-space:nowrap + flex-shrink:0 — bez tego logo lamalo sie na 3 linie
i bylo ucinane zamiast zostac w calosci na jednej linii. */
.logo { font-family: var(--ui); font-size: 12px; font-weight: 800; letter-spacing: 1px; color: var(--text); white-space: nowrap; flex-shrink: 0; }
.logo-accent { color: var(--green); text-shadow: 0 0 12px rgba(var(--green-rgb),0.4); }
.nav-tabs { display: flex; align-items: center; gap: 2px; flex-wrap: nowrap; flex-shrink: 1; min-width: 0; overflow: hidden; }
.tab-btn {
background: transparent; border: 1px solid transparent; padding: 4px 6px; border-radius: 5px;
color: var(--dim); font-family: var(--ui); font-size: 10px; font-weight: 600; cursor: pointer;
letter-spacing: 0.2px; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); flex-shrink: 0;
white-space: nowrap;
}
.tab-btn:hover { color: var(--text); background: var(--panel2); }
.tab-btn.active {
color: var(--green); background: rgba(var(--green-rgb),0.14); border: 1px solid rgba(var(--green-rgb),0.35);
box-shadow: 0 0 16px -2px rgba(var(--green-rgb),0.25); font-weight: 700;
}
/* Status-row + wylogowanie sa teraz grupowane w .topbar-right, zeby latwo
trzymac sie po prawej stronie stalej 52px-wysokiej belki, bez zawijania
(wrap nie jest juz potrzebny — belka ma stala wysokosc, wiec druga linia
po prostu zostalaby obcieta zamiast sie pokazac). */
.topbar-right { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.status-row { display: flex; align-items: center; flex-wrap: nowrap; justify-content: flex-end; gap: 10px; flex-shrink: 0; }
.indicator { display: flex; align-items: center; gap: 5px; }
.dot { width:8px; height:8px; border-radius:50%; background:var(--dim); }
.dot.green { background:var(--green); box-shadow:0 0 8px rgba(var(--green-rgb),0.7); animation:blink 2s ease-in-out infinite; }
.dot.red { background:var(--red); box-shadow:0 0 8px rgba(var(--red-rgb),0.7); }
.dot.amber { background:var(--amber); box-shadow:0 0 8px rgba(var(--amber-rgb),0.7); animation:blink 0.5s ease-in-out infinite; }
@keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.35} }
.logout-btn {
background: none; border: 1px solid var(--border); border-radius: 4px;
color: var(--dim); font-family: var(--mono); font-size: 11px; padding: 5px 10px;
cursor: pointer; transition: all 0.2s; flex-shrink: 0;
}
.logout-btn:hover { border-color: var(--red); color: var(--red); }
.lang-switch-btn {
background: none; border: 1px solid var(--border); border-radius: 4px;
color: var(--dim); font-family: var(--mono); font-size: 10px; font-weight: 700;
padding: 5px 8px; cursor: pointer; transition: all 0.2s; letter-spacing: 1px;
min-width: 28px;
}
.lang-switch-btn:hover { border-color: var(--green2); color: var(--green); }
/* ── RADIO PAGE WRAPPER ── */
#page-radio { padding: 0; position: relative; }
/* Sekcje (TX/RX/FILTR/AUDIO/RADIO) — uzywane w tile-left po przeniesieniu
z usunietego wysuwanego panelu RF/AF. */
.rf-section { padding: 8px 10px 4px; }
.rf-section-title {
font-family: var(--mono);
font-size: 8px;
color: var(--border2);
letter-spacing: 2px;
text-transform: uppercase;
padding-bottom: 4px;
border-bottom: 1px solid var(--border);
margin-bottom: 5px;
}
.rf-divider { height: 1px; background: var(--border); margin: 4px 10px; }
input[type="range"].rf-range {
-webkit-appearance: none; width: 100%; height: 3px;
background: var(--panel3); border-radius: 2px; outline: none; cursor: pointer; min-width: 0;
}
input[type="range"].rf-range::-webkit-slider-thumb {
-webkit-appearance: none; width: 11px; height: 11px; border-radius: 50%;
background: var(--green); cursor: pointer; box-shadow: 0 0 4px rgba(var(--green-rgb),0.4);
}
/* ── Kompaktowe kafelki sliderow (dynamiczne, RadioFunctions) ── */
/* Pojedyncza kolumna — kazdy slider w swoim wierszu, jeden pod drugim. */
#rig-dynamic-sliders {
display: flex;
flex-direction: column;
gap: 6px;
}
.rf-slider-tile {
background: var(--panel2);
border: 1px solid var(--border);
border-radius: 4px;
padding: 8px 10px;
}
.rf-slider-tile-head {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 4px;
}
.rf-slider-tile-left {
display: flex; align-items: center; gap: 5px;
font-family: var(--mono); font-size: 11px; color: var(--dim); letter-spacing: 0.5px;
overflow: hidden; white-space: nowrap;
}
.rf-slider-tile-left .ti { font-size: 13px; flex-shrink: 0; }
.rf-slider-tile-lbl { overflow: hidden; text-overflow: ellipsis; }
.rf-slider-tile-val {
font-family: var(--mono); font-size: 12px; color: var(--green);
flex-shrink: 0; margin-left: 6px;
}
/* Select BW i przyciski audio (przeniesione do tile-left) */
.rf-sub-lbl { font-family: var(--mono); font-size: 10px; color: var(--dim); letter-spacing: 1px; display: block; margin-bottom: 3px; }
.rf-section select {
width: 100%;
background: var(--panel2); border: 1px solid var(--border2); border-radius: 3px;
color: var(--green); font-family: var(--mono); font-size: 11px; padding: 5px 7px;
outline: none;
}
.audio-toggle-btn {
width: 100%; margin-top: 4px; padding: 7px;
background: rgba(var(--green-rgb),0.06); border: 1px solid var(--border); border-radius: 3px;
color: var(--dim); font-family: var(--mono); font-size: 11px; cursor: pointer; letter-spacing: 1px;
transition: all 0.15s;
}
.audio-toggle-btn:hover { color: var(--green); border-color: var(--green2); }
#wj-mute-btn.active { color: var(--red); border-color: var(--red) !important; background: rgba(var(--red-rgb),0.1); }
#wj-mute-btn { flex-shrink: 0; } /* see .wj-period-switch's comment - don't get squeezed by the TX-wait status pill */
/* ── Czarne obwodki na przyciskach/sliderach ──
Paleta sage jest spokojna/naturalna — czarna obwodka 1px na elementach
interaktywnych (przyciski, select, range input) daje wyrazna definicje
krawedzi bez dodawania jaskrawego koloru. Kolorowy border (--border2 itp.)
pozostaje przez box-shadow inset, wiec stan hover/active (zmiana
border-color na --green/--amber) wciaz jest widoczny jako wewnetrzny
pasek koloru, a zewnetrzna czarna linia daje kontrast wzgledem tla. */
button, select, input[type="range"].rf-range,
.step-btn, .mode-btn, .band-btn, .go-btn, .ptt-btn,
.btn-vfo-select, .btn-func-toggle, .btn-power-toggle,
.audio-toggle-btn, .tab-btn, .memory-del {
border-color: #000 !important;
box-shadow: inset 0 0 0 1px var(--border2);
}
button:hover, .step-btn:hover, .mode-btn:hover, .band-btn:hover,
.go-btn:hover, .ptt-btn:hover, .btn-vfo-select:hover, .btn-func-toggle:hover,
.btn-power-toggle:hover, .audio-toggle-btn:hover, .tab-btn:hover {
box-shadow: inset 0 0 0 1px var(--green);
}
.mode-btn.active, .band-btn.active, .step-btn.active, .tab-btn.active,
.btn-vfo-select.active, .btn-func-toggle.active, .btn-power-toggle.active {
box-shadow: inset 0 0 0 1px var(--green);
}
input[type="range"].rf-range::-webkit-slider-thumb {
border: 1px solid #000;
}
/* ── KAFELKOWY GRID ── */
/* grid-template-rows ma teraz 3 wiersze (nie 4) — rotator i CW keyer nie sa
juz osobnymi komorkami siatki, tylko jednym polaczonym kafelkiem
.tile-right (patrz index.html) zajmujacym cala wysokosc kolumny 4.
Wysokosc liczona z --radio-grid-h (ustawianej w JS, updateScale() w
index.html) zamiast sztywnego "calc(1080px - 52px)", zeby dokladnie
wypelnic dostepna wysokosc okna bez przewijania strony ("opcja 2",
2026-10-16). */
.tile-grid {
display: grid;
width: 100%;
grid-template-columns: 250px 1fr 1fr 250px;
grid-template-rows: auto auto minmax(0, 1fr);
gap: 1px;
background: var(--border);
height: var(--radio-grid-h, calc(1080px - 52px));
max-height: var(--radio-grid-h, calc(1080px - 52px));
overflow: hidden;
}
/* Pozycje kafelków */
.tile-left { grid-column: 1; grid-row: 1 / 4; }
.tile-vfo { grid-column: 2 / 4; grid-row: 1; }
.tile-rigfeatures{ grid-column: 2 / 4; grid-row: 2; }
.tile-wf { grid-column: 2 / 4; grid-row: 3; min-height: 0; }
.tile-right { grid-column: 4; grid-row: 1 / 4; }
.tile-rotator { font-size: 125%; background: var(--bg); }
.tile-cw { font-size: 125%; }
/* Wspólne style kafelka */
.tile {
background: var(--bg);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* ── BOX / HEADER (zachowane ze starego CSS) ── */
.box { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.box-header {
/* FIX: byl hardcoded rgba(19,28,49,0.7) - to akurat ciemny granat
(#131c31 = dzisiejsze --panel2 w trybie ciemnym), wiec w nocy wygladalo
ok, ale w jasnym motywie zostawalo tym samym ciemnym paskiem na bialym
tle .box. Zgloszone na zywo: "w trybie nocnym jest zgodne z kolorami
tryb dzienny juz nie" (okno LOG QSO / przyciski ADIF). */
padding: 8px 14px; background: var(--panel2); border-bottom: 1px solid var(--border);
font-family: var(--ui); font-size: 12px; font-weight: 700; letter-spacing: 1.5px; color: var(--dim);
text-transform: uppercase; display: flex; align-items: center; justify-content: space-between;
}
.box-body { padding: 12px; }
/* Nagłówek kafelka (bez ramki box) */
.tile-header {
background: var(--panel2);
padding: 5px 10px;
font-family: var(--mono);
font-size: 9px;
color: var(--dim);
letter-spacing: 2px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.tile-header-val { color: var(--green); font-size: 10px; }
.tile-divider { height: 1px; background: var(--border); flex-shrink: 0; }
/* ── LEWA KOLUMNA ── */
.tile-left { background: var(--bg); overflow-y: auto; overflow-x: hidden; min-height: 0; height: 100%; font-size: 125%; }
/* ── PRAWA KOLUMNA (rotator + CW keyer + czat w jednym, scrollowanym kafelku) ── */
.tile-right {
background: var(--panel);
overflow-y: auto;
overflow-x: hidden;
min-height: 0;
height: 100%;
}
/* Siatka Tryb / Pasmo — identyczny rozmiar */
.mode-band-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 3px;
padding: 7px 8px;
}
.mode-btn, .band-btn {
background: var(--panel2); border: 1px solid var(--border); border-radius: 3px;
color: var(--dim); font-family: var(--mono); font-size: 11px; padding: 7px 4px;
cursor: pointer; text-align: center; letter-spacing: 0.5px; transition: all 0.15s;
}
.mode-btn:hover { border-color: var(--border2); color: var(--text); }
.band-btn:hover { border-color: var(--border2); color: var(--text); }
.mode-btn.active { background: rgba(var(--green-rgb),0.12); border-color: var(--green); color: var(--green); }
.band-btn.active { background: rgba(var(--amber-rgb),0.10); border-color: var(--amber); color: var(--amber); }
/* Szybki Log QSO */
.qso-form {
padding: 8px;
display: flex;
flex-direction: column;
gap: 5px;
flex: 1;
}
.form-label { font-family: var(--mono); font-size: 9px; color: var(--dim); letter-spacing: 1px; margin-bottom: 2px; }
.form-row { display: flex; gap: 5px; }
.form-row input { flex: 1; }
.log-btn {
width: 100%; margin-top: auto;
background: rgba(var(--green-rgb),0.12); border: 1px solid var(--green2); border-radius: 4px;
color: var(--green); font-family: var(--mono); font-size: 10px; letter-spacing: 1px;
padding: 8px; cursor: pointer; transition: all 0.15s;
}
.log-btn:hover { background: rgba(var(--green-rgb),0.2); }
/* ── VFO ── */
.vfo-display {
text-align: left;
padding: 3px 10px 2px;
background: var(--panel);
border-bottom: 1px solid var(--border);
}
.vfo-label { font-family: var(--mono); font-size: 9px; letter-spacing: 2px; color: var(--dim); margin-bottom: 2px; }
.vfo-badges { display: flex; gap: 4px; margin-bottom: 4px; align-items: center; flex-wrap: wrap; }
.vfo-badge {
font-family: var(--mono); font-size: 9px; font-weight: 700; padding: 2px 7px;
border-radius: 4px; border: 1px solid; cursor: default;
}
.freq-big {
font-family: var(--mono); font-size: 32px; font-weight: 800;
color: var(--green); letter-spacing: -1px; line-height: 1;
text-shadow: 0 0 20px rgba(var(--green-rgb),0.5);
cursor: pointer; user-select: none;
}
.freq-big:hover { text-shadow: 0 0 30px rgba(var(--green-rgb),0.85); }
.freq-unit { font-family: var(--mono); font-size: 10px; font-weight: 700; color: var(--dim); margin-bottom: 2px; }
/* Kroki strojenia */
.tuning-steps { display: flex; gap: 2px; padding: 2px 0; flex-wrap: wrap; }
.step-btn {
/* FIX: bylo rgba(19,28,49,.7)/rgba(255,255,255,.08) na sztywno (wartosci
dobre tylko na ciemnym tle - w jasnym motywie dawaly ciemny prostokat
z prawie niewidoczna ramka) - patrz ten sam fix w .box-header. */
background: var(--panel2); border: 1px solid var(--border2); border-radius: 3px;
color: var(--dim); font-family: var(--mono); font-size: 9.5px; font-weight: 600; padding: 1px 6px;
cursor: pointer; transition: all 0.15s;
}
.step-btn:hover { background: var(--panel3); border-color: var(--green); color: var(--green); }
.step-btn:active { transform: scale(0.95); }
.step-btn.active { border-color: var(--green); color: var(--green); background: rgba(var(--green-rgb),0.16); font-weight: 700; }
.go-btn {
background: rgba(var(--green-rgb),0.12); border: 1px solid var(--green); border-radius: 4px;
color: var(--green); font-family: var(--mono); font-size: 12px; font-weight: 700; padding: 3px 8px;
cursor: pointer; flex-shrink: 0; transition: all 0.15s;
}
.go-btn:hover { background: rgba(var(--green-rgb),0.25); }
/* ── S-METER ── */
.smeter-wrap { padding: 3px 10px 3px; }
.smeter-scale {
display: flex; justify-content: space-between;
font-family: var(--mono); font-size: 8.5px; font-weight: 600; color: var(--dim); margin-bottom: 1px; padding: 0 2px;
}
.smeter-bar-wrap {
position: relative; height: 11px; background: var(--meter-track);
border: 1px solid rgba(255, 255, 255, 0.09); border-radius: 3px; overflow: hidden;
}
.smeter-fill {
height: 100%; width: 100%; border-radius: 2px;
background: linear-gradient(90deg,var(--green2) 0%,var(--green2) 55%,var(--amber) 55%,var(--amber) 75%,var(--red) 75%,var(--red) 100%);
clip-path: inset(0 100% 0 0); transition: clip-path 0.1s ease;
}
.smeter-value {
font-family: var(--mono); font-size: 11px; font-weight: 800;
color: var(--green); margin-top: 1px;
text-shadow: 0 0 10px rgba(var(--green-rgb),0.4);
}
/* ── TX Meter (ALC/PWR/SWR/VOLT) — bargraf kompaktowy pod S-metrem ──
Jeden wiersz: nazwa funkcji | pasek | wartosc. Wybor funkcji jest TYLKO
w lewej kolumnie (sekcja TRX FUNKCJE pod PASMO) — patrz UI.setTxMeter(). */
/* ── TX Meter (ALC/PWR/SWR/VOLT) — bargraf kompaktowy pod S-metrem ──
Jeden wiersz: nazwa funkcji | pasek+podzialka | wartosc. Wybor funkcji
jest TYLKO w lewej kolumnie (sekcja TRX FUNKCJE pod PASMO) — patrz
UI.setTxMeter(). Podzialka pod paskiem dopasowana do realnego zakresu
odczytu z radia, inna dla kazdej funkcji (patrz UI._renderTxMeter()). */
.txmeter-wrap {
display: flex; align-items: center; gap: 6px;
margin-top: 2px; padding-top: 2px;
border-top: 1px solid var(--border);
}
.txmeter-label-name {
font-family: var(--mono); font-size: 9px; font-weight: 700;
color: var(--dim); letter-spacing: 1px; flex-shrink: 0;
min-width: 24px;
}
.txmeter-bar-col { flex: 0 0 50%; display: flex; flex-direction: column; gap: 1px; }
.txmeter-bar-wrap {
position: relative; height: 8px; background: var(--meter-track);
border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 2px; overflow: hidden;
}
.txmeter-fill {
height: 100%; width: 0%;
border-radius: 2px;
/* Backend odswieza te mierniki co ~300ms (civ.py, petla pollera) — czas
przejscia dobrany blisko tego odstepu (nie krotszy), zeby pasek byl w
ciaglym ruchu miedzy odczytami zamiast skoku + zamrożenia (co dawalo
wrazenie "szarpanego" bargrafu przy krotszym 0.12s). */
transition: width 0.28s linear;
background: linear-gradient(90deg, var(--green2) 0%, var(--amber) 60%, var(--red) 85%);
}
/* Kolor bargrafów wg metra */
.txmeter-fill.alc { background: linear-gradient(90deg, #4a7a9a 0%, var(--amber) 70%, var(--red) 90%); }
.txmeter-fill.pwr { background: linear-gradient(90deg, var(--green2) 0%, var(--green) 55%, var(--amber) 75%, var(--red) 90%); }
.txmeter-fill.swr { background: linear-gradient(90deg, var(--green2) 0%, var(--green2) 40%, var(--amber) 60%, var(--red) 80%); }
.txmeter-fill.volt { background: linear-gradient(90deg, var(--red) 0%, var(--amber) 40%, var(--green2) 70%); }
/* Podzialka pod paskiem — pozycjonowanie absolutne (left:%), bo punkty SWR
(1.0/1.5/2.0/3.0) NIE sa rownomiernie rozstawione w skali procentowej
(patrz civ.py: pct = min(1.0, swr_raw/120), nieliniowe wzgledem wartosci). */
.txmeter-scale {
position: relative; height: 9px;
font-family: var(--mono); font-size: 8px; color: var(--dim);
}
.txmeter-scale span {
position: absolute; transform: translateX(-50%);
}
.txmeter-scale span:first-child { transform: translateX(0); }
.txmeter-scale span:last-child { transform: translateX(-100%); }
.txmeter-value {
font-family: var(--mono); font-size: 11px; color: var(--amber);
font-weight: 700; letter-spacing: 0.5px; flex-shrink: 0; min-width: 38px; text-align: right;
}
/* ── PTT ── */
.ptt-btn {
padding: 3px 48px; border-radius: 5px; border: 1px solid var(--border2);
background: var(--panel2); color: var(--text); font-family: var(--ui);
font-size: 12.5px; font-weight: 800; letter-spacing: 2px; cursor: pointer; transition: all 0.2s;
height: 25px;
}
.ptt-btn:hover { border-color: var(--green); color: var(--green); }
.ptt-btn.active {
background: rgba(var(--red-rgb),0.2); border-color: var(--red); color: var(--red);
box-shadow: 0 0 20px rgba(var(--red-rgb),0.3); animation: ptt-pulse 0.8s ease-in-out infinite;
}
.ptt-btn.crossband-blocked,
.ptt-btn:disabled {
background: rgba(var(--red-rgb),0.05); border-color: rgba(var(--red-rgb),0.35);
color: rgba(var(--red-rgb),0.55); cursor: not-allowed;
box-shadow: none; animation: none;
}
.ptt-btn.crossband-blocked:hover,
.ptt-btn:disabled:hover {
border-color: rgba(var(--red-rgb),0.35); color: rgba(var(--red-rgb),0.55);
}
/* Radio readonly mode — user nie ma TRX lock, moze tylko ogladac.
* Wyszarzamy wszystkie interaktywne kontrolki (przyciski, suwaki, pola input)
* poza panelem OPERATORZY (bo tam jest przycisk PRZEJMIJ TRX). Waterfall,
* S-meter, VFO display pozostaja pelnokolorowe (readonly nie ukrywa
* odczytow z radia, tylko blokuje ZMIANY). */
.radio-readonly button:not(.op-btn-take):not(.op-btn-request):not(.op-btn-release):not(.op-btn-force):not(#tab-radio):not(.tab-btn):not(.vfo-badge):not([id*="chat"]):not([id*="op-"]),
.radio-readonly input[type="range"],
.radio-readonly input[type="number"]:not([id*="chat"]),
.radio-readonly select:not([id*="chat"]) {
opacity: 0.4;
pointer-events: none;
filter: grayscale(0.6);
}
.radio-readonly .ptt-btn {
opacity: 0.35;
pointer-events: none;
filter: grayscale(0.8);
border-color: var(--dim) !important;
color: var(--dim) !important;
background: transparent !important;
box-shadow: none !important;
animation: none !important;
}
/* Makra CW (F1-F6) sa <div onclick=...>, nie <button> — selektor
* ".radio-readonly button:..." powyzej ich nie lapie, wiec wygladaly na
* aktywne mimo ze backend i tak odrzuca wysylke (/api/cw/send) gdy ktos
* inny trzyma radio. Wykryte w audycie zakladki RADIO 2026-08-15. */
.radio-readonly .cw-macro-card {
opacity: 0.4;
pointer-events: none;
filter: grayscale(0.6);
}
/* Zasada blokady: kto NIE trzyma TRX jest traktowany prawie jak viewer —
* ALE zawsze ma dostep do wlasnego logu QSO (i do zakladki Ustawienia, ktora
* jest osobna strona spoza #page-radio i tak nigdy nie byla dotykana przez
* .radio-readonly). Sam pasek "LOG QSO" jest jedynym elementem logu we-
* wnatrz zakladki RADIO, wiec wylaczamy go z ogolnej blokady przyciskow
* powyzej. Pola tekstowe (CALL/RST/GRID) i tak nigdy nie byly blokowane —
* ogolna reguła gasi tylko input[type=number/range] i select. 2026-08-15. */
.radio-readonly #quick-log-bar button {
opacity: 1;
pointer-events: auto;
filter: none;
}
/* Wyraznie oznaczmy panel OPERATORZY zeby user wiedzial gdzie kliknac PRZEJMIJ */
.radio-readonly #op-list {
border: 1px solid var(--amber);
background: rgba(var(--amber-rgb),0.05);
animation: readonly-highlight 2.5s ease-in-out infinite;
}
@keyframes readonly-highlight {
0%,100% { box-shadow: 0 0 0 rgba(var(--amber-rgb),0); }
50% { box-shadow: 0 0 12px rgba(var(--amber-rgb),0.35); }
}
/* Odblokuj przyciski PRZEJMIJ/POPROŚ/ODDAJ zeby user mogl przejac radio */
.radio-readonly #op-btn-take,
.radio-readonly #op-btn-request,
.radio-readonly #op-btn-release,
.radio-readonly #op-btn-force {
opacity: 1 !important;
pointer-events: auto !important;
filter: none !important;
}
/* ── Waterfall fullscreen ── */
.tile-wf.wf-fullscreen {
position: fixed !important;
top: 0 !important; left: 0 !important;
width: 100vw !important; height: 100vh !important;
z-index: 9999 !important;
padding: 20px !important;
background: #000 !important;
display: flex !important;
flex-direction: column !important;
}
.tile-wf.wf-fullscreen canvas#wf-waterfall { max-height: none !important; flex: 1 !important; }
.tile-wf.wf-fullscreen canvas#wf-spectrum { height: 120px !important; }
/* ── Waterfall collapsed (np. rig bez CI-V scope'u - IC-746 itp.) ──
Ukrywa tylko canvasy - tile-wf zostaje w TYM SAMYM miejscu siatki
(grid-column/grid-row bez zmian), wiec zadne inne kafelki sie nie
przesuwaja. Naglowek (span/peak/badge/S-metr/przyciski) zostaje
widoczny, zeby dalo sie jednym klikiem rozwinac z powrotem. */
.tile-wf.wf-collapsed canvas#wf-spectrum,
.tile-wf.wf-collapsed canvas#wf-waterfall,
.tile-wf.wf-collapsed canvas#wf-axis { display: none; }
/* ── Motywy kolorów (data-theme na <html>) ── */
/* Domyślny: green-amber (IC-7300 feel) — juz w :root */
html[data-theme="blue"] {
--green: #6db4e5;
--green2: rgba(109,180,229,0.4);
--amber: #f0b429;
}
html[data-theme="mono"] {
--green: #e0e0e0;
--green2: rgba(224,224,224,0.4);
--amber: #c0c0c0;
--red: #ff8080;
}
html[data-theme="amber-classic"] {
--green: #ffb84d;
--green2: rgba(255,184,77,0.4);
--amber: #ff8c42;
--fg: #ffb84d;
--text: #ffdba0;
}
/* ── Jasny motyw (dzien/noc, approved 2026-10-15) ─────────────────────────
Live report: "na tym co jest slabo widac" (niektore teksty/panele slabo
widoczne) - niektorzy userzy wola jasne tlo. Zachowujemy TA SAMA
tozsamosc kolorystyczna ("natural sage": szalwiowa zielen / bursztyn /
czerwien) dla motywu DOMYSLNEGO - zmieniaja sie neutralne kolory tla/
tekstu (--bg, --panel*, --text, --dim, --border*) ORAZ akcenty (--green/
--green2/--amber/--red, przeliczone na ciemniejsze/bardziej nasycone
odcienie tej samej barwy - te same pastelowe wartosci z ciemnego motywu
mialyby ZA MALY kontrast na jasnym tle, czyli dokladnie ten sam problem
widocznosci co user zglasza, tylko w nowym miejscu).
PRZELACZANE przez toggle w theme.js, atrybut data-mode (NIE data-theme!)
- localStorage 'ham_theme', jak PL/EN w i18n.js - domyslnie ciemny
(obecny wyglad, bez zmian dla kogos kto nic nie kliknie).
FIX KRYTYCZNY (2026-10-15, live report "przestaly dzialac kolory
bursztyn/crt"): ten blok pierwotnie uzywal TEGO SAMEGO atrybutu
data-theme co blok "Motywy kolorow" nad tym komentarzem (blue/mono/
amber-classic, wybierane w PROFIL -> UI.setTheme, zapisywane w
localStorage per-user jako "theme_<uid>") - obie funkcje (jasny/ciemny
motyw i wybor wariantu koloru) walczyly o jeden atrybut na <html> i sie
nawzajem nadpisywaly (theme.js'owy MutationObserver non-stop resetowal
wybrany przez usera wariant z powrotem na light/dark). Rozdzielone na
dwa niezalezne atrybuty: data-theme = wariant koloru (ui.js), data-mode
= jasny/ciemny (theme.js) - teraz moga dzialac jednoczesnie.
Ten blok jest CELOWO PO blokach wariantow kolorow wyzej (ta sama
specificznosc selektora :root[attr], remis idzie do kolejnosci w
pliku) - w trybie jasnym kazdy wariant koloru (blue/mono/amber-classic)
wraca do czytelnej domyslnej palety zamiast zostawiac blade kolory
dobrane pod ciemne tlo nieczytelne na jasnym - to jedyny rozsadny
kompromis bez recznego dostrajania osobnej jasnej wersji kazdego z 3
wariantow.
UWAGA (znane ograniczenie): sporo odznak/plakietek w index.html ma
WPISANY NA SZTYWNO kolor tla (np. "background:rgba(var(--amber-rgb),0.2)"
zamiast var(--amber)) - te tla NIE zmienia sie w jasnym motywie (zostaja
w odcieniu dobranym pod ciemne tlo), tylko sam tekst (ktory juz uzywa
var(--amber) itp.) sie przelacza - drobna kosmetyczna niespojnosc,
zaakceptowana zamiast przepisywania dziesiatek inline-stylow. Wodospad/
scope/kompas rotora (canvas, nie CSS) CELOWO zostaja ciemne w obu
motywach - to wykresy RF, nie tekst, i tak jest w kazdym programie SDR. */
:root[data-mode="light"] {
/* Jasny wariant palety "SDR Studio Pro" (przejety z forka 2026-10-16,
razem z ciemnym wariantem powyzej). --dim jest tu PELNYM kolorem
(nie rgba() na przezroczystosci) - fork tak to rozwiazal, i daje to
wystarczajacy kontrast bez potrzeby osobnego dostrajania alpha jak w
poprzedniej (sage) palecie. */
--bg: #f8fafc;
--panel: #ffffff;
--panel2: #f1f5f9;
--panel3: #e2e8f0;
--border: rgba(15,23,42,0.09);
--border2: rgba(15,23,42,0.20);
--green: #0284c7;
--green2: #0369a1;
--amber: #d97706;
--red: #dc2626;
--dim: #64748b;
--text: #0f172a;
--on-accent: #ffffff;
--fg: var(--text);
--green-rgb: 2,132,199;
--green2-rgb: 3,105,161;
--amber-rgb: 217,119,6;
--red-rgb: 220,38,38;
--meter-track: #e2e8f0;
--input-bg: #ffffff;
}
@media (prefers-color-scheme: light) {
/* Tylko gdy user NIGDY nie kliknal przelacznika (brak zapisu w
localStorage) - JS (theme.js) i tak ustawia data-mode explicite na
starcie, wiec to tylko fallback zanim JS zdazy sie wykonac. */
:root:not([data-mode]) {
--bg: #f8fafc;
--panel: #ffffff;
--panel2: #f1f5f9;
--panel3: #e2e8f0;
--border: rgba(15,23,42,0.09);
--border2: rgba(15,23,42,0.20);
--green: #0284c7;
--green2: #0369a1;
--amber: #d97706;
--red: #dc2626;
--dim: #64748b;
--text: #0f172a;
--on-accent: #ffffff;
--fg: var(--text);
--green-rgb: 2,132,199;
--green2-rgb: 3,105,161;
--amber-rgb: 217,119,6;
--red-rgb: 220,38,38;
--meter-track: #e2e8f0;
--input-bg: #ffffff;
}
}
@keyframes ptt-pulse { 0%,100%{box-shadow:0 0 20px rgba(var(--red-rgb),0.3)} 50%{box-shadow:0 0 35px rgba(var(--red-rgb),0.6)} }
/* Neon text-shadow glows (big blur radii, designed for a dark background -
the VFO digits, S-meter reading, logo accent) read as thick/blurry
rather than crisp on a LIGHT panel instead of adding a "CRT glow" effect
- live report 2026-10-16: "czcionka nadal malo czytelna kolorystycznie"
(after the --dim contrast fix above, this was the other half of it).
Turned off in light mode; dark mode keeps every glow exactly as-is. */
:root[data-mode="light"] .freq-big,
:root[data-mode="light"] .freq-big:hover,
:root[data-mode="light"] .smeter-value,
:root[data-mode="light"] .logo-accent,
:root[data-mode="light"] .vfo-digit.active:hover,
:root[data-mode="light"] .vfo-digit.selected,
:root[data-mode="light"] .vfo-arrow:hover {
text-shadow: none;
}
/* ── PANEL FUNKCJI RADIA (VFO A/B, func toggle) ── */
.tile-rigfeatures { padding: 0; }
/* Jeden zwarty wiersz przyciskow (VFO A/B, Zasilanie, funkcje) miedzy PTT
i waterfallem. Nie zawija sie — w razie nadmiaru elementow przewija
horyzontalnie (scrollbar maskowany, widoczny tylko na hover/drag). */
.rf-buttons-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 3px;
padding: 2px 8px;
}
/* Wszystkie grupy/przyciski trzymaja sie jednego wiersza (scroll-x w razie
przepelnienia) — nie kurcza sie i nie zawijaja. */
.rf-buttons-row .rig-action-group,
.rf-buttons-row .btn-vfo-select,
.rf-buttons-row .btn-power-toggle,
.rf-buttons-row .btn-func-toggle { flex-shrink: 0; white-space: nowrap; }
/* Zunifikowany przycisk dla rf-buttons-row — jeden rozmiar dla wszystkich */
.rf-btn {
/* FIX: bylo rgba(19,28,49,.75)/rgba(255,255,255,.08) na sztywno - patrz
ten sam fix w .box-header (dobre tylko na ciemnym tle). */
padding: 3px 9px; border-radius: 4px; border: 1px solid var(--border2);
background: var(--panel2); color: var(--dim); font-family: var(--mono);
font-size: 10px; font-weight: 600; letter-spacing: 0.5px; cursor: pointer; transition: all 0.15s;
white-space: nowrap; flex-shrink: 0;
}
.rf-btn:hover { border-color: var(--green); color: var(--green); background: var(--panel3); }
.rf-btn.active {
background: rgba(var(--green-rgb), 0.18); border-color: var(--green); color: var(--green);
box-shadow: 0 0 10px rgba(var(--green-rgb), 0.25); font-weight: 700;
}
.rf-btn-power { color: var(--green); }
.rf-btn-power:hover { border-color: var(--red); color: var(--red); }
.rf-btn-power.active {
background: rgba(var(--red-rgb),0.18); border-color: var(--red); color: var(--red);
box-shadow: 0 0 10px rgba(var(--red-rgb), 0.3);
}
/* Zachowane dla kompatybilnosci wstecznej */
.btn-vfo-select, .btn-func-toggle, .btn-power-toggle { padding: 6px 15px; border-radius: 6px; border: 1px solid var(--border2); background: var(--panel2); color: var(--dim); font-family: var(--mono); font-size: 12px; font-weight: 600; letter-spacing: 0.5px; cursor: pointer; transition: all 0.15s; }
.btn-vfo-select:hover, .btn-func-toggle:hover { border-color: var(--green); color: var(--green); background: var(--panel3); }
.btn-vfo-select.active, .btn-func-toggle.active { background: rgba(var(--green-rgb),0.18); border-color: var(--green); color: var(--green); box-shadow: 0 0 10px rgba(var(--green-rgb), 0.25); font-weight: 700; }
.btn-power-toggle { color: var(--green); }
.btn-power-toggle:hover { border-color: var(--red); color: var(--red); }
.btn-power-toggle.active { background: rgba(var(--red-rgb),0.18); border-color: var(--red); color: var(--red); }
/* ── WATERFALL ── */
.wf-header {
background: var(--panel2); padding: 3px 10px;
display: flex; justify-content: space-between; align-items: center;
border-bottom: 1px solid var(--border); flex-shrink: 0;
}
#wf-spectrum { width: 100%; flex: 0 0 clamp(60px, 30%, 110px); min-height: 50px; max-height: 110px; display: block; background: #060806; }
#wf-axis { width: 100%; height: 20px; display: block; background: #0a0c0a; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); flex-shrink: 0; }
#wf-waterfall { width: 100%; display: block; background: #060806; flex: 1 1 0; min-height: 60px; }
/* ── PASEK SOFTKEYS (ekranowe przyciski radia, jak w IC-7300) ── */
.wf-softkeys {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 3px;
padding: 3px 5px;
background: var(--panel2);
border-top: 1px solid var(--border);
flex-shrink: 0;
}
.wf-softkey-btn {
background: var(--panel3);
border: 1px solid var(--border2);
border-radius: 4px;
color: var(--dim);
font-family: var(--mono);
font-size: 10px;
font-weight: 700;
padding: 4px 2px;
text-align: center;
cursor: pointer;
transition: all 0.15s;
user-select: none;
white-space: nowrap;
}
.wf-softkey-btn:hover { border-color: var(--green); color: var(--text); }
.wf-softkey-btn.active {
background: rgba(var(--green-rgb),0.18);
border-color: var(--green);
color: var(--green);
box-shadow: inset 0 0 6px rgba(var(--green-rgb),0.25);
}
/* SPAN jako rozwijane menu zamiast przycisku-cyklu - zgloszone na zywo:
"span na waterfallu nie przelacza sie normalnie" (klikanie przez 7
wartosci kazdorazowo bylo niewygodne - teraz wybor od razu). */
.wf-softkey-select {
background: var(--panel3);
border: 1px solid var(--border2);
border-radius: 4px;
color: var(--green);
font-family: var(--mono);
font-size: 10px;
font-weight: 700;
padding: 4px 2px;
text-align: center;
text-align-last: center;
cursor: pointer;
transition: all 0.15s;
}
.wf-softkey-select:hover { border-color: var(--green); }
/* ── ROTATOR ── */
.rotator-inner { padding: 4px 10px 6px; }
#rot-compass { width: 205px !important; height: 205px !important; }
.rot-az-display { font-family: var(--display); font-size: 45px; font-weight: 900; color: var(--green); text-align: center; line-height: 1; }
.rot-unit-label { font-family: var(--mono); font-size: 9px; color: var(--dim); text-align: center; margin-bottom: 6px; }
.rot-target-inp {
width: 100%; margin-bottom: 5px;
background: var(--panel2); border: 1px solid var(--border2); border-radius: 3px;
color: var(--text); font-family: var(--mono); font-size: 11px; padding: 5px 8px; outline: none;
}
.rot-btns { display: flex; gap: 4px; }
.rot-go-btn {
flex: 1; background: rgba(var(--green-rgb),0.1); border: 1px solid var(--green2);
border-radius: 3px; color: var(--green); font-family: var(--mono); font-size: 12px;
padding: 9px; cursor: pointer; letter-spacing: 1px; transition: all 0.15s;
}
.rot-stop-btn {
flex: 1; background: rgba(var(--red-rgb),0.08); border: 1px solid rgba(var(--red-rgb),0.3);
border-radius: 3px; color: var(--red); font-family: var(--mono); font-size: 10px;
padding: 7px; cursor: pointer; letter-spacing: 1px; transition: all 0.15s;
}
/* ── CW KEYER ── */
.cw-macros-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 3px;
padding: 6px 8px;
}
.cw-macro-card {
background: var(--panel2); border: 1px solid var(--border); border-radius: 3px;
padding: 5px 7px; cursor: pointer; transition: all 0.12s;
min-height: 38px; max-height: 52px; overflow: hidden;
display: flex; flex-direction: column; gap: 2px; position: relative;
}
.cw-macro-card:hover { border-color: var(--green2); background: rgba(var(--green-rgb),0.06); }
.cw-macro-card.empty { border-style: dashed; opacity: 0.5; }
.cw-macro-card.empty:hover { opacity: 0.8; }
.cw-macro-header {
display: flex; align-items: center; gap: 4px;
}
.cw-macro-label {
font-family: var(--mono); font-size: 8px; color: var(--amber);
letter-spacing: 0.5px; display: block; text-transform: uppercase;
}
.cw-macro-text {
font-family: var(--mono); font-size: 8px; color: var(--dim);
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
flex: 1; min-width: 0; display: block;
}
.cw-macro-num { font-family: var(--mono); font-size: 8px; color: var(--dim); }
.cw-macro-edit-btn {
display: none; margin-left: auto;
background: transparent; border: 1px solid var(--border2); border-radius: 2px;
color: var(--dim); font-size: 9px; line-height: 1; padding: 0px 3px; cursor: pointer;
}
.cw-macro-edit-btn:hover { color: var(--amber); border-color: var(--amber); }
.cw-macro-card:hover .cw-macro-edit-btn { display: inline-block; }
/* ── TX VU METER + AUDIO SLIDERS ── */
#tx-vu-bar {
flex: 1; height: 6px; background: var(--panel3);
border-radius: 3px; overflow: hidden; position: relative;
}
#tx-vu-fill {
height: 100%; width: 0%; background: var(--green);
border-radius: 3px; transition: width 0.05s;
}
#tx-gain-slider, #rx-vol-slider {
width: 100%; cursor: pointer; height: 3px;
-webkit-appearance: none; appearance: none;
background: var(--panel3); border-radius: 2px; outline: none;
}
#tx-gain-slider::-webkit-slider-thumb,
#rx-vol-slider::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 12px; height: 12px; border-radius: 50%;
background: var(--green); cursor: pointer;
border: 1px solid #000;
}
#tx-gain-slider::-moz-range-thumb,
#rx-vol-slider::-moz-range-thumb {
width: 12px; height: 12px; border-radius: 50%;
background: var(--green); cursor: pointer;
border: 1px solid #000;
}
/* ── FOX / HOUND MODE ── */
.wj-hound-step {
display: flex; align-items: center; gap: 6px;
padding: 3px 6px; border-radius: 2px;
font-family: var(--mono); font-size: 9px;
color: #555; border: 1px solid transparent;
transition: all 0.2s;
}
.wj-hound-step.active {
color: #f90; border-color: rgba(255,140,0,0.3);
background: rgba(255,140,0,0.08);
}
.wj-hound-step.done {
color: #4c8; border-color: rgba(76,200,140,0.2);
background: rgba(76,200,140,0.06);
}
.wj-hound-step.failed {
color: #f66; border-color: rgba(255,80,80,0.3);
background: rgba(255,80,80,0.06);
}
.wj-hound-step-num {
width: 14px; height: 14px; border-radius: 50%;
background: #222; border: 1px solid #444;
display: flex; align-items: center; justify-content: center;
font-size: 8px; flex-shrink: 0;
}
.wj-hound-step.active .wj-hound-step-num { background: #f90; border-color: #f90; color: #000; }
.wj-hound-step.done .wj-hound-step-num { background: #4c8; border-color: #4c8; color: #000; }
.wj-hound-step.failed .wj-hound-step-num { background: #f44; border-color: #f44; color: #fff; }
.wj-hound-step-label { flex: 1; }
.wj-hound-step-status { font-size: 9px; color: #888; }
/* ── LOG QSO ── */
.log-th {
padding: 7px 10px; text-align: left; font-family: var(--mono);
font-size: 10px; color: var(--dim); letter-spacing: 1px;
border-right: 1px solid var(--border); white-space: nowrap;
}
.log-th:last-child { border-right: none; }
.log-th:hover { color: var(--green); }
#log-table tbody tr {
border-bottom: 1px solid var(--border);
transition: background 0.1s;
}
#log-table tbody tr:hover { background: var(--panel2); }
#log-table tbody td {
padding: 6px 10px; font-family: var(--mono); font-size: 11px;
color: var(--text); border-right: 1px solid var(--border); white-space: nowrap;
}
#log-table tbody td:last-child { border-right: none; }
.log-call { color: var(--green); font-weight: bold; }
.log-mode-cw { color: var(--amber); }
.log-mode-ft8 { color: #7ec8e3; }
.log-mode-ft4 { color: #5bb8d4; }
.log-action-btn {
background: none; border: 1px solid var(--border2); border-radius: 3px;
color: var(--dim); font-family: var(--mono); font-size: 9px;
padding: 2px 6px; cursor: pointer; transition: all 0.1s;
}
.log-action-btn:hover { border-color: var(--green); color: var(--green); }
.log-action-btn.del:hover { border-color: var(--red); color: var(--red); }
/* ── FORMY globalne (zachowane) ── */
input[type="text"], input[type="number"], input[type="password"], input[type="email"], select {
background: var(--panel2); border: 1px solid var(--border2); border-radius: 4px;
color: var(--green); font-family: var(--mono); font-size: 12px; padding: 7px 10px;
outline: none; width: 100%; transition: border-color 0.2s;
}
input:focus, select:focus { border-color: var(--green); box-shadow: 0 0 4px rgba(var(--green-rgb),0.15); }
input::placeholder { color: var(--dim); }
/* ── TOAST ── */
.toast {
position: fixed; top: 16px; left: 50%; transform: translateX(-50%) translateY(-20px);
background: var(--panel); border: 1px solid var(--green2); border-radius: 8px;
padding: 14px 28px; font-family: var(--mono); font-size: 14px; color: var(--green);
z-index: 99999; opacity: 0; transition: all 0.25s; pointer-events: none;
box-shadow: 0 4px 24px rgba(0,0,0,0.6); white-space: nowrap; max-width: 90vw;
text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast.error { border-color: var(--red); color: var(--red); }
.toast.warning { border-color: var(--amber); color: var(--amber); }
/* ── SCROLLBAR ── */
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 2px; }
/* ── PAGES ── */
.page-content { display: none; padding: 14px; }
.page-content.active { display: block; }
#page-wsjtx { padding: 0; }
#page-wsjtx.active { display: flex; flex-direction: column; height: var(--wsjtx-h, 1028px); overflow: hidden; }
body.page-wsjtx-active, html.page-wsjtx-active { overflow: hidden; scrollbar-gutter: auto; }
#page-radio { padding: 0; }
#page-radio.active { display: block; height: var(--radio-grid-h, calc(1080px - 52px)); max-height: var(--radio-grid-h, calc(1080px - 52px)); overflow: hidden; }
body.page-radio-active, html.page-radio-active { overflow: hidden; scrollbar-gutter: auto; }
/* DX Cluster — pelna wysokosc, bez scrolla strony. Lista spotow scrolluje
wewnetrznie, reszta (pasek gorny, config, CLI) jest staticzna. */
#page-dxcluster { padding: 8px; }
#page-dxcluster.active { display: flex; flex-direction: column; height: calc(100vh - 90px); overflow: hidden; }
#page-dxcluster .page-col { max-width: 100%; padding-bottom: 0; gap: 6px; }
/* ── LOG TABLE ── */
.log-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 11px; }
.log-table th { padding: 6px 8px; text-align: left; color: var(--dim); font-weight: 400; border-bottom: 1px solid var(--border); font-size: 10px; letter-spacing: 1px; }
.log-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); color: var(--text); }
.log-table tr:hover td { background: var(--panel2); }
.log-table .call { color: var(--green); font-weight: bold; }
.log-table .freq { color: var(--amber); }
.del-btn { background: none; border: none; color: var(--dim); cursor: pointer; font-size: 13px; transition: color 0.1s; }
.del-btn:hover { color: var(--red); }
/* ── SETTINGS ── */
.settings-layout { display: flex; flex-direction: column; gap: 18px; padding: 0 0 25px; }
/* Jednolity wrapper dla stron admin/settings/internet — jedna kolumna, max-width */
.page-col {
display: flex; flex-direction: column; gap: 18px;
max-width: 1075px; padding-bottom: 30px;
}
.page-col .box { width: 100%; }
.page-col .box-header { font-size: 15px; padding: 9px 15px; letter-spacing: 2px; }
.page-col .box-body { padding: 15px; font-size: 13px; }
.page-col input[type="text"],
.page-col input[type="number"],
.page-col input[type="password"],
.page-col input[type="email"],
.page-col select,
.page-col textarea {
font-size: 13px; padding: 7px 10px;
}
/* PROFIL: pary bokow obok siebie (MOJ PROFIL+HASLO, AUDIO+WYGLAD), TX EQ
sam na cala szerokosc. Staly grid bez @media — zgodny z zasada #app-scale
nizej (caly UI renderowany w stalym 1440px i skalowany transform:scale(),
wiec proporcje kolumn sa identyczne na kazdej fizycznej rozdzielczosci,
nie trzeba przelamywac ukladu przy roznych ekranach). */
.profile-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; align-items: start; }
.settings-inner { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; padding: 13px 15px; }
.sg { display: flex; flex-direction: column; gap: 5px; }
.sg label { font-family: var(--mono); font-size: 13px; color: var(--dim); letter-spacing: 1px; }
.save-btn {
background: rgba(var(--green-rgb),0.12); border: 1px solid var(--green2); border-radius: 4px;
color: var(--green); font-family: var(--mono); font-size: 14px; letter-spacing: 1.5px;
padding: 11px; cursor: pointer; width: 100%; transition: all 0.15s;
}
.save-btn:hover { background: rgba(var(--green-rgb),0.2); }
/* ── ADMIN ── */
.admin-btn {
background: rgba(var(--green-rgb),0.1); border: 1px solid var(--green2); border-radius: 4px;
color: var(--green); font-family: var(--mono); font-size: 10px; padding: 4px 10px; cursor: pointer;
}
.admin-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 11px; }
.admin-table th { padding: 7px 10px; text-align: left; color: var(--dim); font-size: 10px; letter-spacing: 1px; border-bottom: 1px solid var(--border); font-weight: 400; }
.admin-table td { padding: 7px 10px; border-bottom: 1px solid var(--border); color: var(--text); }
.admin-table tr:hover td { background: var(--panel2); }
/* ── Transfer Widget (wybor przyciskow / sliderow) ── */
.tw-widget { background: var(--panel2); border: 1px solid var(--border); border-radius: 5px; padding: 12px; }
.tw-body { display: grid; grid-template-columns: 1fr 90px 1fr; gap: 8px; align-items: start; }
.tw-col { display: flex; flex-direction: column; gap: 4px; }
.tw-col-header {
font-family: var(--mono); font-size: 9px; color: var(--dim); letter-spacing: 1px;
display: flex; justify-content: space-between; align-items: center;
padding: 3px 0; border-bottom: 1px solid var(--border); margin-bottom: 2px;
}
.tw-count {
background: var(--panel3); color: var(--green); font-size: 9px;
padding: 1px 5px; border-radius: 8px; font-family: var(--mono);
}
.tw-list {
min-height: 160px; max-height: 240px; overflow-y: auto;
background: var(--bg); border: 1px solid var(--border); border-radius: 3px;
padding: 2px;
}
.tw-list.tw-drag-over { border-color: var(--green); background: rgba(var(--green-rgb),0.05); }
.tw-item {
display: flex; justify-content: space-between; align-items: center;
padding: 5px 8px; font-family: var(--mono); font-size: 10px; color: var(--text);
border-radius: 2px; cursor: pointer; user-select: none; gap: 6px;
border-bottom: 1px solid var(--border);
}
.tw-item:last-child { border-bottom: none; }
.tw-item:hover { background: var(--panel2); }
.tw-item.tw-selected { background: rgba(var(--green-rgb),0.12); color: var(--green); border-color: rgba(var(--green-rgb),0.2); }
.tw-item[draggable="true"]:active { opacity: 0.5; }
.tw-sub { font-size: 8px; color: var(--dim); letter-spacing: 0.5px; flex-shrink: 0; }
.tw-buttons {
display: flex; flex-direction: column; gap: 4px;
padding-top: 20px; /* wyrownaj z lista */
}
.tw-btn {
background: var(--panel3); border: 1px solid var(--border); border-radius: 3px;
color: var(--dim); font-family: var(--mono); font-size: 9px; padding: 5px 4px;
cursor: pointer; text-align: center; letter-spacing: 0.5px; white-space: nowrap;
transition: all 0.1s;
}
.tw-btn:hover { border-color: var(--green); color: var(--green); background: rgba(var(--green-rgb),0.06); }
.tw-hint { font-family: var(--mono); font-size: 8px; color: var(--dim); text-align: center; padding-top: 2px; }
/* ── MODAL ── */
.modal-overlay {
display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.7);
z-index: 1000; align-items: center; justify-content: center;
}
.modal-overlay.active,
.modal-overlay[style*="flex"] { display: flex; }
.modal-box {
background: var(--panel); border: 1px solid var(--border2); border-radius: 8px;
padding: 20px; max-width: 480px; width: 90%; max-height: 90vh; overflow-y: auto;
}
.modal-header {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 16px; font-family: var(--mono); font-size: 12px; color: var(--text); letter-spacing: 2px;
}
.modal-close {
background: none; border: none; color: var(--dim); cursor: pointer; font-size: 16px; transition: color 0.2s;
}
.modal-close:hover { color: var(--red); }
.modal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.full-row { grid-column: 1 / -1; }
.change-pwd-form { display: flex; flex-direction: column; gap: 12px; padding: 12px; }
.change-pwd-form .field { display: flex; flex-direction: column; gap: 4px; }
.change-pwd-form label { font-family: var(--mono); font-size: 10px; color: var(--dim); letter-spacing: 1px; }
.role-badge {
font-family: var(--mono); font-size: 10px; padding: 2px 8px; border-radius: 3px;
border: 1px solid var(--border); color: var(--dim); background: var(--panel2);
}
/* ── VFO CYFRY (zachowane z oryginalnego) ── */
.vfo-digit { display: inline-block; min-width: 30px; text-align: center; border-radius: 4px; padding: 0 2px; transition: background 0.12s, color 0.15s; }
.vfo-digit.active { cursor: ns-resize; }
.vfo-digit.active:hover { background: rgba(var(--green-rgb),0.12); text-shadow: 0 0 20px rgba(var(--green-rgb),1); }
.vfo-digit.selected { background: rgba(var(--green-rgb),0.18); outline: 1px solid rgba(var(--green-rgb),0.5); text-shadow: 0 0 25px rgba(var(--green-rgb),1); }
.vfo-sep { color: rgba(var(--green-rgb),0.3); padding: 0 2px; font-size: 36px; line-height: 1; }
.vfo-sep.sep-mhz { color: rgba(var(--green-rgb),0.5); font-size: 40px; }
/* Up/down arrow triangles above/below each VFO digit — click bumps that
digit's position by one step, same effect as scrolling on it (an
alternative for touchscreens/mice where wheel-over-a-digit isn't
obvious). */
/* min-width matches .vfo-digit's own min-width — locks each column to a
fixed, non-overlapping slot in the row, so an arrow is always centered
over its OWN digit and never visually bleeds toward a neighbor
(reported live: clicking the arrow shown over one digit changed a
DIFFERENT, adjacent one instead). letter-spacing:-1px is set on the
#vfo-digits row itself (inline style) for the bare digit characters —
reset to normal here so it doesn't also compress/shift the arrow
glyphs relative to their column. */
.vfo-digit-col { display: inline-flex; flex-direction: column; align-items: center;
line-height: 1; min-width: 30px; letter-spacing: normal; }
.vfo-arrow { font-size: 9px; line-height: 1; padding: 1px 0; color: rgba(var(--green-rgb),0.3);
cursor: pointer; user-select: none; transition: color 0.12s, text-shadow 0.12s;
width: 100%; text-align: center; }
.vfo-arrow:hover { color: rgba(var(--green-rgb),0.95); text-shadow: 0 0 8px rgba(var(--green-rgb),0.7); }
/* ── CHAT (operator chat, embedded in the OPERATORZY panel — chat.js) ── */
.chat-msg { font-family: var(--mono); font-size: 10px; line-height: 1.4; padding: 3px 5px;
border-radius: 3px; background: transparent; }
.chat-msg.chat-mine { background: rgba(var(--green-rgb),0.06); }
.chat-msg.chat-system { display: flex; align-items: center; gap: 5px; color: var(--dim);
font-size: 9px; padding: 1px 5px; }
.chat-header { display: flex; align-items: center; gap: 4px; margin-bottom: 1px; }
.chat-role-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
.chat-user { font-weight: bold; }
.chat-call { color: var(--dim); font-size: 9px; }
.chat-time { color: var(--dim); font-size: 8px; margin-left: auto; }
.chat-text { color: var(--text); word-break: break-word; }
.chat-sys-text { font-style: italic; }
.chat-badge {
position: absolute; top: -5px; right: -5px; min-width: 15px; height: 15px;
border-radius: 8px; background: var(--red); color: #fff; font-size: 9px;
line-height: 15px; text-align: center; padding: 0 3px; font-weight: bold;
}
/* ── RESPONSIVE ── */
/* RESPONSYWNOSC: brak @media breakpoints — caly layout jest renderowany w
stalym rozmiarze referencyjnym 1440x1080 (#app-scale) i skalowany
jednolicie (transform:scale) wg viewportu w index.html. Reflow przez
@media kolidowalby z tym podejsciem (zmienialby pozycje elementow przy
przeskalowanym, ale wciaz "logicznie 1440px" widoku). */
/* Selektor radia */
#rig-selector-row select {
width: 100%; font-family: var(--mono); font-size: 11px;
background: var(--panel2); border: 1px solid var(--border);
color: var(--green); border-radius: 4px; padding: 6px 8px;
}
/* ══════════════════════════════════════════════════════════════════════════════
WSJT-X PAGE — ciemny motyw zbliżony do oryginału WSJT-X
══════════════════════════════════════════════════════════════════════════════ */
/* Layout */
/* ── 1. GÓRNY PASEK STATUSU I STEROWANIA (DWIE LINIE, przejete z forka 2026-10-17) ── */
.wsjtx-layout {
display: flex; flex-direction: column;
flex: 1; height: 100%;
background: var(--wj-bg); color: var(--wj-text); overflow: hidden;
font-family: var(--ui); user-select: none;
}
.wsjtx-topbar {
/* FIX (2026-10-17, live report: chce wiekszy wodospad, "wyciagnac okna
band i rx wyzej") - caly pasek nad wodospadem (ten blok + cycle-bar +
scope-toolbar ponizej) scisniety, zeby zwolnic wysokosc dla wodospadu
i okien dekodowan pod nim. */
display: flex; flex-direction: column; gap: 5px;
background: var(--wj-topbar); border-bottom: 2px solid var(--wj-border);
padding: 5px 10px; flex-shrink: 0; z-index: 10;
box-shadow: 0 2px 10px rgba(0,0,0,0.15);
}
.wj-topbar-row-1, .wj-topbar-row-2 {
display: flex; align-items: center; gap: 10px; width: 100%; flex-wrap: nowrap;
}
.wj-topbar-left {
display: flex; align-items: center; gap: 10px; flex: 1; min-width: 0;
}
.wj-topbar-right {
width: 380px; min-width: 380px; display: flex; align-items: center;
box-sizing: border-box; padding-left: 6px;
}
.wsjtx-freq {
font-family: var(--mono); font-size: 18px; font-weight: 800;
color: #fff; letter-spacing: 0.6px;
background: var(--wj-input-bg); padding: 4px 10px; border-radius: 6px;
border: 1px solid var(--wj-border-bright);
display: flex; align-items: center; gap: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
min-width: 158px; white-space: nowrap; flex-shrink: 0; box-sizing: border-box;
}
.wsjtx-freq::before {
content: ''; display: inline-block; width: 9px; height: 9px; border-radius: 50%;
background: var(--green); box-shadow: 0 0 8px var(--green); flex-shrink: 0;
}
html[data-mode="light"] .wsjtx-freq { color: #0284c7; background: #ffffff; border-color: #cbd5e1; }
.wj-mode-switch {
display: inline-flex; border: 1px solid var(--wj-border-bright); border-radius: 6px;
overflow: hidden; background: var(--wj-input-bg); flex-shrink: 0;
}
.wj-mode-btn {
font-family: var(--mono); font-size: 12px; font-weight: 800; letter-spacing: 0.5px;
color: var(--wj-text-dim); background: transparent; border: none;
padding: 4px 11px; cursor: pointer; transition: all 0.15s;
}
.wj-mode-btn:hover { background: rgba(255,255,255,0.08); color: var(--wj-text); }
.wj-mode-btn.active { background: #0284c7 !important; color: #fff !important; }
.wj-band-select {
/* Natural (auto) width on purpose - checked live 2026-10-21: both 80px
and 19ch cut the selected option to "20m (14.074 ..." because the
native dropdown arrow eats into the box. Auto sizes to the widest
option, which is the smallest width that shows the full text. */
font-family: var(--mono); font-size: 12px; font-weight: 800;
background: var(--wj-input-bg); border: 1px solid var(--wj-border-bright);
color: var(--green); padding: 5px 9px; border-radius: 6px; outline: none; cursor: pointer;
width: auto; flex-shrink: 0;
}
.wj-topbar-check {
font-size: 12px; font-family: var(--mono); font-weight: 800; color: var(--wj-text);
display: flex; align-items: center; gap: 6px; cursor: pointer;
padding: 4px 9px; border-radius: 6px; border: 1px solid var(--wj-border-bright);
background: var(--wj-card); transition: all 0.15s;
flex-shrink: 0; /* see .wj-period-switch's comment - don't get squeezed by the TX-wait status pill */
}
.wj-topbar-check:hover { background: var(--wj-card-sub); border-color: #0284c7; }
.wj-topbar-check input { cursor: pointer; accent-color: var(--green); width: 15px; height: 15px; }
.wj-topbar-check.hound-box {
color: #f97316; border-color: rgba(249,115,22,0.55); background: rgba(249,115,22,0.12);
}
.wj-topbar-check.hound-box input { accent-color: #f97316; }
.wj-period-switch {
/* flex-shrink:0 (2026-10-20, live request: "jak [status pill] sie pojawi
przycina 1st/2nd") - #wj-tx-wait-status sharing this row starts at
display:none and only appears during TX wait/transmit; the row is
flex-wrap:nowrap, so without this, the newly-visible pill's width
squeezed the OTHER items (this one included) to make room instead of
the pill's own ellipsis/overflow handling absorbing it alone. */
display: inline-flex; border: 1px solid var(--wj-border-bright); border-radius: 6px; overflow: hidden;
flex-shrink: 0;
}
.wj-period-switch .wj-mode-btn.active { background: #0284c7 !important; color: #fff !important; }
.wsjtx-clock-wrap {
display: flex; align-items: center; gap: 6px;
background: var(--wj-input-bg); border: 1px solid var(--wj-border-bright);
padding: 3px 8px; border-radius: 6px;
flex-shrink: 0; /* see .wj-period-switch's comment above */
}
#wj-clock-s {
font-family: var(--mono); font-size: 17px; font-weight: 800; color: var(--wj-text);
min-width: 22px; text-align: right;
}
.wsjtx-status-pill {
font-family: var(--mono); font-size: 11px; font-weight: 800;
padding: 4px 10px; border-radius: 6px; border: 1px solid var(--wj-border-bright);
color: var(--wj-text-dim); background: var(--wj-card);
display: flex; align-items: center; gap: 6px; white-space: nowrap;
}
.wsjtx-status-pill.online { color: #22c55e; border-color: rgba(34,197,94,0.4); background: rgba(34,197,94,0.15); }
.wsjtx-status-pill.tx { color: #ef4444; border-color: rgba(239,68,68,0.4); background: rgba(239,68,68,0.15); }
.wsjtx-status-pill.decoding { color: var(--amber); border-color: rgba(var(--amber-rgb),0.4); background: rgba(var(--amber-rgb),0.15); }
/* Przyciski topbar */
.wj-btn {
font-family: var(--mono); font-size: 12px; font-weight: 800;
padding: 5px 11px; cursor: pointer; border-radius: 6px;
border: 1px solid var(--wj-border-bright); background: var(--wj-card); color: var(--wj-text);
transition: all 0.15s; display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
}
.wj-btn:hover { background: var(--wj-card-sub); border-color: #0284c7; color: #fff; }
html[data-mode="light"] .wj-btn:hover { color: #0284c7; }
.wj-btn-tune {
background: rgba(239,68,68,0.15); border-color: rgba(239,68,68,0.5); color: #ef4444;
}
.wj-btn-tune:hover, .wj-btn-tune.active { background: #ef4444 !important; color: #fff !important; border-color: #ef4444 !important; }
#wj-own-rx-btn {
width: 100%; justify-content: center; font-size: 12.5px; font-weight: 800; padding: 6px 14px;
background: rgba(34,197,94,0.2); border-color: #22c55e; color: #22c55e;
}
#wj-own-rx-btn.active { background: rgba(34,197,94,0.25); border-color: #22c55e; color: #22c55e; }
#wj-own-rx-btn.active:hover { background: rgba(34,197,94,0.4); color: #fff; }
#wj-own-rx-btn.stopped { background: rgba(245,158,11,0.18); border-color: #f59e0b; color: #f59e0b; }
#wj-own-rx-btn.stopped:hover { background: rgba(245,158,11,0.35); color: #fff; }
/* ── 1.5. PASEK POSTĘPU CYKLU UTC ── */
.cycle-bar-wrap {
background: #03060a; border-bottom: 1px solid var(--wj-border);
padding: 4px 12px; flex-shrink: 0; box-sizing: border-box;
}
.cycle-bar-header {
display: flex; justify-content: space-between; align-items: center;
font-family: var(--mono); font-size: 11px; font-weight: 700;
margin-bottom: 3px; color: var(--wj-text-dim);
}
.cycle-progress-outer {
position: relative; height: 16px; background: #020408;
border: 1px solid var(--wj-border-bright); border-radius: 5px; overflow: hidden; display: flex;
}
.cycle-progress-fill {
height: 100%; width: 0%;
background: linear-gradient(90deg, #10b981 0%, #06b6d4 75%, #f59e0b 84%, #f43f5e 100%);
transition: width 0.1s linear;
}
.cycle-markers-overlay {
position: absolute; inset: 0; display: flex; pointer-events: none;
font-family: var(--mono); font-size: 9.5px; font-weight: 800; line-height: 16px;
}
.c-marker-rx { position: absolute; left: 8px; color: #4ade80; }
.c-marker-dec { position: absolute; left: 82%; color: #facc15; }
.c-marker-tx { position: absolute; left: 93%; color: #f87171; }
html[data-mode="light"] .cycle-bar-wrap { background: #f1f5f9; border-bottom: 1px solid var(--wj-border); }
html[data-mode="light"] .cycle-progress-outer { background: #e2e8f0; border-color: #cbd5e1; }
html[data-mode="light"] .cycle-bar-header { color: #475569; }
/* ── 2. WODOSPAD / SCOPE I TOOLBAR CZĘSTOTLIWOŚCI (przejete z forka 2026-10-17) ── */
.wsjtx-scope-wrap {
flex-shrink: 0; border-bottom: 1px solid var(--wj-border); background: #020408;
width: 100%; min-width: 0; display: flex; flex-direction: column;
}
.wsjtx-scope-toolbar {
display: flex; align-items: center; gap: 8px; padding: 3px 10px;
background: var(--wj-card); border-bottom: 1px solid var(--wj-border);
font-family: var(--mono); font-size: 11px; color: var(--wj-text-dim);
}
.wsjtx-scope-canvas {
/* Halved (2026-10-20, live request: "ramka w ktorej sie znajduje wybor
jest za duza") - this is the clickable waterfall used to pick the TX
audio frequency; the backing bitmap resolution tracks this CSS height
dynamically (see wsjtx_scope.js's resize handler), so no JS change
needed alongside this. */
display: block; width: 100%; height: 80px; cursor: crosshair;
background: #020408;
}
.wsjtx-scope-axis {
position: relative; height: 20px; background: var(--wj-topbar);
border-bottom: 1px solid var(--wj-border); overflow: hidden;
font-family: var(--mono); font-size: 11px; font-weight: 700; color: var(--wj-text-dim);
}
.wsjtx-scope-axis .wj-axis-tick {
position: absolute; top: 3px; transform: translateX(-50%);
white-space: nowrap;
}
/* Flagi stacji wolających CQ na wodospadzie FT8 (klikalne — QSY do nich) */
.wj-cq-flag {
position: absolute; top: 2px; transform: translateX(-50%);
pointer-events: auto; cursor: pointer;
display: flex; flex-direction: column; align-items: center;
font-family: var(--mono); font-size: 9px; line-height: 1.1;
z-index: 5;
}
.wj-cq-flag::after {
content: ''; width: 1px; height: 6px; background: rgba(var(--green-rgb),0.67);
margin-top: 1px;
}
.wj-cq-flag-call {
background: rgba(2,8,16,0.82); border: 1px solid var(--green); color: var(--green);
border-radius: 2px; padding: 1px 4px; white-space: nowrap; box-shadow: 0 0 4px rgba(0,0,0,0.6);
}
.wj-cq-flag:hover .wj-cq-flag-call {
background: var(--green); color: var(--on-accent); border-color: var(--green);
}
#wj-tx-freeze-btn.active { border-color: #2a6a8a; color: #4cf; background: #05202a; }
#wj-fake-split-toggle.active { border-color: #8a6a2a; color: #fc0; background: #2a1f05; }
/* Edytowalne pola czestotliwosci RX/TX w toolbarze scope'a */
.wj-freq-field {
font-family: var(--mono); font-size: 11.5px; font-weight: 800;
display: inline-flex; align-items: center; gap: 5px;
}
.wj-freq-input {
width: 56px; background: var(--wj-input-bg); border: 1px solid var(--wj-border-bright);
color: inherit; font-family: var(--mono); font-size: 12px; font-weight: 800;
padding: 2px 5px; border-radius: 4px; text-align: right; outline: none;
}
.wj-freq-input:focus { border-color: #0284c7; }
/* Ukryj strzalki spinner w number input dla czystszego wygladu */
.wj-freq-input::-webkit-inner-spin-button,
.wj-freq-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
.wj-freq-input[type=number] { -moz-appearance: textfield; }
/* ── 3. POZIOMY SPLITTER MIĘDZY WODOSPADEM A DEKODOWANIAMI ── */
.wj-h-split-resizer {
height: 7px; background: var(--wj-card);
border-top: 1px solid var(--wj-border); border-bottom: 1px solid var(--wj-border);
cursor: row-resize; display: flex; align-items: center; justify-content: center;
transition: background 0.15s; z-index: 10; flex-shrink: 0;
}
.wj-h-split-resizer:hover, .wj-h-split-resizer.dragging { background: #0284c7; }
.wj-h-split-grip { width: 60px; height: 3px; border-radius: 2px; background: rgba(255,255,255,0.4); }
html[data-mode="light"] .wj-h-split-grip { background: rgba(0,0,0,0.3); }
/* ── 4. OKNA DEKODOWAŃ: BAND ACTIVITY & RX FREQUENCY ── */
.wsjtx-main {
display: flex; flex-direction: row; flex: 1;
overflow: hidden; gap: 0; min-height: 0; align-items: stretch;
}
.wsjtx-decodes-wrap {
display: flex; flex-direction: row; flex: 1; width: 100%;
min-height: 0; overflow: hidden; position: relative; background: var(--wj-bg);
}
.wj-split-pane {
display: flex; flex-direction: column; overflow: hidden;
min-width: 140px; flex-grow: 1; flex-shrink: 1; min-height: 0;
}
.wj-split-resizer {
flex: 0 0 6px; cursor: col-resize; background: var(--wj-card);
border-left: 1px solid var(--wj-border); border-right: 1px solid var(--wj-border);
position: relative; transition: background 0.2s;
}
.wj-split-resizer:hover, .wj-split-resizer.dragging { background: #0284c7; }
.wj-split-resizer::after {
content: ''; position: absolute; left: 50%; top: 50%;
transform: translate(-50%,-50%); width: 2px; height: 30px;
background: rgba(255,255,255,0.3); border-radius: 1px;
}
.wj-btn-xs {
font-size: 10px; padding: 1px 5px; line-height: 1.4;
}
/* Poziomy resizer gora/dol miedzy wodospadem a oknami dekodow (Band Activity / RX Frequency) */
.wj-h-split-resizer {
height: 7px; background: var(--wj-card);
border-top: 1px solid var(--wj-border); border-bottom: 1px solid var(--wj-border);
cursor: row-resize; display: flex; align-items: center; justify-content: center;
transition: background 0.15s; z-index: 10; flex-shrink: 0;
}
.wj-h-split-resizer:hover, .wj-h-split-resizer.dragging { background: #0284c7; }
.wj-h-split-grip { width: 60px; height: 3px; border-radius: 2px; background: rgba(255,255,255,0.4); }
html[data-mode="light"] .wj-h-split-grip { background: rgba(0,0,0,0.3); }
.wj-panel-title {
background: var(--wj-card); border-bottom: 1px solid var(--wj-border);
padding: 6px 12px; display: flex; align-items: center; gap: 8px;
font-family: var(--mono); font-size: 13px; font-weight: 800;
color: var(--wj-text); letter-spacing: 0.5px;
}
.wsjtx-decode-header {
/* FIX (2026-10-17, live report: "band i rx maja za duza czcionke nie
miesci sie pelen opis dekodow"): kolumny i font przejete 1:1 z forka
byly za duze na realna tresc (dluzsze prawdziwe znaki/lokatory/raporty
niz testowe skroty) - zmniejszone kolumny Grid/Country/czas/dB/DT/Freq,
zwezony gap i font, zeby oddac wiecej miejsca kolumnie Message (1fr). */
display: grid; grid-template-columns: 50px 38px 38px 48px 1fr 42px 44px;
column-gap: 5px; padding: 4px 8px;
background: var(--wj-card-sub); border-bottom: 1px solid var(--wj-border);
font-family: var(--mono); font-size: 10px; color: var(--wj-text-dim);
font-weight: 800; letter-spacing: 0.3px; flex-shrink: 0; box-sizing: border-box;
}
.wsjtx-decode-header .wjh-r { text-align: right; }
.wjh {
padding: 0 4px; white-space: nowrap; user-select: none;
}
.wjh-r { text-align: right; }
.wsjtx-decode-list {
flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0;
background: var(--wj-bg); font-family: var(--mono); font-size: 11.5px;
}
.wsjtx-decode-list::-webkit-scrollbar { width: 5px; }
.wsjtx-decode-list::-webkit-scrollbar-track { background: var(--wj-bg); }
.wsjtx-decode-list::-webkit-scrollbar-thumb { background: var(--wj-border-bright); border-radius: 3px; }
/* Wiersz zdekodowanej stacji */
.wj-decode-row {
display: grid; grid-template-columns: 50px 38px 38px 48px 1fr 42px 44px;
column-gap: 5px; align-items: center; padding: 3px 8px; line-height: 20px;
cursor: pointer; border-bottom: 1px solid rgba(255,255,255,0.04);
font-weight: 600; color: var(--wj-text); font-size: 11.5px;
transition: background 0.1s; white-space: nowrap; overflow: hidden; box-sizing: border-box;
}
.wj-decode-row .wj-d-snr, .wj-decode-row .wj-d-dt, .wj-decode-row .wj-d-freq { text-align: right; }
.wj-decode-row .wj-d-msg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
html[data-mode="light"] .wj-decode-row { border-bottom: 1px solid rgba(0,0,0,0.06); }
.wj-decode-row:hover { background: rgba(255,255,255,0.08); }
html[data-mode="light"] .wj-decode-row:hover { background: rgba(2,132,199,0.08); }
.wj-decode-row.wj-cq { color: #38bdf8; font-weight: 700; } /* CQ — jasny niebieski jak w WSJT-X */
.wj-decode-row.wj-mycall { color: #fca5a5; background: rgba(239,68,68,0.2) !important; font-weight: 800; border-left: 3px solid #ef4444; } /* własny callsign */
.wj-decode-row.wj-dx { color: #22c55e; font-weight: 700; } /* odpowiedź do DX — zielony */
.wj-decode-row.wj-73 { color: #fbbf24; font-weight: 700; } /* 73/RR73 — bursztyn */
.wj-decode-row.wj-worked { color: var(--wj-text-dimmer); text-decoration: line-through; opacity: 0.6; } /* juz w logu */
.wj-decode-row.wj-own-tx { color: #fef08a; background: rgba(245,158,11,0.16) !important; font-weight: 700; border-left: 3px solid #f59e0b; } /* nasz TX */
/* Tryb jasny: kolory wierszy powyzej (pastelowe, dobrane pod ciemne tlo)
zlewaly sie z jasnym tlem - zgloszenie 2026-11-02 (zrzut ft8.bmp: nasze
TX, 73/RR73 i CQ prawie niewidoczne). Te same barwy, ciemniejsze odcienie.
!important przy TX - tlo i znacznik "TX" sa ustawiane inline w wsjtx.js. */
html[data-mode="light"] .wj-decode-row.wj-cq { color: #0369a1; }
html[data-mode="light"] .wj-decode-row.wj-mycall { color: #991b1b; background: rgba(239,68,68,0.14) !important; border-left-color: #dc2626; }
html[data-mode="light"] .wj-decode-row.wj-dx { color: #15803d; }
html[data-mode="light"] .wj-decode-row.wj-73 { color: #b45309; }
html[data-mode="light"] .wj-decode-row.wj-worked { color: #64748b; opacity: 0.8; }
html[data-mode="light"] .wj-decode-row.wj-own-tx { color: #78350f !important; background: rgba(245,158,11,0.22) !important; border-left: 3px solid #d97706 !important; }
html[data-mode="light"] .wj-decode-row.wj-own-tx .wj-tx-mark { color: #b45309 !important; }
html[data-mode="light"] .wj-tx-btn { color: #334155; }
html[data-mode="light"] .wj-tx-btn:hover { color: #0f172a; }
html[data-mode="light"] .wj-tx-btn.active { color: #92400e !important; }
html[data-mode="light"] .wj-tx-num { color: #b45309; }
html[data-mode="light"] #wj-autoqso-status { color: #0f172a !important; }
.wj-decode-row.selected { outline: 1px solid var(--green); background: rgba(56,189,248,0.15) !important; }
/* ── Belka separatora periodow dekodowania (dashed line + badge) —
zastepuje pojedynczy <div class="wj-period-sep"> generowany w
wsjtx.js (_renderDecodes) prostym, tematycznym podzialem zamiast
pelnej struktury sep-line/sep-badge z forka, bo JS nie generuje
tych dodatkowych elementow potomnych. ── */
.wj-period-sep {
border-top: 2px solid var(--wj-period-bar-border); margin: 4px 6px 2px;
opacity: 0.9;
}
.wj-d-time { color: var(--wj-text-dimmer); font-size: 11px; }
.wj-d-dt, .wj-d-freq { color: var(--wj-text-dim); }
.wj-d-grid { min-width: 52px; color: var(--wj-text-dimmer); font-size: 10px; }
.wj-d-country { min-width: 60px; color: var(--wj-text-dimmer); font-size: 10px; overflow: hidden; text-overflow: ellipsis; }
.wj-empty {
padding: 20px; text-align: center;
font-family: var(--mono); font-size: 11px; color: var(--wj-text-dimmer);
}
/* ── Zmienne FT8/WSJT-X (przejete z forka 2026-10-16, panel prawy) —
oddzielny, lokalny zestaw tokenow dla tej strony (karty/obwodki
dopasowane do ciemnej "karty" w stylu oryginalnego WSJT-X), poza
glownymi --panel/--border uzywanymi wszedzie indziej. ── */
:root {
--wj-bg: #070c14;
--wj-card: #0b111a;
--wj-card-sub: #0f1724;
--wj-topbar: #090e18;
--wj-border: rgba(var(--green-rgb),0.12);
--wj-border-bright: rgba(var(--green-rgb),0.28);
--wj-text: #e2e8f0;
--wj-text-dim: #94a3b8;
--wj-text-dimmer: #64748b;
--wj-input-bg: var(--input-bg);
--wj-period-bar-bg: #081220;
--wj-period-bar-border: #0284c7;
--wj-period-bar-text: #38bdf8;
--wj-badge-bg: rgba(2, 132, 199, 0.25);
--wj-badge-border: rgba(56, 189, 248, 0.4);
}
:root[data-mode="light"] {
--wj-bg: #f8fafc;
--wj-card: #ffffff;
--wj-card-sub: #f1f5f9;
--wj-topbar: #ffffff;
--wj-border: rgba(15, 23, 42, 0.09);
--wj-border-bright: rgba(15, 23, 42, 0.20);
--wj-text: #0f172a;
--wj-text-dim: #475569;
--wj-text-dimmer: #64748b;
--wj-period-bar-bg: #e0f2fe;
--wj-period-bar-border: #0284c7;
--wj-period-bar-text: #0369a1;
--wj-badge-bg: rgba(2, 132, 199, 0.15);
--wj-badge-border: rgba(2, 132, 199, 0.4);
}
/* Prawa kolumna */
.wsjtx-right {
width: 380px; min-width: 380px; flex-shrink: 0;
display: flex; flex-direction: column;
background: var(--wj-card);
border-left: 2px solid var(--wj-border);
overflow-y: auto;
gap: 10px; padding: 12px; box-sizing: border-box;
/* Kolumna zaczyna sie od linii waterfalla —
wsjtx-main jest flex-row i zaczyna sie po topbarze+scope */
}
.wsjtx-right input[type="text"],
.wsjtx-right select {
background: var(--wj-input-bg) !important;
border: 1px solid var(--wj-border-bright) !important;
color: var(--text) !important;
border-radius: 4px !important;
}
.wsjtx-right input[type="text"]:focus,
.wsjtx-right select:focus {
border-color: var(--green2) !important;
}
.wj-panel-title {
font-family: var(--mono); font-size: 13.5px; font-weight: 800;
color: var(--green); letter-spacing: 0.8px; margin-bottom: 8px;
}
.wj-panel-title label {
font-size: 10px; color: var(--dim); display: flex; align-items: center;
gap: 4px; cursor: pointer; font-weight: normal; text-transform: none;
letter-spacing: normal;
}
/* Automatyka QSO — "status box" (przejete z forka): karta z wyrazniejszym
stanem GOTOWY/trwajace QSO, zamiast samej linii tekstu jak poprzednio */
.auto-qso-box {
background: var(--wj-card-sub);
border: 1px solid var(--wj-border-bright);
border-radius: 6px;
padding: 10px 12px;
font-family: var(--mono);
}
.auto-qso-box.active {
border-color: rgba(34, 197, 94, 0.6);
background: rgba(34, 197, 94, 0.08);
}
/* TX panel - FIX (2026-10-17, live report: "makra nie sa w stanie pokazac
pelnego wypelnienia czcionka"): fork's own 13px/380px combo still wasn't
enough for REALNA wypelniona tresc makra (dlugi prawdziwy znak + lokator
+ raport, np. "DL1ABC SQ3MZM R-15" - dluzsze niz skrocone dane testowe) -
zmniejszony font i padding daja wiecej znakow na linie. */
.wsjtx-tx-panel {
padding: 10px 12px; border: 1px solid var(--wj-border); border-radius: 6px;
background: var(--wj-card-sub); flex-shrink: 0;
}
.wsjtx-tx-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 4px; margin-top: 8px;
}
.wj-tx-btn {
display: flex; align-items: center; gap: 4px; min-width: 0;
background: var(--wj-input-bg); border: 1px solid var(--wj-border-bright); border-radius: 5px;
padding: 5px 6px; cursor: pointer; text-align: left;
font-family: var(--mono); color: #cbd5e1; font-size: 10px; font-weight: 700;
transition: all 0.15s; overflow: hidden;
}
.wj-tx-btn:hover { border-color: var(--green2); background: var(--wj-card); color: #fff; }
.wj-tx-btn.active {
background: rgba(245, 158, 11, 0.28) !important; border-color: #f59e0b !important;
color: #fbbf24 !important; font-weight: 800;
}
.wj-tx-num { font-size: 10px; font-weight: 800; color: #f59e0b; flex-shrink: 0; }
.wj-tx-text {
font-size: inherit; line-height: 1.3; flex: 1; min-width: 0;
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Pola DX i SNR */
/* Font shrunk 15.5px -> 12px (2026-10-21, live request: the DX field's
placeholder "-- KLIKNIJ WIERSZ LUB WPISZ --" didn't fit the field at the
old size) - kept identical between DX and Grid on purpose, so the two
fields still look consistent with each other, per the same request. */
#wj-dx-call { font-size: 12px !important; font-weight: 800 !important; color: var(--green) !important; padding: 7px 10px !important; }
#wj-dx-grid { width: 68px !important; font-size: 12px !important; font-weight: 800 !important; color: var(--green) !important; padding: 7px 8px !important; text-align: center; }
#wj-dx-snr { font-size: 15px !important; font-weight: 800 !important; color: #22c55e !important; }
/* Antena i Rotor */
#wj-beam-row { font-size: 12.5px !important; padding: 8px 10px !important; border-radius: 5px !important; }
#wj-beam-az { font-size: 20px !important; font-weight: 800 !important; color: #f59e0b !important; }
#wj-rotor-az { font-size: 13px !important; font-weight: 800 !important; color: #22c55e !important; }
/* Log QSO */
.wsjtx-log-panel {
padding: 12px 14px; border: 1px solid var(--wj-border); border-radius: 6px;
background: var(--wj-card-sub); display: flex; flex-direction: column; gap: 8px;
}
#wj-log-call, #wj-log-grid { font-size: 13.5px !important; padding: 6px 10px !important; }
#wj-log-mode, #wj-log-rst-sent, #wj-log-rst-rcvd { font-size: 12.5px !important; padding: 5px 8px !important; }
#wj-log-comment { font-size: 12px !important; padding: 5px 8px !important; }
/* Legenda kolorow wierszy dekodowania (zastepuje martwy panel "konfiguracja
WSJT-X" - HAMCTRL ma wlasny dekoder, zewnetrzny WSJT-X nigdy nie jest
wymagany, wiec ta instrukcja konfiguracji nie miala tu racji bytu) */
.wsjtx-config-panel {
padding: 10px 12px; flex-shrink: 0; border: 1px solid var(--wj-border);
border-radius: 6px; background: var(--wj-card-sub);
}
.wj-legend-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 6px 10px;
}
.wj-legend-item {
display: flex; align-items: center; gap: 6px;
font-family: var(--mono); font-size: 10.5px; color: var(--wj-text-dim);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.wj-legend-dot {
width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0;
}
/* Dolny pasek */
.wsjtx-bottombar {
display: flex; align-items: center; gap: 10px;
background: var(--wj-topbar); border-top: 1px solid var(--wj-border);
padding: 5px 14px; flex-shrink: 0;
font-family: var(--mono); font-size: 12px; color: var(--dim);
}
/* TX pulse animation */
@keyframes wj-tx-pulse {
from { opacity: 1; } to { opacity: 0.5; }
}
/* Podswietlenie pola "callsign DX" w formularzu logowania, gdy automatyka
QSO konczy polaczenie i czeka na zatwierdzenie przez uzytkownika */
@keyframes wj-pending-log-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(140, 190, 255, 0); }
50% { box-shadow: 0 0 0 4px rgba(140, 190, 255, 0.5); }
}
.wj-pending-log-highlight {
animation: wj-pending-log-pulse 0.6s ease-in-out 3;
border-color: #8be !important;
}
/* Page layout override dla WSJTX — pełna wysokość */
/* ── Log QSO w zakładce WSJT-X ── */
.wj-log-row {
border-bottom: 1px solid #111;
transition: background 0.05s;
}
.wj-log-row:hover { background: #141414; }
.wj-log-row td { padding: 2px 4px; color: #888; white-space: nowrap; }
.wj-log-call { color: #4cf !important; font-weight: bold; }
.wj-log-empty {
padding: 16px; text-align: center;
font-family: 'Share Tech Mono', monospace;
font-size: 10px; color: #333;
}
/* Tryby cyfrowe (USB-D, LSB-D, PKTUSB, PKTLSB) — wyróżnienie */
.mode-btn-digital {
border-color: rgba(var(--amber-rgb),0.4) !important;
color: var(--amber) !important;
}
.mode-btn-digital.active {
background: rgba(var(--amber-rgb),0.15) !important;
border-color: var(--amber) !important;
color: var(--amber) !important;
}