narty-2027/pages/linki.html
oskar 06ab1b24aa UI: kolumna dostępności 6–13.02, sekcja Archiwum, nowy termin w nagłówkach
porownanie.html:
- nowa kolumna „Dostępność 6–13.02” z pola `dostepnosc_6_13_02_2027`,
  skrócona do emoji + werdyktu ( DOSTĘPNE / 🔍 do sprawdzenia /
   BRAK (zweryf.)), pełna notatka w atrybucie title
- sortowanie kolumny układa decyzyjnie (wolne → do sprawdzenia → brak),
  nie alfabetycznie po emoji
- wiersz Glemm Lodge na czerwono i z przekreśloną nazwą: zweryfikowany
  brak miejsc bije kolor statusu ceny (reguła CSS niżej wygrywa)
- karty `priorytet: ARCHIWALNA` wypadają z tabeli i z bilansu do osobnej
  sekcji „Archiwum” na dole strony
- legenda i bilans statusów pod tabelą

Termin wyjazdu wyjechał do stałych TERM_LABEL/TERM_SHORT/TERM_NOTE:
nagłówki wszystkich stron (BUNDLE_NAME, też w gen_viz.py), lead
start.html, opis kafla porównania i raport braków biorą go stąd.
Zmiana terminu = zmiana stałych i regeneracja.

Raport braków czyta teraz dostępność z pola frontmattera zamiast zgadywać
z nagłówków w body, i pomija karty archiwalne. Wypis po regeneracji
podaje bilans /🔍/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 10:00:23 +02:00

229 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="pl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Linki — Ski 2027 — wyjazd 613.02.2027</title>
<style>
:root { color-scheme: light dark; --fg:#0f172a; --muted:#64748b; --bg:#ffffff;
--line:#e2e8f0; --accent:#0d9488; --chip:#f1f5f9; --quote:#f8fafc; }
@media (prefers-color-scheme: dark) {
:root { --fg:#e2e8f0; --muted:#94a3b8; --bg:#0f172a; --line:#334155;
--accent:#2dd4bf; --chip:#1e293b; --quote:#1e293b; }
}
* { box-sizing: border-box; }
body { margin: 0 auto; padding: 1.25rem 1rem 4rem; max-width: 46rem;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
font-size: 17px; line-height: 1.55; color: var(--fg); background: var(--bg);
-webkit-text-size-adjust: 100%; }
nav { font-size: .85rem; margin-bottom: 1.25rem; }
nav a { color: var(--muted); }
h1 { font-size: 1.55rem; line-height: 1.25; margin: 0 0 .35rem; }
h2 { font-size: 1.2rem; margin: 2rem 0 .5rem; padding-top: .5rem;
border-top: 1px solid var(--line); }
h3 { font-size: 1.02rem; margin: 1.4rem 0 .4rem; }
p, ul, ol { margin: .6rem 0; }
ul, ol { padding-left: 1.3rem; }
li { margin: .25rem 0; }
li.task { list-style: none; margin-left: -1.3rem; }
li.task input { margin-right: .45rem; }
a { color: var(--accent); }
a.ext::after { content: " ↗"; font-size: .8em; color: var(--muted); }
code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .88em;
background: var(--chip); padding: .1em .35em; border-radius: 4px; }
pre { background: var(--chip); padding: .75rem; border-radius: 6px; overflow-x: auto; }
pre code { background: none; padding: 0; }
blockquote { margin: .9rem 0; padding: .6rem .9rem; background: var(--quote);
border-left: 3px solid var(--accent); border-radius: 0 6px 6px 0; }
blockquote p { margin: .2rem 0; }
hr { border: 0; border-top: 1px solid var(--line); margin: 1.5rem 0; }
.table-wrap { overflow-x: auto; margin: .8rem 0; -webkit-overflow-scrolling: touch; }
table { border-collapse: collapse; width: 100%; font-size: .92rem; }
th, td { border: 1px solid var(--line); padding: .4rem .55rem; vertical-align: top; }
th { background: var(--chip); font-weight: 600; }
.chip { display: inline-block; background: var(--chip); color: var(--muted);
font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;
padding: .15rem .5rem; border-radius: 999px; }
.lead { color: var(--muted); margin: .35rem 0 1.25rem; }
.meta { margin: 0 0 1.5rem; }
.meta summary { cursor: pointer; font-size: .85rem; color: var(--muted);
padding: .3rem 0; }
.meta table { font-size: .85rem; }
.meta th { width: 38%; text-align: left; font-weight: 500; word-break: break-word; }
.cards { list-style: none; padding: 0; margin: 0; }
.cards li { border: 1px solid var(--line); border-radius: 8px; padding: .8rem .9rem;
margin: .6rem 0; }
.cards a { font-weight: 600; text-decoration: none; }
.cards p { margin: .3rem 0 0; font-size: .9rem; color: var(--muted); }
footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
font-size: .78rem; color: var(--muted); }
/* landing: kafle i lista regionów */
body.wide { max-width: 72rem; }
.tiles, .regions { list-style: none; padding: 0; }
.tiles { margin: 1.25rem 0 2rem; display: grid; gap: .75rem; }
.regions { margin: .6rem 0 0; display: grid; gap: .5rem; }
@media (min-width: 34rem) {
.tiles { grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); }
.regions { grid-template-columns: repeat(2, 1fr); }
}
.tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px;
padding: .9rem 1rem; text-decoration: none; color: var(--fg); }
.tiles a { font-size: 1.05rem; font-weight: 600; }
.tiles a:hover, .tiles a:focus, .regions a:hover, .regions a:focus {
border-color: var(--accent); }
.tiles small, .regions small { display: block; margin-top: .3rem; font-weight: 400;
font-size: .85rem; color: var(--muted); }
.regions a { font-weight: 600; }
/* podsumowanie per region na karcie porównania */
.summary { list-style: none; padding: 0; margin: .8rem 0 2rem; display: grid; gap: .6rem; }
@media (min-width: 48rem) { .summary { grid-template-columns: repeat(2, 1fr); } }
.summary li { border: 1px solid var(--line); border-left: 3px solid var(--accent);
border-radius: 8px; padding: .65rem .85rem; }
.summary .budget { font-size: .95rem; }
.summary p { margin: .35rem 0 0; font-size: .88rem; color: var(--muted); }
/* tabela porównawcza: sortowanie klikiem + kolor statusu ceny */
table.sortable th { cursor: pointer; user-select: none; white-space: nowrap; }
table.sortable th::after { content: " ↕"; font-size: .8em; opacity: .35; }
table.sortable th[data-dir="asc"]::after { content: " ↑"; opacity: 1; }
table.sortable th[data-dir="desc"]::after { content: " ↓"; opacity: 1; }
table.sortable td { font-size: .88rem; padding: .35rem .5rem; }
table.sortable td[data-num] { white-space: nowrap; }
table.sortable .name { font-weight: 600; }
tr.verified td { background: rgba(13, 148, 136, .14); }
tr.estimate td { background: rgba(100, 116, 139, .10); color: var(--muted); }
tr.estimate td.name a, tr.verified td.name a { color: var(--accent); }
/* zweryfikowany BRAK miejsc bije kolor statusu ceny — reguła niżej wygrywa */
tr.unavailable td { background: rgba(220, 38, 38, .16); }
tr.unavailable td.name a { text-decoration: line-through; }
td.avail { white-space: nowrap; font-weight: 600; }
.archive { margin-top: 2.5rem; }
.archive h2 { margin-bottom: .2rem; }
.archive .lead { font-size: .88rem; margin-top: .2rem; }
.legend { font-size: .82rem; color: var(--muted); margin: .5rem 0 0; }
.legend .box { display: inline-block; width: .8em; height: .8em; border-radius: 3px;
border: 1px solid var(--line); vertical-align: -.05em; margin-right: .25em; }
.legend .box.v { background: rgba(13, 148, 136, .35); }
.legend .box.e { background: rgba(100, 116, 139, .25); }
.legend .box.u { background: rgba(220, 38, 38, .35); }
/* miniatury w tabeli */
td.thumb { padding: .25rem; width: 128px; }
td.thumb img { width: 120px; height: 80px; object-fit: cover; border-radius: 6px;
display: block; }
td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius: 6px;
align-items: center; justify-content: center; background: var(--chip);
color: var(--muted); font-weight: 700; letter-spacing: .05em;
text-decoration: none; }
/* galeria na karcie oferty */
.gallery { margin: 0 0 1.25rem; }
.gallery-main img { width: 100%; max-height: 24rem; object-fit: cover;
border-radius: 10px; display: block; background: var(--chip); }
.gallery-thumbs { display: flex; gap: .4rem; margin-top: .4rem; overflow-x: auto;
-webkit-overflow-scrolling: touch; padding-bottom: .2rem; }
.gallery-thumbs button { flex: 0 0 auto; padding: 0; border: 2px solid transparent;
border-radius: 6px; background: none; cursor: pointer; }
.gallery-thumbs button[aria-current] { border-color: var(--accent); }
.gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover;
border-radius: 4px; display: block; }
/* spis linków do kopiowania */
.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
margin: 1rem 0 1.5rem; }
.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem;
border: 1px solid var(--accent); border-radius: 8px;
background: var(--accent); color: #f8fafc; cursor: pointer; }
.copybar button + button { background: none; color: var(--accent); }
.copybar button:hover { filter: brightness(1.08); }
.copy-status { font-size: .85rem; color: var(--muted); }
.links { list-style: none; padding: 0; margin: .5rem 0 0; }
.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); }
.links li:last-child { border-bottom: 0; }
.links a { font-weight: 600; text-decoration: none; }
.links code.url { display: block; margin: .2rem 0; font-size: .82rem;
background: none; padding: 0; color: var(--muted);
word-break: break-all; user-select: all; }
.links p { margin: 0; font-size: .85rem; color: var(--muted); }
/* kafel regionu ze zdjęciem w tle */
.regions a.hero { background-size: cover; background-position: center;
border-color: transparent; color: #f8fafc; min-height: 6.5rem;
display: flex; flex-direction: column; justify-content: flex-end; }
.regions a.hero small { color: #e2e8f0; }
</style>
</head>
<body>
<nav><a href="start.html">← Start</a> · <a href="index.html">Wszystkie karty</a> · <a href="porownanie.html">Porównanie ofert</a> · <a href="linki.html">Linki</a> · <a href="../viz.html">Graf</a></nav>
<h1>Linki do wysłania</h1>
<p class="lead">Wszystkie 24 stron wystawki jako pełne adresy — do skopiowania i wrzucenia grupie. Lista powstaje z tych samych danych co reszta stron, więc nowa oferta pojawi się tu sama.</p>
<div class="copybar"><button type="button" id="copy-all">Kopiuj wszystkie</button><button type="button" id="copy-start" data-url="https://narty27.kapala.org/">Kopiuj link startowy</button><span class="copy-status" role="status" aria-live="polite"></span></div>
<section data-section="Wejście"><h2>Wejście</h2><ul class="links"><li><a href="https://narty27.kapala.org/">Start</a><code class="url">https://narty27.kapala.org/</code><p>Landing decyzyjny — od tego zaczyna grupa.</p></li><li><a href="https://narty27.kapala.org/pages/porownanie.html">Porównanie ofert</a><code class="url">https://narty27.kapala.org/pages/porownanie.html</code><p>Wszystkie noclegi w jednej sortowalnej tabeli, ze zdjęciami.</p></li><li><a href="https://narty27.kapala.org/pages/wyjazd.html">Analiza wariantów</a><code class="url">https://narty27.kapala.org/pages/wyjazd.html</code><p>Wyjazd narciarski 4 rodzin (14 osób) z Warszawy w tygodniu Fasching: porównanie czterech regionów, statusy…</p></li><li><a href="https://narty27.kapala.org/pages/index.html">Spis wszystkich kart</a><code class="url">https://narty27.kapala.org/pages/index.html</code><p>Każdy dokument bazy wiedzy w jednym miejscu.</p></li><li><a href="https://narty27.kapala.org/viz.html">Graf powiązań</a><code class="url">https://narty27.kapala.org/viz.html</code><p>Struktura bazy — załącznik.</p></li><li><a href="https://narty27.kapala.org/pages/linki.html">Ten spis linków</a><code class="url">https://narty27.kapala.org/pages/linki.html</code><p>Strona, którą czytasz.</p></li></ul></section>
<section data-section="Regiony"><h2>Regiony</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/saalbach/region.html">Saalbach</a><code class="url">https://narty27.kapala.org/pages/saalbach/region.html</code><p>Budżet grupy ~16 20019 500 €. Wariant bazowy: 270 km tras i najkrótszy dojazd z Warszawy, ale w tygodniu Fasching…</p></li><li><a href="https://narty27.kapala.org/pages/ischgl/region.html">Ischgl/Paznaun</a><code class="url">https://narty27.kapala.org/pages/ischgl/region.html</code><p>Budżet grupy ~14 20017 000 €. Wariant alternatywny: 239 km Silvretta Areny z jazdą do bezcłowego Samnaun, nocleg w…</p></li><li><a href="https://narty27.kapala.org/pages/kronplatz/region.html">Kronplatz</a><code class="url">https://narty27.kapala.org/pages/kronplatz/region.html</code><p>Budżet grupy ~14 50017 700 €. Wariant alternatywny i najlepiej znoszący tydzień Fasching: 121 km na jednej górze plus…</p></li><li><a href="https://narty27.kapala.org/pages/sfl/region.html">SFL</a><code class="url">https://narty27.kapala.org/pages/sfl/region.html</code><p>Budżet grupy ~14 50017 900 €. Wariant alternatywny: 214 km na słonecznym płaskowyżu i najlepsza infrastruktura…</p></li></ul></section>
<section data-section="Oferty — Saalbach"><h2>Oferty — Saalbach</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/alpenparks-sonnleiten.html">AlpenParks Hotel &amp; Apartment Sonnleiten</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/alpenparks-sonnleiten.html</code><p>PLAN A-bis · Saalbach</p></li><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/kristall-schattbergxpress.html">Appartementhaus Kristall at SchattbergXpress</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/kristall-schattbergxpress.html</code><p>PLAN A · Saalbach (centrum)</p></li><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/sportchalet-viehhofen.html">Sportchalet Viehhofen</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/sportchalet-viehhofen.html</code><p>PLAN B · Viehhofen (3 km od Saalbach)</p></li><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/alpenparks-rehrenberg.html">AlpenParks Apartments Rehrenberg</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/alpenparks-rehrenberg.html</code><p>ARCHIWALNA · Viehhofen</p></li><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/dom-dla-grupy.html">Duży dom wakacyjny 1214 os. (do wyszukania)</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/dom-dla-grupy.html</code><p>ARCHIWALNA · Viehhofen / Maishofen / strona Fieberbrunn</p></li><li><a href="https://narty27.kapala.org/pages/saalbach/oferty/glemm-lodge.html">Glemm Lodge Apartments ⭐⭐⭐⭐</a><code class="url">https://narty27.kapala.org/pages/saalbach/oferty/glemm-lodge.html</code><p>NIEDOSTĘPNY w terminie; referencja cenowa + opcja przy zmianie tygodnia · między Saalbach a Hinterglemm</p></li></ul></section>
<section data-section="Oferty — Ischgl/Paznaun"><h2>Oferty — Ischgl/Paznaun</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/ischgl/oferty/apartment-brandau-kappl.html">Apartment Brandau</a><code class="url">https://narty27.kapala.org/pages/ischgl/oferty/apartment-brandau-kappl.html</code><p>PLAN A · Kappl (Paznaun)</p></li><li><a href="https://narty27.kapala.org/pages/ischgl/oferty/apartments-kappl.html">Apartments Kappl</a><code class="url">https://narty27.kapala.org/pages/ischgl/oferty/apartments-kappl.html</code><p>PLAN B · Kappl (Paznaun)</p></li></ul></section>
<section data-section="Oferty — Kronplatz"><h2>Oferty — Kronplatz</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/kronplatz/oferty/haeuslerhof-valdaora.html">Häuslerhof</a><code class="url">https://narty27.kapala.org/pages/kronplatz/oferty/haeuslerhof-valdaora.html</code><p>PLAN A · Valdaora/Olang</p></li><li><a href="https://narty27.kapala.org/pages/kronplatz/oferty/residence-corones-valdaora.html">Residence Corones</a><code class="url">https://narty27.kapala.org/pages/kronplatz/oferty/residence-corones-valdaora.html</code><p>PLAN B · Valdaora (centrum)</p></li></ul></section>
<section data-section="Oferty — SFL"><h2>Oferty — SFL</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/sfl/oferty/haus-central-ladis.html">Haus Central (Apart Central)</a><code class="url">https://narty27.kapala.org/pages/sfl/oferty/haus-central-ladis.html</code><p>PLAN A · Ladis</p></li><li><a href="https://narty27.kapala.org/pages/sfl/oferty/apart-ideal-serfaus.html">Apart Ideal</a><code class="url">https://narty27.kapala.org/pages/sfl/oferty/apart-ideal-serfaus.html</code><p>PLAN B · Serfaus (obrzeża)</p></li></ul></section>
<section data-section="Pozostałe"><h2>Pozostałe</h2><ul class="links"><li><a href="https://narty27.kapala.org/pages/szablon-maila.html">Szablon zapytania grupowego (EN)</a><code class="url">https://narty27.kapala.org/pages/szablon-maila.html</code><p>Gotowy angielski mail o 4 apartamenty dla 14 osób w terminie 613.02.2027, z pytaniem o najbliższy wolny…</p></li><li><a href="https://narty27.kapala.org/pages/start.html">Start pod własną ścieżką</a><code class="url">https://narty27.kapala.org/pages/start.html</code><p>Ten sam landing co adres główny — przydaje się w zakładkach.</p></li></ul></section>
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
tych plików, edytuj <code>.md</code> i przegeneruj.<br>
powered by <a href="https://gethumanai.pl" target="_blank" rel="noopener">gethumanai.pl</a></footer>
<script>
function collectAll() {
var lines = [];
document.querySelectorAll('section[data-section]').forEach(function (section) {
lines.push(section.dataset.section + ':');
section.querySelectorAll('.url').forEach(function (code) {
lines.push(code.textContent.trim());
});
lines.push('');
});
return lines.join('\n').trim() + '\n';
}
function say(message) {
var status = document.querySelector('.copy-status');
status.textContent = message;
window.setTimeout(function () {
if (status.textContent === message) status.textContent = '';
}, 4000);
}
// navigator.clipboard istnieje tylko w bezpiecznym kontekście (https albo
// localhost) — po http z adresu IP trzeba starą drogą przez zaznaczenie.
function copyLegacy(text) {
var area = document.createElement('textarea');
area.value = text;
area.setAttribute('readonly', '');
area.style.position = 'fixed';
area.style.top = '-1000px';
document.body.appendChild(area);
area.select();
var copied = false;
try { copied = document.execCommand('copy'); } catch (error) { copied = false; }
document.body.removeChild(area);
return copied;
}
function copyText(text, what) {
var ok = function () { say('Skopiowane: ' + what); };
var no = function () { say('Nie udało się skopiować — zaznacz adres ręcznie.'); };
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(ok, function () {
if (copyLegacy(text)) { ok(); } else { no(); }
});
} else if (copyLegacy(text)) { ok(); } else { no(); }
}
document.getElementById('copy-all').addEventListener('click', function () {
copyText(collectAll(), 'cała lista');
});
document.getElementById('copy-start').addEventListener('click', function () {
copyText(this.dataset.url, 'link startowy');
});
</script>
</body>
</html>