Przeprojektowanie pod decyzję: start.html, porownanie.html, galerie
Wejściem jest teraz landing pages/start.html (trzy kafle: porównanie ofert, analiza wariantów, graf + kafle czterech regionów ze zdjęciem najlepszej oferty w tle), a graf schodzi do roli załącznika. start.html linkuje root-absolutnie, bo ten sam plik jest serwowany jako /index.html i jako /pages/start.html. pages/porownanie.html: jedna tabela wszystkich 12 ofert prosto z frontmatterów (miniatura, priorytet, region, ceny, status, odległość, www), sortowanie klikiem w nagłówek (czysty JS, bez zależności), wiersze barwione statusem ceny. Nad tabelą podsumowanie per region — budżet całkowity i jeden wniosek parsowane z tabeli i „Wniosków przekrojowych” w wyjazd.md, bez zaszywania nazw regionów w kodzie. Karty ofert dostały galerię (duże zdjęcie + miniatury, lazy loading), a nawigacja na każdej stronie prowadzi do startu i porównania. Komenda deployu (kopiuje start.html jako /content/index.html) jest wypisywana przez gen_pages.py. Pliki .md bez zmian, check_okf zielony. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ddc2ed0c54
commit
8f1557e736
679
gen_pages.py
679
gen_pages.py
|
|
@ -1,9 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Renderuje karty HTML konceptów bundle'a do katalogu pages/.
|
||||
|
||||
Graf (viz.html) pokazuje strukturę; karty pokazują treść — ceny, dostępność,
|
||||
plusy/minusy, listy do zrobienia. Węzeł w grafie linkuje do karty przez pole
|
||||
`resource` (patrz gen_viz.py).
|
||||
Wejściem dla człowieka jest `pages/start.html` (landing decyzyjny), a nie graf:
|
||||
graf to załącznik pokazujący strukturę powiązań. Karty pokazują treść — ceny,
|
||||
dostępność, plusy/minusy, listy do zrobienia. Węzeł w grafie linkuje do karty
|
||||
przez pole `resource` (patrz gen_viz.py).
|
||||
|
||||
Poza kartą na koncept skrypt generuje trzy strony zbiorcze:
|
||||
|
||||
pages/index.html spis wszystkich kart, pogrupowany katalogami
|
||||
pages/porownanie.html jedna tabela WSZYSTKICH ofert (nie regionów),
|
||||
sortowalna klikiem, złożona z samych frontmatterów;
|
||||
na górze podsumowanie per region z `wyjazd.md`
|
||||
pages/start.html landing: trzy kafle (porównanie / analiza / graf)
|
||||
plus linki do kart czterech regionów
|
||||
|
||||
`start.html` jest jedyną stroną z linkami root-absolutnymi (`/viz.html`,
|
||||
`/pages/...`), bo ten sam plik jest serwowany pod dwiema ścieżkami: jako
|
||||
`/index.html` (korzeń serwisu) i jako `/pages/start.html`. Ścieżki względne
|
||||
działałyby tylko pod jedną z nich. Wymaga to serwowania bundle'a z korzenia
|
||||
domeny — lokalnie `python3 -m http.server` w katalogu bundle'a, na PIHA
|
||||
korzeń wolumenu (komenda deployu na końcu wypisu skryptu).
|
||||
|
||||
Renderer markdown: używa `python-markdown`, jeśli jest zainstalowany
|
||||
(rozszerzenia tables + fenced_code + sane_lists); w przeciwnym razie własnego
|
||||
|
|
@ -358,11 +375,139 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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; }
|
||||
"""
|
||||
|
||||
SORT_JS = """
|
||||
document.querySelectorAll('table.sortable').forEach(function (table) {
|
||||
var head = table.tHead.rows[0];
|
||||
Array.prototype.forEach.call(head.cells, function (th, i) {
|
||||
th.tabIndex = 0;
|
||||
th.setAttribute('role', 'button');
|
||||
th.addEventListener('click', function () { sortTable(table, i, th); });
|
||||
th.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); sortTable(table, i, th); }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function sortTable(table, index, th) {
|
||||
var dir = th.dataset.dir === 'asc' ? 'desc' : 'asc';
|
||||
Array.prototype.forEach.call(table.tHead.rows[0].cells, function (h) {
|
||||
delete h.dataset.dir;
|
||||
h.setAttribute('aria-sort', 'none');
|
||||
});
|
||||
th.dataset.dir = dir;
|
||||
th.setAttribute('aria-sort', dir === 'asc' ? 'ascending' : 'descending');
|
||||
var body = table.tBodies[0];
|
||||
var rows = Array.prototype.slice.call(body.rows);
|
||||
var mul = dir === 'asc' ? 1 : -1;
|
||||
rows.sort(function (a, b) {
|
||||
var x = a.cells[index], y = b.cells[index];
|
||||
var nx = x.dataset.num, ny = y.dataset.num;
|
||||
if (nx !== undefined && ny !== undefined) return (parseFloat(nx) - parseFloat(ny)) * mul;
|
||||
// Wiersze bez wartości liczbowej zawsze na końcu — w obie strony.
|
||||
if (nx !== undefined) return -1;
|
||||
if (ny !== undefined) return 1;
|
||||
var kx = x.dataset.key !== undefined ? x.dataset.key : x.textContent;
|
||||
var ky = y.dataset.key !== undefined ? y.dataset.key : y.textContent;
|
||||
if (!kx) return 1;
|
||||
if (!ky) return -1;
|
||||
return kx.localeCompare(ky, 'pl') * mul;
|
||||
});
|
||||
rows.forEach(function (row) { body.appendChild(row); });
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def _page_shell(title: str, depth: int, content: str) -> str:
|
||||
def _page_shell(
|
||||
title: str,
|
||||
depth: int,
|
||||
content: str,
|
||||
*,
|
||||
body_class: str = "",
|
||||
nav: str | None = None,
|
||||
script: str = "",
|
||||
) -> str:
|
||||
up = "../" * depth
|
||||
if nav is None:
|
||||
nav = (
|
||||
f'<a href="{up}start.html">← Start</a> · '
|
||||
f'<a href="{up}index.html">Wszystkie karty</a> · '
|
||||
f'<a href="{up}porownanie.html">Porównanie ofert</a> · '
|
||||
f'<a href="{up}../viz.html">Graf</a>'
|
||||
)
|
||||
css_class = f' class="{body_class}"' if body_class else ""
|
||||
script_tag = f"\n<script>{script}</script>" if script else ""
|
||||
return f"""<!doctype html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
|
|
@ -371,11 +516,11 @@ def _page_shell(title: str, depth: int, content: str) -> str:
|
|||
<title>{html.escape(title)} — {html.escape(BUNDLE_NAME)}</title>
|
||||
<style>{CSS}</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav><a href="{up}index.html">← Wszystkie karty</a> · <a href="{up}../viz.html">Graf</a></nav>
|
||||
<body{css_class}>
|
||||
<nav>{nav}</nav>
|
||||
{content}
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>{script_tag}
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
|
@ -434,12 +579,20 @@ def render_page(concept: Concept, pages: dict[str, str]) -> str:
|
|||
description = str(frontmatter.get("description") or "")
|
||||
lead = f'<p class="lead">{html.escape(description)}</p>' if description else ""
|
||||
|
||||
images = offer_images(concept) if is_offer(concept) else []
|
||||
gallery = render_gallery(images, str(frontmatter.get("nazwa") or concept.title))
|
||||
|
||||
content = (
|
||||
f'<span class="chip">{chip}</span>\n'
|
||||
f"<h1>{headline}</h1>\n{lead}\n"
|
||||
f"<h1>{headline}</h1>\n{lead}\n{gallery}\n"
|
||||
f"{_meta_table(frontmatter)}\n{rendered}"
|
||||
)
|
||||
return _page_shell(concept.title, concept.id.count("/"), content)
|
||||
return _page_shell(
|
||||
concept.title,
|
||||
concept.id.count("/"),
|
||||
content,
|
||||
script=GALLERY_JS if len(images) > 1 else "",
|
||||
)
|
||||
|
||||
|
||||
_INDEX_HEADING_RE = re.compile(r"^#\s+(.+)$", re.M)
|
||||
|
|
@ -505,6 +658,467 @@ def render_index(concepts: list[Concept]) -> str:
|
|||
return _page_shell("Karty", 0, content)
|
||||
|
||||
|
||||
# --- zdjęcia obiektów (pages/img/<id>/, patrz gen_images.py) -----------
|
||||
|
||||
IMG_DIRNAME = "img"
|
||||
IMG_DIR = PAGES_DIR / IMG_DIRNAME
|
||||
IMG_EXTS = (".jpg", ".jpeg", ".png", ".webp")
|
||||
|
||||
|
||||
def offer_image_paths(concept: Concept) -> list[str]:
|
||||
"""Zdjęcia oferty jako ścieżki względne wobec katalogu `pages/`.
|
||||
|
||||
Mapowanie id → zdjęcia jest konwencją katalogową (pages/img/<id>/NN.jpg),
|
||||
nie polem frontmattera — pliki .md zostają nietknięte.
|
||||
"""
|
||||
offer_id = str(concept.frontmatter.get("id") or "").strip()
|
||||
if not offer_id:
|
||||
return []
|
||||
directory = IMG_DIR / offer_id
|
||||
if not directory.is_dir():
|
||||
return []
|
||||
files = sorted(p for p in directory.iterdir() if p.suffix.lower() in IMG_EXTS)
|
||||
return [f"{IMG_DIRNAME}/{offer_id}/{p.name}" for p in files]
|
||||
|
||||
|
||||
def offer_images(concept: Concept) -> list[str]:
|
||||
"""To samo, ale względem katalogu karty konceptu."""
|
||||
up = "../" * concept.id.count("/")
|
||||
return [up + path for path in offer_image_paths(concept)]
|
||||
|
||||
|
||||
def initials(name: str) -> str:
|
||||
"""Inicjały obiektu — placeholder dla ofert bez zdjęć."""
|
||||
words = [w for w in re.split(r"[\s/–—-]+", name) if w and w[0].isalnum()]
|
||||
return "".join(w[0].upper() for w in words[:2]) or "?"
|
||||
|
||||
|
||||
GALLERY_JS = """
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
"""
|
||||
|
||||
|
||||
def render_gallery(images: list[str], alt: str) -> str:
|
||||
if not images:
|
||||
return ""
|
||||
escaped_alt = html.escape(alt, quote=True)
|
||||
first = html.escape(images[0], quote=True)
|
||||
thumbs = ""
|
||||
if len(images) > 1:
|
||||
buttons = "".join(
|
||||
f'<button type="button"{" aria-current=\"true\"" if n == 0 else ""}>'
|
||||
f'<img src="{html.escape(src, quote=True)}" alt="{escaped_alt} — '
|
||||
f'zdjęcie {n + 1}" loading="lazy"></button>'
|
||||
for n, src in enumerate(images)
|
||||
)
|
||||
thumbs = f'<div class="gallery-thumbs">{buttons}</div>'
|
||||
return (
|
||||
'<figure class="gallery">'
|
||||
f'<div class="gallery-main"><img src="{first}" alt="{escaped_alt}" '
|
||||
'loading="lazy"></div>'
|
||||
f"{thumbs}</figure>"
|
||||
)
|
||||
|
||||
|
||||
# --- podsumowanie per region (z wyjazd.md) ----------------------------
|
||||
|
||||
TRIP_FILE = "wyjazd.md"
|
||||
BUDGET_ROW = "budżet grupy razem"
|
||||
FALLBACK_ROW = "charakter"
|
||||
|
||||
_TABLE_LINK_RE = re.compile(r"\[([^\]]+)\]\(/([^)\s]+\.md)\)")
|
||||
_EMPHASIS_RE = re.compile(r"\*\*|\*|`")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Region:
|
||||
concept_id: str # np. "ischgl/region"
|
||||
dirname: str # np. "ischgl" ("" dla Saalbach z korzenia)
|
||||
label: str # np. "Ischgl/Paznaun"
|
||||
budget: str = ""
|
||||
conclusion: str = ""
|
||||
|
||||
|
||||
def _plain(cell: str) -> str:
|
||||
"""Komórka tabeli markdown → czysty tekst (linki na same etykiety)."""
|
||||
text = _LINK_RE.sub(r"\1", cell)
|
||||
return _EMPHASIS_RE.sub("", text).strip()
|
||||
|
||||
|
||||
def parse_regions() -> list[Region]:
|
||||
"""Regiony + budżet całkowity + jeden wniosek — z tabeli w `wyjazd.md`.
|
||||
|
||||
Nic nie jest tu zaszyte na sztywno: kolumny bierzemy z nagłówka tabeli
|
||||
porównawczej (te, które linkują do `*/region.md`), budżet z wiersza
|
||||
„Budżet grupy razem”, a wniosek z pierwszej pozycji „Wniosków
|
||||
przekrojowych”, która dany region wymienia. Dorzucenie piątego wariantu
|
||||
do `wyjazd.md` wystarczy, żeby pojawił się na stronach.
|
||||
"""
|
||||
path = BUNDLE_ROOT / TRIP_FILE
|
||||
if not path.is_file():
|
||||
return []
|
||||
text = path.read_text(encoding="utf-8")
|
||||
|
||||
regions: list[Region] = []
|
||||
rows: dict[str, list[str]] = {}
|
||||
lines = text.split("\n")
|
||||
for n, line in enumerate(lines):
|
||||
if not line.strip().startswith("|"):
|
||||
continue
|
||||
header = _cells(line)
|
||||
targets = [_TABLE_LINK_RE.search(c) for c in header[1:]]
|
||||
if sum(1 for t in targets if t and t.group(2).endswith("region.md")) < 2:
|
||||
continue
|
||||
if n + 1 >= len(lines) or not _TABLE_SEP_RE.match(lines[n + 1]):
|
||||
continue
|
||||
for match in targets:
|
||||
if not match or not match.group(2).endswith("region.md"):
|
||||
continue
|
||||
concept_id = match.group(2)[: -len(".md")]
|
||||
regions.append(
|
||||
Region(
|
||||
concept_id=concept_id,
|
||||
dirname=posixpath.dirname(concept_id),
|
||||
label=_plain(match.group(1)),
|
||||
)
|
||||
)
|
||||
for row in lines[n + 2 :]:
|
||||
if not row.strip().startswith("|"):
|
||||
break
|
||||
cells = _cells(row)
|
||||
rows[_plain(cells[0]).lower()] = [_plain(c) for c in cells[1:]]
|
||||
break
|
||||
|
||||
conclusions = parse_conclusions(text)
|
||||
for index, region in enumerate(regions):
|
||||
for key in (BUDGET_ROW, FALLBACK_ROW):
|
||||
values = rows.get(key) or []
|
||||
if index < len(values) and values[index]:
|
||||
if key == BUDGET_ROW:
|
||||
region.budget = values[index]
|
||||
elif not region.conclusion:
|
||||
region.conclusion = values[index]
|
||||
keyword = region.dirname or region.label.split()[0]
|
||||
for raw in conclusions:
|
||||
if keyword.lower() in raw.lower():
|
||||
region.conclusion = _plain(raw.split(". ")[0]).rstrip(".") + "."
|
||||
break
|
||||
return regions
|
||||
|
||||
|
||||
_ORDERED_ITEM_RE = re.compile(r"^\s*\d+[.)]\s+(.*)$")
|
||||
|
||||
|
||||
def parse_conclusions(text: str) -> list[str]:
|
||||
"""Pozycje listy numerowanej z sekcji „Wnioski przekrojowe”."""
|
||||
section = re.split(r"^#{2,4}\s+Wnioski przekrojowe\s*$", text, flags=re.M)
|
||||
if len(section) < 2:
|
||||
return []
|
||||
items: list[str] = []
|
||||
for line in section[1].split("\n"):
|
||||
if line.startswith("#"):
|
||||
break
|
||||
match = _ORDERED_ITEM_RE.match(line)
|
||||
if match:
|
||||
items.append(match.group(1).strip())
|
||||
elif line.startswith((" ", "\t")) and line.strip() and items:
|
||||
items[-1] += " " + line.strip()
|
||||
return items
|
||||
|
||||
|
||||
# --- karta porównania ofert -------------------------------------------
|
||||
|
||||
# (nagłówek, pole frontmattera / klucz specjalny, czy sortować liczbowo)
|
||||
COLUMNS: list[tuple[str, str, bool]] = [
|
||||
("Oferta", "_nazwa", False),
|
||||
("Priorytet", "priorytet", False),
|
||||
("Region", "_region", False),
|
||||
("Miejscowość", "miejscowosc", False),
|
||||
("€ / apart. / tydzień", "cena_za_apartament_tydzien", True),
|
||||
("Rodzina 2+2", "koszt_rodzina_2p2", True),
|
||||
("Rodzina 2+1", "koszt_rodzina_2p1", True),
|
||||
("Status ceny", "status_ceny", False),
|
||||
("Od wyciągu", "odleglosc_od_wyciagu", False),
|
||||
("WWW", "www", False),
|
||||
]
|
||||
|
||||
_DIGIT_GAP_RE = re.compile(r"(?<=\d)[\s ](?=\d)")
|
||||
_NUMBER_RE = re.compile(r"\d+(?:[.,]\d+)?")
|
||||
|
||||
|
||||
def sort_number(value: str) -> float | None:
|
||||
"""Pierwsza liczba w tekście, ze spacją jako separatorem tysięcy."""
|
||||
match = _NUMBER_RE.search(_DIGIT_GAP_RE.sub("", value))
|
||||
return float(match.group(0).replace(",", ".")) if match else None
|
||||
|
||||
|
||||
def is_offer(concept: Concept) -> bool:
|
||||
return str(concept.frontmatter.get("type", "")).startswith("oferta")
|
||||
|
||||
|
||||
def _shorten(value: str) -> str:
|
||||
"""Sam rdzeń wartości; doprecyzowanie w nawiasie/po myślniku idzie do title.
|
||||
|
||||
„SZACUNEK — silnik online na alpenparks.at” w komórce rozpycha tabelę na
|
||||
trzy wiersze, a i tak koduje ją kolor wiersza. Pełna treść zostaje pod
|
||||
kursorem, nic nie ginie.
|
||||
"""
|
||||
for separator in (" — ", " (", " - ", " dla "):
|
||||
head = value.split(separator)[0].strip()
|
||||
if head and head != value:
|
||||
return head
|
||||
return value
|
||||
|
||||
|
||||
# Kolumny, w których doprecyzowanie JEST treścią — skracanie zabrałoby sens.
|
||||
NO_SHORTEN = {"odleglosc_od_wyciagu"}
|
||||
|
||||
|
||||
def _cell(value: str, numeric: bool, suffix: str = "", short: bool = True) -> str:
|
||||
attrs = ""
|
||||
if not value:
|
||||
return '<td data-key="">—</td>'
|
||||
if numeric:
|
||||
number = sort_number(value)
|
||||
if number is not None:
|
||||
attrs += f' data-num="{number:g}"'
|
||||
else:
|
||||
attrs += f' data-key="{html.escape(value, quote=True)}"'
|
||||
shown = _shorten(value) if short else value
|
||||
if shown != value:
|
||||
attrs += f' title="{html.escape(value, quote=True)}"'
|
||||
return f"<td{attrs}>{html.escape(shown + suffix)}</td>"
|
||||
|
||||
|
||||
def render_comparison(concepts: list[Concept], regions: list[Region]) -> str:
|
||||
"""Jedna tabela wszystkich ofert bundle'a, prosto z frontmatterów."""
|
||||
by_dir = {r.dirname: r for r in regions}
|
||||
offers = [c for c in concepts if is_offer(c)]
|
||||
# Domyślna kolejność: najtańsza rodzina 2+2 na górze — pierwsze pytanie,
|
||||
# które grupa zadaje. Resztę porządków daje sortowanie klikiem.
|
||||
offers.sort(
|
||||
key=lambda c: (
|
||||
sort_number(str(c.frontmatter.get("koszt_rodzina_2p2") or "")) or 1e9,
|
||||
c.title,
|
||||
)
|
||||
)
|
||||
|
||||
rows = []
|
||||
for concept in offers:
|
||||
frontmatter = concept.frontmatter
|
||||
status = str(frontmatter.get("status_ceny") or "")
|
||||
upper = status.upper()
|
||||
css_row = (
|
||||
"verified" if "ZWERYFIKOWANE" in upper
|
||||
else "estimate" if "SZACUNEK" in upper
|
||||
else ""
|
||||
)
|
||||
dirname = posixpath.dirname(posixpath.dirname(concept.id))
|
||||
region = by_dir.get(dirname)
|
||||
region_label = region.label if region else (dirname or "—")
|
||||
href = html.escape(posixpath.relpath(concept.page, "."), quote=True)
|
||||
|
||||
images = offer_image_paths(concept) # porownanie.html leży w pages/
|
||||
name = str(frontmatter.get("nazwa") or concept.title)
|
||||
if images:
|
||||
thumb = (
|
||||
f'<a href="{href}"><img src="{html.escape(images[0], quote=True)}" '
|
||||
f'alt="{html.escape(name, quote=True)}" loading="lazy"></a>'
|
||||
)
|
||||
else:
|
||||
thumb = (
|
||||
f'<a href="{href}" class="placeholder" title="brak zdjęć">'
|
||||
f"{html.escape(initials(name))}</a>"
|
||||
)
|
||||
|
||||
cells = [f'<td class="thumb" data-key="">{thumb}</td>']
|
||||
for _, field, numeric in COLUMNS:
|
||||
if field == "_nazwa":
|
||||
cells.append(
|
||||
f'<td class="name" data-key="{html.escape(name, quote=True)}">'
|
||||
f'<a href="{href}">{html.escape(name)}</a></td>'
|
||||
)
|
||||
elif field == "_region":
|
||||
page = html.escape(f"{region.concept_id}.html", quote=True) if region else ""
|
||||
label = html.escape(region_label)
|
||||
shown = f'<a href="{page}">{label}</a>' if region else label
|
||||
cells.append(
|
||||
f'<td data-key="{html.escape(region_label, quote=True)}">{shown}</td>'
|
||||
)
|
||||
elif field == "www":
|
||||
url = str(frontmatter.get("www") or "").strip()
|
||||
if url:
|
||||
host = re.sub(r"^https?://(www\.)?", "", url).split("/")[0]
|
||||
cells.append(
|
||||
f'<td data-key="{html.escape(host, quote=True)}">'
|
||||
f'<a href="{html.escape(url, quote=True)}" target="_blank" '
|
||||
f'rel="noopener" class="ext">{html.escape(host)}</a></td>'
|
||||
)
|
||||
else:
|
||||
cells.append('<td data-key="">—</td>')
|
||||
else:
|
||||
value = str(frontmatter.get(field) or "")
|
||||
suffix = ""
|
||||
if not value and field == "cena_za_apartament_tydzien":
|
||||
# Dom dla całej grupy wyceniany jest w całości — pokazujemy
|
||||
# to zamiast pustej komórki, ale z etykietą „za co”.
|
||||
value = str(frontmatter.get("cena_za_calosc_tydzien") or "")
|
||||
suffix = " (cały dom)" if value else ""
|
||||
cells.append(
|
||||
_cell(value, numeric, suffix, short=field not in NO_SHORTEN)
|
||||
)
|
||||
klass = f' class="{css_row}"' if css_row else ""
|
||||
rows.append(f"<tr{klass}>{''.join(cells)}</tr>")
|
||||
|
||||
head = '<th data-key="">Zdjęcie</th>' + "".join(
|
||||
f'<th scope="col" aria-sort="none">{html.escape(title)}</th>'
|
||||
for title, _, _ in COLUMNS
|
||||
)
|
||||
table = (
|
||||
'<div class="table-wrap"><table class="sortable"><thead><tr>'
|
||||
f"{head}</tr></thead><tbody>{''.join(rows)}</tbody></table></div>"
|
||||
)
|
||||
|
||||
summary = ""
|
||||
if regions:
|
||||
items = []
|
||||
for region in regions:
|
||||
budget = (
|
||||
f'<span class="budget"><strong>Budżet grupy:</strong> '
|
||||
f"{html.escape(region.budget)}</span>"
|
||||
if region.budget
|
||||
else ""
|
||||
)
|
||||
conclusion = (
|
||||
f"<p>{render_inline(region.conclusion)}</p>" if region.conclusion else ""
|
||||
)
|
||||
items.append(
|
||||
f'<li><a href="{html.escape(region.concept_id, quote=True)}.html">'
|
||||
f"{html.escape(region.label)}</a><br>{budget}{conclusion}</li>"
|
||||
)
|
||||
summary = (
|
||||
"<h2>Regiony w skrócie</h2>\n"
|
||||
'<ul class="summary">' + "".join(items) + "</ul>"
|
||||
)
|
||||
|
||||
content = (
|
||||
"<h1>Porównanie ofert</h1>\n"
|
||||
f'<p class="lead">Wszystkie {len(offers)} rozważanych noclegów w jednej '
|
||||
"tabeli — dane prosto z frontmatterów kart. Kliknij nagłówek, żeby "
|
||||
"posortować; domyślnie od najtańszej rodziny 2+2.</p>\n"
|
||||
f"{summary}\n"
|
||||
"<h2>Wszystkie oferty</h2>\n"
|
||||
f"{table}\n"
|
||||
'<p class="legend"><span class="box v"></span>cena zweryfikowana '
|
||||
' <span class="box e"></span>szacunek do potwierdzenia</p>'
|
||||
)
|
||||
return _page_shell(
|
||||
"Porównanie ofert", 0, content, body_class="wide", script=SORT_JS
|
||||
)
|
||||
|
||||
|
||||
# --- landing decyzyjny ------------------------------------------------
|
||||
|
||||
# start.html jest serwowany i jako /index.html, i jako /pages/start.html —
|
||||
# tylko ścieżki root-absolutne działają pod obiema (patrz docstring modułu).
|
||||
TILES = [
|
||||
("/pages/porownanie.html", "Porównanie ofert",
|
||||
"Wszystkie noclegi w jednej sortowalnej tabeli: ceny, status ceny, "
|
||||
"priorytet, odległość od wyciągu."),
|
||||
("/pages/wyjazd.html", "Analiza wariantów",
|
||||
"Cztery regiony obok siebie: skipassy, dojazd, budżet całkowity grupy "
|
||||
"i wnioski przekrojowe."),
|
||||
("/viz.html", "Graf powiązań",
|
||||
"Struktura bazy wiedzy — koncepty, linki między nimi i adresy "
|
||||
"zewnętrzne. Załącznik, nie punkt wejścia."),
|
||||
]
|
||||
|
||||
|
||||
def render_start(concepts: list[Concept], regions: list[Region]) -> str:
|
||||
tiles = "".join(
|
||||
f'<li><a href="{href}">{html.escape(title)}'
|
||||
f"<small>{html.escape(description)}</small></a></li>"
|
||||
for href, title, description in TILES
|
||||
)
|
||||
|
||||
by_id = {c.id: c for c in concepts}
|
||||
cards = []
|
||||
for region in regions:
|
||||
concept = by_id.get(region.concept_id)
|
||||
description = region.budget or (
|
||||
str(concept.frontmatter.get("description") or "") if concept else ""
|
||||
)
|
||||
style = ""
|
||||
hero = region_hero(concepts, region)
|
||||
if hero:
|
||||
style = (
|
||||
' class="hero" style="background-image:linear-gradient('
|
||||
"rgba(15,23,42,.62),rgba(15,23,42,.72)),url('"
|
||||
f"/pages/{html.escape(hero, quote=True)}')\""
|
||||
)
|
||||
label = html.escape(region.label)
|
||||
budget = (
|
||||
f"<small>Budżet grupy: {html.escape(region.budget)}</small>"
|
||||
if region.budget
|
||||
else f"<small>{html.escape(description)}</small>"
|
||||
)
|
||||
cards.append(
|
||||
f'<li><a href="/pages/{html.escape(region.concept_id, quote=True)}.html"'
|
||||
f"{style}>{label}{budget}</a></li>"
|
||||
)
|
||||
|
||||
content = (
|
||||
f"<h1>{html.escape(BUNDLE_NAME)}</h1>\n"
|
||||
'<p class="lead">16–23.01.2027, 4 rodziny / 14 osób, wyjazd z Warszawy. '
|
||||
"Od czego zacząć:</p>\n"
|
||||
f'<ul class="tiles">{tiles}</ul>\n'
|
||||
"<h2>Regiony</h2>\n"
|
||||
'<p class="lead">Karta regionu = skipassy, dojazd, terminy, koszty '
|
||||
"życia i budżet całkowity.</p>\n"
|
||||
f'<ul class="regions">{"".join(cards)}</ul>'
|
||||
)
|
||||
nav = (
|
||||
'<a href="/pages/index.html">Wszystkie karty</a> · '
|
||||
'<a href="/pages/porownanie.html">Porównanie ofert</a> · '
|
||||
'<a href="/viz.html">Graf</a>'
|
||||
)
|
||||
return _page_shell("Start", 0, content, nav=nav)
|
||||
|
||||
|
||||
def region_hero(concepts: list[Concept], region: Region) -> str:
|
||||
"""Pierwsze zdjęcie najlepszej oferty regionu (priorytet PLAN A)."""
|
||||
offers = [
|
||||
c
|
||||
for c in concepts
|
||||
if is_offer(c) and posixpath.dirname(posixpath.dirname(c.id)) == region.dirname
|
||||
]
|
||||
|
||||
def rank(concept: Concept) -> tuple[int, str]:
|
||||
priority = str(concept.frontmatter.get("priorytet") or "").upper()
|
||||
for n, marker in enumerate(("PLAN A", "PLAN B", "PLAN C")):
|
||||
if priority.startswith(marker):
|
||||
return (n, concept.id)
|
||||
return (9, concept.id)
|
||||
|
||||
for concept in sorted(offers, key=rank):
|
||||
images = offer_image_paths(concept)
|
||||
if images:
|
||||
return images[0]
|
||||
return ""
|
||||
|
||||
|
||||
# --- raport braków ----------------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -535,13 +1149,27 @@ def missing_data(concepts: list[Concept]) -> list[tuple[str, list[str]]]:
|
|||
# --- main -------------------------------------------------------------
|
||||
|
||||
|
||||
DEPLOY_COMMAND = """ssh piha 'rm -rf /tmp/narty27-deploy && mkdir -p /tmp/narty27-deploy' \\
|
||||
&& scp -r viz.html pages piha:/tmp/narty27-deploy/ \\
|
||||
&& ssh piha 'docker run --rm -v narty27_narty27_content:/content \\
|
||||
-v /tmp/narty27-deploy:/src:ro alpine sh -c \\
|
||||
"rm -rf /content/pages && cp -r /src/viz.html /src/pages /content/ \\
|
||||
&& cp /src/pages/start.html /content/index.html && ls -la /content"' \\
|
||||
&& ssh piha 'rm -rf /tmp/narty27-deploy'"""
|
||||
|
||||
|
||||
def main() -> int:
|
||||
concepts = load_concepts()
|
||||
pages = {c.id: c.page for c in concepts}
|
||||
regions = parse_regions()
|
||||
|
||||
if PAGES_DIR.exists():
|
||||
shutil.rmtree(PAGES_DIR)
|
||||
PAGES_DIR.mkdir(parents=True)
|
||||
# Katalog img/ jest wejściem, nie wyjściem tego skryptu (wypełnia go
|
||||
# gen_images.py) — czyścimy wszystko poza nim.
|
||||
PAGES_DIR.mkdir(parents=True, exist_ok=True)
|
||||
for entry in PAGES_DIR.iterdir():
|
||||
if entry.name == IMG_DIRNAME:
|
||||
continue
|
||||
shutil.rmtree(entry) if entry.is_dir() else entry.unlink()
|
||||
|
||||
written = []
|
||||
for concept in concepts:
|
||||
|
|
@ -550,9 +1178,14 @@ def main() -> int:
|
|||
out.write_text(render_page(concept, pages), encoding="utf-8")
|
||||
written.append(out)
|
||||
|
||||
index = PAGES_DIR / "index.html"
|
||||
index.write_text(render_index(concepts), encoding="utf-8")
|
||||
written.append(index)
|
||||
for name, markup in (
|
||||
("index.html", render_index(concepts)),
|
||||
("porownanie.html", render_comparison(concepts, regions)),
|
||||
("start.html", render_start(concepts, regions)),
|
||||
):
|
||||
path = PAGES_DIR / name
|
||||
path.write_text(markup, encoding="utf-8")
|
||||
written.append(path)
|
||||
|
||||
print(f"Renderer markdown: {RENDERER}")
|
||||
print(f"Zapisano {len(written)} plików w {PAGES_DIR}:")
|
||||
|
|
@ -560,6 +1193,18 @@ def main() -> int:
|
|||
size = path.stat().st_size / 1024
|
||||
print(f" {path.relative_to(BUNDLE_ROOT)} ({size:.1f} KiB)")
|
||||
|
||||
print()
|
||||
offers = [c for c in concepts if is_offer(c)]
|
||||
with_photos = [c for c in offers if offer_image_paths(c)]
|
||||
print(
|
||||
f"Regiony z wyjazd.md: {len(regions)} "
|
||||
f"({', '.join(r.label for r in regions) or '—'})"
|
||||
)
|
||||
print(
|
||||
f"Oferty w tabeli porównawczej: {len(offers)}, "
|
||||
f"ze zdjęciami: {len(with_photos)} (zdjęcia dokłada gen_images.py)"
|
||||
)
|
||||
|
||||
gaps = missing_data(concepts)
|
||||
print()
|
||||
if gaps:
|
||||
|
|
@ -570,6 +1215,10 @@ def main() -> int:
|
|||
print(f" – {item}")
|
||||
else:
|
||||
print("Braków w danych ofert nie wykryto.")
|
||||
|
||||
print()
|
||||
print("Deploy na PIHA (korzeń serwisu = start.html, graf pod /viz.html):")
|
||||
print(DEPLOY_COMMAND)
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -57,10 +57,80 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="index.html">← Wszystkie karty</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<nav><a href="start.html">← Start</a> · <a href="index.html">Wszystkie karty</a> · <a href="porownanie.html">Porównanie ofert</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<h1>Ski 2027 — Skicircus Saalbach</h1>
|
||||
<p class="lead">Karty konceptów bundle'a — pełna treść każdego dokumentu (ceny, dostępność, plusy/minusy). Struktura powiązań: <a href="../viz.html">graf</a>.</p>
|
||||
<h2>Wyjazd i wiedza ogólna</h2>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Apartment Brandau (Kappl) — PLAN A</h1>
|
||||
<p class="lead">Rodzinny dom z sześcioma apartamentami w Kappl — bierzemy cztery, ceny doliny zamiast cen Ischgl, PLAN A wariantu Ischgl.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../../img/apartment-brandau-kappl/01.jpg" alt="Apartment Brandau" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../../img/apartment-brandau-kappl/01.jpg" alt="Apartment Brandau — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../../img/apartment-brandau-kappl/02.jpg" alt="Apartment Brandau — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../../img/apartment-brandau-kappl/03.jpg" alt="Apartment Brandau — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../../img/apartment-brandau-kappl/04.jpg" alt="Apartment Brandau — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../../img/apartment-brandau-kappl/05.jpg" alt="Apartment Brandau — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Apartment Brandau (Kappl)</td></tr><tr><th>description</th><td>Rodzinny dom z sześcioma apartamentami w Kappl — bierzemy cztery, ceny doliny zamiast cen Ischgl, PLAN A wariantu Ischgl.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, ischgl, kappl, plan-a</td></tr><tr><th>id</th><td>apartment-brandau-kappl</td></tr><tr><th>nazwa</th><td>Apartment Brandau</td></tr><tr><th>operator</th><td>prywatna rodzina</td></tr><tr><th>miejscowosc</th><td>Kappl (Paznaun)</td></tr><tr><th>odleglosc_od_wyciagu</th><td>skibus (przystanek blisko domu) → Kappl / Ischgl ~10 min</td></tr><tr><th>uklad_dla_grupy</th><td>6 apartamentów w jednym domu → 4 dla nas</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~900–1 300 € (szacunek, styczeń)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~900–1 300 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~750–1 100 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena mailowo</td></tr><tr><th>priorytet</th><td>PLAN A dla wariantu Ischgl</td></tr><tr><th>www</th><td><a href="http://www.kappl-apartment-brandau.at" target="_blank" rel="noopener" class="ext">http://www.kappl-apartment-brandau.at</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -76,5 +147,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Mail (szablon EN): 4 apartamenty, 16–23.01.2027, konfiguracja 4+4+3+3, cena grupowa.</li><li class="task"><input type="checkbox" disabled> Alternatywy w Kappl, jeśli brak miejsc: Apartments Kappl (apartments.kappl.at — skibus 100 m, narciarnia z suszarką), Ferienwohnen Mattle (centrum, blisko trasy i skibusa), Apart Avenzio (3 min od gondoli Kappl).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Apartments Kappl — PLAN B</h1>
|
||||
<p class="lead">Tyrolski dom apartamentowy 100 m od przystanku skibusa bez przesiadki do Ischgl — liczba wolnych jednostek do potwierdzenia, PLAN B wariantu Ischgl.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../../img/apartments-kappl/01.jpg" alt="Apartments Kappl" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../../img/apartments-kappl/01.jpg" alt="Apartments Kappl — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../../img/apartments-kappl/02.jpg" alt="Apartments Kappl — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../../img/apartments-kappl/03.jpg" alt="Apartments Kappl — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../../img/apartments-kappl/04.jpg" alt="Apartments Kappl — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../../img/apartments-kappl/05.jpg" alt="Apartments Kappl — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Apartments Kappl</td></tr><tr><th>description</th><td>Tyrolski dom apartamentowy 100 m od przystanku skibusa bez przesiadki do Ischgl — liczba wolnych jednostek do potwierdzenia, PLAN B wariantu Ischgl.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, ischgl, kappl, plan-b</td></tr><tr><th>id</th><td>apartments-kappl</td></tr><tr><th>nazwa</th><td>Apartments Kappl</td></tr><tr><th>operator</th><td>prywatny</td></tr><tr><th>miejscowosc</th><td>Kappl (Paznaun)</td></tr><tr><th>odleglosc_od_wyciagu</th><td>przystanek skibusa 100 m — bez przesiadki do Ischgl, Galtür, See</td></tr><tr><th>uklad_dla_grupy</th><td>kilka apartamentów w jednym domu</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~900–1 400 € (szacunek, styczeń)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~900–1 400 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~750–1 150 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena mailowo</td></tr><tr><th>priorytet</th><td>PLAN B dla wariantu Ischgl</td></tr><tr><th>www</th><td><a href="https://www.apartments.kappl.at" target="_blank" rel="noopener" class="ext">https://www.apartments.kappl.at</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>Fakty</h2>
|
||||
|
|
@ -74,5 +145,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Mail: ile apartamentów dostępnych 16–23.01.2027, konfiguracje łóżek.</li><li class="task"><input type="checkbox" disabled> Jeśli < 4 jednostek: dobrać drugi dom w Kappl (lista alternatyw w karcie Brandau).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">region</span>
|
||||
<h1>Ischgl / Paznaun — informacje o regionie</h1>
|
||||
<p class="lead">Wariant alternatywny: 239 km Silvretta Areny z jazdą do bezcłowego Samnaun, nocleg w tańszym Kappl — skipass zweryfikowany na 26/27, bez zniżki dla 17-latków.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>region</td></tr><tr><th>title</th><td>Ischgl / Silvretta Arena + dolina Paznaun</td></tr><tr><th>description</th><td>Wariant alternatywny: 239 km Silvretta Areny z jazdą do bezcłowego Samnaun, nocleg w tańszym Kappl — skipass zweryfikowany na 26/27, bez zniżki dla 17-latków.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>region, ischgl, wariant, skipass, logistyka</td></tr><tr><th>id</th><td>ischgl-paznaun</td></tr><tr><th>nazwa</th><td>Ischgl / Silvretta Arena + dolina Paznaun</td></tr><tr><th>kraj</th><td>Austria</td></tr><tr><th>region</th><td>Tyrol</td></tr><tr><th>sezon</th><td>2026/2027</td></tr><tr><th>grupa</th><td>4 rodziny (2×2+2, 2×2+1) = 14 osób</td></tr><tr><th>wyjazd_z</th><td>Warszawa</td></tr><tr><th>rekomendowany_termin</th><td>16–23.01.2027 (alt. 9–16.01)</td></tr><tr><th>strategia_budzetowa</th><td>mieszkać w Kappl, nie w Ischgl</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Wariant "Ischgl, ale w budżecie": jeździmy w Silvretta Arenie, mieszkamy w <strong>Kappl</strong> (10 min od Ischgl, skibus gratis). Nocleg w samym Ischgl odrzucony cenowo już na etapie pierwszego porównania.</p>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Häuslerhof (Valdaora) — PLAN A</h1>
|
||||
<p class="lead">Gospodarstwo z dokładnie czterema apartamentami — grupa zajmuje cały obiekt, anulacja bezpłatna do 2 miesięcy przed, PLAN A wariantu Kronplatz.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../../img/haeuslerhof-valdaora/01.jpg" alt="Häuslerhof" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../../img/haeuslerhof-valdaora/01.jpg" alt="Häuslerhof — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../../img/haeuslerhof-valdaora/02.jpg" alt="Häuslerhof — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../../img/haeuslerhof-valdaora/03.jpg" alt="Häuslerhof — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../../img/haeuslerhof-valdaora/04.jpg" alt="Häuslerhof — zdjęcie 4" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Häuslerhof (Valdaora)</td></tr><tr><th>description</th><td>Gospodarstwo z dokładnie czterema apartamentami — grupa zajmuje cały obiekt, anulacja bezpłatna do 2 miesięcy przed, PLAN A wariantu Kronplatz.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, kronplatz, valdaora, plan-a</td></tr><tr><th>id</th><td>haeuslerhof-valdaora</td></tr><tr><th>nazwa</th><td>Häuslerhof</td></tr><tr><th>operator</th><td>gospodarstwo rodzinne (agroturystyka premium)</td></tr><tr><th>miejscowosc</th><td>Valdaora/Olang</td></tr><tr><th>odleglosc_od_wyciagu</th><td>skibus do gondoli Olang (Kronplatz)</td></tr><tr><th>uklad_dla_grupy</th><td>DOKŁADNIE 4 apartamenty (2, 4 lub 6 os.) — cały obiekt dla nas</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~800–1 300 € (szacunek, styczeń)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~900–1 300 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~800–1 100 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena mailowo</td></tr><tr><th>priorytet</th><td>PLAN A dla wariantu Kronplatz</td></tr><tr><th>anulacja</th><td>bezpłatnie do 2 mies. przed; 2 mies.–4 tyg.: 50%; <4 tyg.: 80%</td></tr><tr><th>www</th><td><a href="https://www.haeuslerhof.it" target="_blank" rel="noopener" class="ext">https://www.haeuslerhof.it</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -76,5 +147,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Mail: wszystkie 4 apartamenty, 16–23.01.2027, 4+4+3+3.</li><li class="task"><input type="checkbox" disabled> Potwierdzić odległość do przystanku skibusa i częstotliwość rano.</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Residence Corones (Valdaora) — PLAN B</h1>
|
||||
<p class="lead">Rezydencja w centrum Valdaory z przystankiem skibusa przed domem i Digital Guestpassem w cenie — PLAN B wariantu Kronplatz.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Residence Corones (Valdaora)</td></tr><tr><th>description</th><td>Rezydencja w centrum Valdaory z przystankiem skibusa przed domem i Digital Guestpassem w cenie — PLAN B wariantu Kronplatz.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, kronplatz, valdaora, plan-b</td></tr><tr><th>id</th><td>residence-corones-valdaora</td></tr><tr><th>nazwa</th><td>Residence Corones</td></tr><tr><th>operator</th><td>rodzina Pörnbacher-Oberhuber</td></tr><tr><th>miejscowosc</th><td>Valdaora (centrum)</td></tr><tr><th>odleglosc_od_wyciagu</th><td>przystanek skibusa PRZED domem → 5 min do gondoli Kronplatz</td></tr><tr><th>uklad_dla_grupy</th><td>kilka apartamentów w rezydencji</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~900–1 400 € (szacunek, styczeń)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~900–1 400 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~800–1 150 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena mailowo (reception@corones.it)</td></tr><tr><th>priorytet</th><td>PLAN B dla wariantu Kronplatz</td></tr><tr><th>www</th><td><a href="https://corones.it" target="_blank" rel="noopener" class="ext">https://corones.it</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>Fakty</h2>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">region</span>
|
||||
<h1>Kronplatz — informacje o regionie</h1>
|
||||
<p class="lead">Wariant alternatywny: 121 km na jednej górze plus cały Dolomiti Superski w karnecie, baza w tanim Olang/Valdaora, junior do 18 lat bez dopłaty.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>region</td></tr><tr><th>title</th><td>Kronplatz / Plan de Corones (Południowy Tyrol)</td></tr><tr><th>description</th><td>Wariant alternatywny: 121 km na jednej górze plus cały Dolomiti Superski w karnecie, baza w tanim Olang/Valdaora, junior do 18 lat bez dopłaty.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>region, kronplatz, wariant, skipass, logistyka</td></tr><tr><th>id</th><td>kronplatz</td></tr><tr><th>nazwa</th><td>Kronplatz / Plan de Corones (Południowy Tyrol)</td></tr><tr><th>kraj</th><td>Włochy</td></tr><tr><th>region</th><td>Val Pusteria / Pustertal</td></tr><tr><th>sezon</th><td>2026/2027</td></tr><tr><th>grupa</th><td>4 rodziny (2×2+2, 2×2+1) = 14 osób</td></tr><tr><th>wyjazd_z</th><td>Warszawa</td></tr><tr><th>rekomendowany_termin</th><td>16–23.01.2027 (alt. 9–16.01)</td></tr><tr><th>strategia_budzetowa</th><td>baza w Olang/Valdaora — taniej niż Val Gardena, skibus pod gondolę</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Wariant "Dolomity, ale nie Sella Ronda": ta sama jakość karnetu Dolomiti Superski i widoków, zupełnie inny teren. Baza: <strong>Olang/Valdaora</strong> u podnóża góry — wyraźnie taniej niż doliny Sella Rondy.</p>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>AlpenParks Rehrenberg Viehhofen — PLAN C</h1>
|
||||
<p class="lead">Najtańszy nowy obiekt na liście — przestronne apartamenty z prywatnymi saunami w Viehhofen, wariant rezerwowy o mieszanych opiniach, PLAN C.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../img/alpenparks-rehrenberg/01.jpg" alt="AlpenParks Apartments Rehrenberg" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../img/alpenparks-rehrenberg/01.jpg" alt="AlpenParks Apartments Rehrenberg — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../img/alpenparks-rehrenberg/02.jpg" alt="AlpenParks Apartments Rehrenberg — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../img/alpenparks-rehrenberg/03.jpg" alt="AlpenParks Apartments Rehrenberg — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../img/alpenparks-rehrenberg/04.jpg" alt="AlpenParks Apartments Rehrenberg — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../img/alpenparks-rehrenberg/05.jpg" alt="AlpenParks Apartments Rehrenberg — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>AlpenParks Apartments Rehrenberg</td></tr><tr><th>description</th><td>Najtańszy nowy obiekt na liście — przestronne apartamenty z prywatnymi saunami w Viehhofen, wariant rezerwowy o mieszanych opiniach, PLAN C.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, viehhofen, plan-c</td></tr><tr><th>id</th><td>alpenparks-rehrenberg</td></tr><tr><th>nazwa</th><td>AlpenParks Apartments Rehrenberg</td></tr><tr><th>operator</th><td>AlpenParks</td></tr><tr><th>miejscowosc</th><td>Viehhofen</td></tr><tr><th>gps</th><td>47.3662, 12.7317</td></tr><tr><th>odleglosc_od_wyciagu</th><td>5–10 min pieszo do gondoli zellamseeXpress</td></tr><tr><th>ocena</th><td>4.2/5 (Google) — opinie mieszane</td></tr><tr><th>uklad_dla_grupy</th><td>4 osobne apartamenty</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 100–1 500 € (szacunek, styczeń poza feriami)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 100–1 500 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~950–1 250 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — silnik online na alpenparks.at</td></tr><tr><th>priorytet</th><td>PLAN C (rezerwowy)</td></tr><tr><th>www</th><td><a href="https://www.alpenparks.at" target="_blank" rel="noopener" class="ext">https://www.alpenparks.at</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -79,5 +150,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Tylko jeśli plany A i B nie wypalą: sprawdzić ceny datami 16–23.01.2027.</li><li class="task"><input type="checkbox" disabled> Zweryfikować najnowsze opinie (Booking/Google, filtr: zima 2026).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>AlpenParks Sonnleiten Saalbach</h1>
|
||||
<p class="lead">Duży aparthotel ~2 min pieszo od stoku w Saalbach, z basenem i sauną w cenie — największa szansa na 4 wolne apartamenty w jednym tygodniu.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../img/alpenparks-sonnleiten/01.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../img/alpenparks-sonnleiten/01.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../img/alpenparks-sonnleiten/02.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../img/alpenparks-sonnleiten/03.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../img/alpenparks-sonnleiten/04.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../img/alpenparks-sonnleiten/05.jpg" alt="AlpenParks Hotel & Apartment Sonnleiten — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>AlpenParks Hotel & Apartment Sonnleiten</td></tr><tr><th>description</th><td>Duży aparthotel ~2 min pieszo od stoku w Saalbach, z basenem i sauną w cenie — największa szansa na 4 wolne apartamenty w jednym tygodniu.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, saalbach, aparthotel</td></tr><tr><th>id</th><td>alpenparks-sonnleiten</td></tr><tr><th>nazwa</th><td>AlpenParks Hotel & Apartment Sonnleiten</td></tr><tr><th>operator</th><td>AlpenParks</td></tr><tr><th>miejscowosc</th><td>Saalbach</td></tr><tr><th>gps</th><td>47.3953, 12.6378</td></tr><tr><th>odleglosc_od_wyciagu</th><td>~2 min pieszo do stoku</td></tr><tr><th>ocena</th><td>4.6/5 (Google)</td></tr><tr><th>uklad_dla_grupy</th><td>4 osobne apartamenty (po 1 na rodzinę)</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 400–1 900 € (szacunek, styczeń poza feriami)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 400–1 900 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~1 200–1 500 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — silnik rezerwacyjny online na alpenparks.at</td></tr><tr><th>priorytet</th><td>PLAN A-bis (największa szansa na 4 wolne apartamenty)</td></tr><tr><th>www</th><td><a href="https://www.alpenparks.at" target="_blank" rel="noopener" class="ext">https://www.alpenparks.at</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -79,5 +150,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Wpisać daty 16–23.01.2027 w silnik na alpenparks.at → zrzut cen rzeczywistych dla 4 jednostek.</li><li class="task"><input type="checkbox" disabled> Równolegle mail grupowy z pytaniem o rabat przy 4 apartamentach.</li><li class="task"><input type="checkbox" disabled> Sprawdzić politykę anulacji (sieciówki często mają elastyczne taryfy za dopłatą — przy grupie 14 os. warto).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta-koncept</span>
|
||||
<h1>Dom dla całej grupy (12–14 os.) — koncept</h1>
|
||||
<p class="lead">Koncept wynajęcia jednego domu wakacyjnego dla całej grupy zamiast czterech osobnych apartamentów — klimat chaletowy w połowie ceny Glemm Lodge.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta-koncept</td></tr><tr><th>title</th><td>Duży dom wakacyjny 12–14 os. (do wyszukania)</td></tr><tr><th>description</th><td>Koncept wynajęcia jednego domu wakacyjnego dla całej grupy zamiast czterech osobnych apartamentów — klimat chaletowy w połowie ceny Glemm Lodge.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, dom-wakacyjny, koncept</td></tr><tr><th>id</th><td>dom-dla-grupy</td></tr><tr><th>nazwa</th><td>Duży dom wakacyjny 12–14 os. (do wyszukania)</td></tr><tr><th>miejscowosc</th><td>Viehhofen / Maishofen / strona Fieberbrunn</td></tr><tr><th>uklad_dla_grupy</th><td>jeden dom dla całej grupy</td></tr><tr><th>cena_za_calosc_tydzien</th><td>4 500–6 500 € (szacunek, styczeń poza feriami)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 400–1 900 € (podział od osoby)</td></tr><tr><th>koszt_rodzina_2p1</th><td>~1 100–1 500 € (podział od osoby)</td></tr><tr><th>status_ceny</th><td>SZACUNEK — konkretny dom do znalezienia</td></tr><tr><th>priorytet</th><td>WARIANT RÓWNOLEGŁY (klimat 'wszyscy razem')</td></tr><tr><th>gdzie_szukac</th><td><a href="https://casamundo.pl" target="_blank" rel="noopener" class="ext">casamundo.pl</a>, <a href="https://interhome.pl" target="_blank" rel="noopener" class="ext">interhome.pl</a>, <a href="https://e-domizil.de" target="_blank" rel="noopener" class="ext">e-domizil.de</a>, <a href="https://vrbo.com" target="_blank" rel="noopener" class="ext">vrbo.com</a>, <a href="https://belvilla.com" target="_blank" rel="noopener" class="ext">belvilla.com</a>, <a href="https://allchalets.com" target="_blank" rel="noopener" class="ext">allchalets.com</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Glemm Lodge Apartments — poza budżetem (dane referencyjne)</h1>
|
||||
<p class="lead">Jedyna oferta z w pełni zweryfikowanym cennikiem (~4 370 €/rodzinę) — odrzucona budżetowo, służy jako punkt odniesienia cenowego.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../img/glemm-lodge/01.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../img/glemm-lodge/01.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐ — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../img/glemm-lodge/02.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐ — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../img/glemm-lodge/03.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐ — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../img/glemm-lodge/04.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐ — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../img/glemm-lodge/05.jpg" alt="Glemm Lodge Apartments ⭐⭐⭐⭐ — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Glemm Lodge Apartments ⭐⭐⭐⭐</td></tr><tr><th>description</th><td>Jedyna oferta z w pełni zweryfikowanym cennikiem (~4 370 €/rodzinę) — odrzucona budżetowo, służy jako punkt odniesienia cenowego.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, saalbach, referencja</td></tr><tr><th>id</th><td>glemm-lodge</td></tr><tr><th>nazwa</th><td>Glemm Lodge Apartments ⭐⭐⭐⭐</td></tr><tr><th>operator</th><td>Dieter Weschenfelder (prywatny)</td></tr><tr><th>kontakt</th><td><a href="mailto:info@glemmlodge-apartments.com">info@glemmlodge-apartments.com</a></td></tr><tr><th>miejscowosc</th><td>między Saalbach a Hinterglemm</td></tr><tr><th>adres</th><td>Glemmtaler Landesstr. 263, 5753 Saalbach</td></tr><tr><th>gps</th><td>47.3879, 12.6178</td></tr><tr><th>odleglosc_od_wyciagu</th><td>zjazd trasą 86 pod dom; skibus pod drzwiami (5 min do gondoli)</td></tr><tr><th>ocena</th><td>9.1/10 (Booking), 4.8/5 (Google)</td></tr><tr><th>uklad_dla_grupy</th><td>2 apartamenty po 7 os. (Top 2 + Top 3)</td></tr><tr><th>cena_za_apartament_tydzien</th><td>7 000 € + 500 € sprzątanie + taksy (CENNIK OFICJALNY 26/27)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~4 370 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~3 277 €</td></tr><tr><th>status_ceny</th><td>ZWERYFIKOWANE (cennik + kalendarz obiektu, stan 31.07.2026)</td></tr><tr><th>priorytet</th><td>ODRZUCONA BUDŻETOWO (wraca do gry przy rabacie do ~5 000 €/apartament)</td></tr><tr><th>www</th><td><a href="https://glemmlodge-apartments.com" target="_blank" rel="noopener" class="ext">https://glemmlodge-apartments.com</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>Dlaczego w bazie mimo odrzucenia</h2>
|
||||
|
|
@ -84,5 +155,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Mail do Dietera: cena za 7 os./apartament zamiast 10–12 — jeśli ≤ 5 000 €, wariant wraca do rozważań (~2 900/2 200 € na rodzinę).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Appartementhaus Kristall at SchattbergXpress — PLAN A</h1>
|
||||
<p class="lead">Apartamenty 50 m od gondoli Schattberg X-press w centrum Saalbach — najlepsza lokalizacja w rozważanym budżecie, PLAN A.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Appartementhaus Kristall at SchattbergXpress</td></tr><tr><th>description</th><td>Apartamenty 50 m od gondoli Schattberg X-press w centrum Saalbach — najlepsza lokalizacja w rozważanym budżecie, PLAN A.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, saalbach, plan-a</td></tr><tr><th>id</th><td>kristall-schattbergxpress</td></tr><tr><th>nazwa</th><td>Appartementhaus Kristall at SchattbergXpress</td></tr><tr><th>operator</th><td>All in One Apartments</td></tr><tr><th>miejscowosc</th><td>Saalbach (centrum)</td></tr><tr><th>adres</th><td>Schulstraße 341, 5753 Saalbach</td></tr><tr><th>gps</th><td>47.3903, 12.6355</td></tr><tr><th>odleglosc_od_wyciagu</th><td>50 m — gondola Schattberg X-press</td></tr><tr><th>ocena</th><td>8.5/10 (Booking)</td></tr><tr><th>uklad_dla_grupy</th><td>4 osobne apartamenty (po 1 na rodzinę)</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 500–2 000 € (szacunek, styczeń poza feriami)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 500–2 000 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~1 200–1 600 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — do potwierdzenia zapytaniem</td></tr><tr><th>priorytet</th><td>PLAN A</td></tr><tr><th>www</th><td><a href="https://www.booking.com/hotel/at/appartementhaus-kristall.html" target="_blank" rel="noopener" class="ext">https://www.booking.com/hotel/at/appartementhaus-kristall.html</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Sportchalet Viehhofen — PLAN B (budżetowy faworyt)</h1>
|
||||
<p class="lead">Kameralny obiekt holenderskich gospodarzy w Viehhofen z oceną 5.0 — najlepszy stosunek ceny do jakości, PLAN B.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../img/sportchalet-viehhofen/01.jpg" alt="Sportchalet Viehhofen" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../img/sportchalet-viehhofen/01.jpg" alt="Sportchalet Viehhofen — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../img/sportchalet-viehhofen/02.jpg" alt="Sportchalet Viehhofen — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../img/sportchalet-viehhofen/03.jpg" alt="Sportchalet Viehhofen — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../img/sportchalet-viehhofen/04.jpg" alt="Sportchalet Viehhofen — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../img/sportchalet-viehhofen/05.jpg" alt="Sportchalet Viehhofen — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Sportchalet Viehhofen</td></tr><tr><th>description</th><td>Kameralny obiekt holenderskich gospodarzy w Viehhofen z oceną 5.0 — najlepszy stosunek ceny do jakości, PLAN B.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, viehhofen, plan-b</td></tr><tr><th>id</th><td>sportchalet-viehhofen</td></tr><tr><th>nazwa</th><td>Sportchalet Viehhofen</td></tr><tr><th>operator</th><td>prywatni gospodarze (NL, komunikacja EN)</td></tr><tr><th>miejscowosc</th><td>Viehhofen (3 km od Saalbach)</td></tr><tr><th>gps</th><td>47.3678, 12.7351</td></tr><tr><th>odleglosc_od_wyciagu</th><td>gondola zellamseeXpress za rogiem; skibus do Skicircusu pod domem</td></tr><tr><th>ocena</th><td>5.0/5 (Google)</td></tr><tr><th>uklad_dla_grupy</th><td>3–4 apartamenty w jednym budynku</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 200–1 600 € (szacunek, styczeń poza feriami)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 200–1 600 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~1 000–1 300 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena tylko mailowo</td></tr><tr><th>priorytet</th><td>PLAN B (najlepszy stosunek ceny do jakości)</td></tr><tr><th>www</th><td><a href="https://sportchaletviehhofen.com" target="_blank" rel="noopener" class="ext">https://sportchaletviehhofen.com</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -80,5 +151,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Mail z zapytaniem o dostępność wszystkich apartamentów 16–23.01.2027 (opcja B: 9–16.01) + konfigurację łóżek dla 4+4+3+3.</li><li class="task"><input type="checkbox" disabled> Zapytać o cenę za zajęcie całego obiektu (package deal).</li><li class="task"><input type="checkbox" disabled> Potwierdzić rozkład skibusa w styczniu (częstotliwość rano 8:30–9:30).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
184
pages/porownanie.html
Normal file
184
pages/porownanie.html
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="index.html">← Wszystkie karty</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<nav><a href="start.html">← Start</a> · <a href="index.html">Wszystkie karty</a> · <a href="porownanie.html">Porównanie ofert</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<span class="chip">region</span>
|
||||
<h1>Skicircus Saalbach — informacje o regionie</h1>
|
||||
<p class="lead">Wszystko poza noclegiem: teren narciarski, skipassy ALPIN CARD, dojazd z Warszawy, analiza terminów, koszty życia i budżet całkowity grupy.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>region</td></tr><tr><th>title</th><td>Skicircus Saalbach-Hinterglemm-Leogang-Fieberbrunn</td></tr><tr><th>description</th><td>Wszystko poza noclegiem: teren narciarski, skipassy ALPIN CARD, dojazd z Warszawy, analiza terminów, koszty życia i budżet całkowity grupy.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>region, saalbach, skipass, logistyka</td></tr><tr><th>id</th><td>skicircus-saalbach</td></tr><tr><th>nazwa</th><td>Skicircus Saalbach-Hinterglemm-Leogang-Fieberbrunn</td></tr><tr><th>kraj</th><td>Austria</td></tr><tr><th>region</th><td>Salzburg (Pinzgau)</td></tr><tr><th>sezon</th><td>2026/2027</td></tr><tr><th>grupa</th><td>4 rodziny: 2×(2+2 nastolatki) + 2×(2+1 dziecko) = 14 osób</td></tr><tr><th>wyjazd_z</th><td>Warszawa</td></tr><tr><th>rekomendowany_termin</th><td>16–23.01.2027 (alternatywa: 9–16.01.2027)</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Baza wiedzy wyjazdu narciarskiego zimą 2026/27. Ten dokument zbiera wszystko, co <strong>nie dotyczy konkretnego noclegu</strong>: teren narciarski, skipassy, dojazd, terminy, koszty życia i logistykę grupy. Oferty noclegowe → katalog <a href="index.html"><code>oferty/</code></a>.</p>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Apart Ideal (Serfaus) — PLAN B</h1>
|
||||
<p class="lead">Sześć apartamentów z oceną 4.8/5, 50 m od podziemnego metra w Serfaus — pełne doświadczenie regionu za dopłatą, PLAN B wariantu SFL.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Apart Ideal (Serfaus)</td></tr><tr><th>description</th><td>Sześć apartamentów z oceną 4.8/5, 50 m od podziemnego metra w Serfaus — pełne doświadczenie regionu za dopłatą, PLAN B wariantu SFL.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, sfl, serfaus, plan-b</td></tr><tr><th>id</th><td>apart-ideal-serfaus</td></tr><tr><th>nazwa</th><td>Apart Ideal</td></tr><tr><th>operator</th><td>Daniel & Nina (rodzina Purtscher)</td></tr><tr><th>miejscowosc</th><td>Serfaus (obrzeża)</td></tr><tr><th>odleglosc_od_wyciagu</th><td>50 m do stacji metra 'Tube' → <10 min do kolejki</td></tr><tr><th>ocena</th><td>4.8/5 (13 opinii, portal regionu)</td></tr><tr><th>uklad_dla_grupy</th><td>6 apartamentów w domu → 4 dla nas</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 300–1 800 € (szacunek; Serfaus = najdroższa wieś)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 300–1 800 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~1 100–1 500 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena przez portal regionu</td></tr><tr><th>priorytet</th><td>PLAN B dla wariantu SFL</td></tr><tr><th>www</th><td><a href="https://serfaus-fiss-ladis.at/en/serfaus/apartment/apart-ideal_dr91356" target="_blank" rel="noopener" class="ext">https://serfaus-fiss-ladis.at/en/serfaus/apartment/apart-ideal_dr91356</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../../index.html">← Wszystkie karty</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../../start.html">← Start</a> · <a href="../../index.html">Wszystkie karty</a> · <a href="../../porownanie.html">Porównanie ofert</a> · <a href="../../../viz.html">Graf</a></nav>
|
||||
<span class="chip">oferta</span>
|
||||
<h1>Haus Central / Apart Central (Ladis) — PLAN A</h1>
|
||||
<p class="lead">Dokładnie cztery apartamenty z balkonami przy kolejce Sonnenbahn w najtańszej wsi płaskowyżu — cały dom dla grupy, PLAN A wariantu SFL.</p>
|
||||
<figure class="gallery"><div class="gallery-main"><img src="../../img/haus-central-ladis/01.jpg" alt="Haus Central (Apart Central)" loading="lazy"></div><div class="gallery-thumbs"><button type="button" aria-current="true"><img src="../../img/haus-central-ladis/01.jpg" alt="Haus Central (Apart Central) — zdjęcie 1" loading="lazy"></button><button type="button"><img src="../../img/haus-central-ladis/02.jpg" alt="Haus Central (Apart Central) — zdjęcie 2" loading="lazy"></button><button type="button"><img src="../../img/haus-central-ladis/03.jpg" alt="Haus Central (Apart Central) — zdjęcie 3" loading="lazy"></button><button type="button"><img src="../../img/haus-central-ladis/04.jpg" alt="Haus Central (Apart Central) — zdjęcie 4" loading="lazy"></button><button type="button"><img src="../../img/haus-central-ladis/05.jpg" alt="Haus Central (Apart Central) — zdjęcie 5" loading="lazy"></button></div></figure>
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>oferta</td></tr><tr><th>title</th><td>Haus Central (Apart Central), Ladis</td></tr><tr><th>description</th><td>Dokładnie cztery apartamenty z balkonami przy kolejce Sonnenbahn w najtańszej wsi płaskowyżu — cały dom dla grupy, PLAN A wariantu SFL.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>nocleg, sfl, ladis, plan-a</td></tr><tr><th>id</th><td>haus-central-ladis</td></tr><tr><th>nazwa</th><td>Haus Central (Apart Central)</td></tr><tr><th>operator</th><td>prywatny</td></tr><tr><th>miejscowosc</th><td>Ladis</td></tr><tr><th>odleglosc_od_wyciagu</th><td>przy kolejce Sonnenbahn Ladis-Fiss / przystanku skibusa</td></tr><tr><th>uklad_dla_grupy</th><td>DOKŁADNIE 4 apartamenty, każdy z balkonem — cały dom dla nas</td></tr><tr><th>cena_za_apartament_tydzien</th><td>~1 000–1 400 € (szacunek, niski sezon 16–23.01)</td></tr><tr><th>koszt_rodzina_2p2</th><td>~1 000–1 400 €</td></tr><tr><th>koszt_rodzina_2p1</th><td>~850–1 200 €</td></tr><tr><th>status_ceny</th><td>SZACUNEK — wycena przez serfaus-fiss-ladis.at lub mailowo</td></tr><tr><th>priorytet</th><td>PLAN A dla wariantu SFL</td></tr><tr><th>www</th><td><a href="https://www.serfaus-fiss-ladis.at/en/Accommodation-list/Haus-Central-holiday-apartment-Ladis_ad-40835" target="_blank" rel="noopener" class="ext">https://www.serfaus-fiss-ladis.at/en/Accommodation-list/Haus-Central-holiday-apartment-Ladis_ad-40835</a></td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<h2>W skrócie</h2>
|
||||
|
|
@ -76,5 +147,20 @@ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--line);
|
|||
<ul><li class="task"><input type="checkbox" disabled> Zapytanie przez system serfaus-fiss-ladis.at (przycisk enquiry) lub mail: 4 apartamenty, 16–23.01.2027, 4+4+3+3.</li><li class="task"><input type="checkbox" disabled> Alternatywa w Ladis: Laudegghof (4★ apartamenty, jacuzzi/spa, 8.0 na Expedia, sprzątanie 100–120 €/apartament).</li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
<script>
|
||||
document.querySelectorAll('.gallery').forEach(function (gallery) {
|
||||
var big = gallery.querySelector('.gallery-main img');
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
var img = button.querySelector('img');
|
||||
big.src = img.dataset.full || img.src;
|
||||
gallery.querySelectorAll('.gallery-thumbs button').forEach(function (b) {
|
||||
b.removeAttribute('aria-current');
|
||||
});
|
||||
button.setAttribute('aria-current', 'true');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="../index.html">← Wszystkie karty</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<nav><a href="../start.html">← Start</a> · <a href="../index.html">Wszystkie karty</a> · <a href="../porownanie.html">Porównanie ofert</a> · <a href="../../viz.html">Graf</a></nav>
|
||||
<span class="chip">region</span>
|
||||
<h1>Serfaus-Fiss-Ladis — informacje o regionie</h1>
|
||||
<p class="lead">Wariant alternatywny i najtańszy budżetowo: 214 km na słonecznym płaskowyżu, infrastruktura skrojona pod rodziny, a termin 16–23.01 to u nich oficjalnie niski sezon.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>region</td></tr><tr><th>title</th><td>Serfaus-Fiss-Ladis (SFL)</td></tr><tr><th>description</th><td>Wariant alternatywny i najtańszy budżetowo: 214 km na słonecznym płaskowyżu, infrastruktura skrojona pod rodziny, a termin 16–23.01 to u nich oficjalnie niski sezon.</td></tr><tr><th>timestamp</th><td>2026-08-01T00:00:00Z</td></tr><tr><th>tags</th><td>region, sfl, wariant, skipass, logistyka</td></tr><tr><th>id</th><td>serfaus-fiss-ladis</td></tr><tr><th>nazwa</th><td>Serfaus-Fiss-Ladis (SFL)</td></tr><tr><th>kraj</th><td>Austria</td></tr><tr><th>region</th><td>Tyrol (górna dolina Innu)</td></tr><tr><th>sezon</th><td>2026/2027</td></tr><tr><th>grupa</th><td>4 rodziny (2×2+2, 2×2+1) = 14 osób</td></tr><tr><th>wyjazd_z</th><td>Warszawa</td></tr><tr><th>rekomendowany_termin</th><td>16–23.01.2027 — u nich to NISKI SEZON (taniej!)</td></tr><tr><th>strategia_budzetowa</th><td>mieszkać w Fiss lub Ladis (do ~30% taniej niż Serfaus)</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-08-01</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Najbardziej "rodzinny" kurort Alp: słoneczny płaskowyż na 1 200–1 400 m, <strong>214 km tras i 68 wyciągów</strong>, śnieg pewny do wiosny (jazda do 2 820 m). Wielokrotny zwycięzca rankingów family-friendly. Dla naszej mieszanki wiekowej (nastolatki + młodsze dzieci) teren skrojony idealnie: dwa snowparki (Freestyle Circus Serfaus, Fun Area Fiss-Ladis), funslope'y, a w Serfaus kultowe metro podziemne (wioska bez aut).</p>
|
||||
|
|
|
|||
143
pages/start.html
Normal file
143
pages/start.html
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<!doctype html>
|
||||
<html lang="pl">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Start — Ski 2027 — Skicircus Saalbach</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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="/pages/index.html">Wszystkie karty</a> · <a href="/pages/porownanie.html">Porównanie ofert</a> · <a href="/viz.html">Graf</a></nav>
|
||||
<h1>Ski 2027 — Skicircus Saalbach</h1>
|
||||
<p class="lead">16–23.01.2027, 4 rodziny / 14 osób, wyjazd z Warszawy. Od czego zacząć:</p>
|
||||
<ul class="tiles"><li><a href="/pages/porownanie.html">Porównanie ofert<small>Wszystkie noclegi w jednej sortowalnej tabeli: ceny, status ceny, priorytet, odległość od wyciągu.</small></a></li><li><a href="/pages/wyjazd.html">Analiza wariantów<small>Cztery regiony obok siebie: skipassy, dojazd, budżet całkowity grupy i wnioski przekrojowe.</small></a></li><li><a href="/viz.html">Graf powiązań<small>Struktura bazy wiedzy — koncepty, linki między nimi i adresy zewnętrzne. Załącznik, nie punkt wejścia.</small></a></li></ul>
|
||||
<h2>Regiony</h2>
|
||||
<p class="lead">Karta regionu = skipassy, dojazd, terminy, koszty życia i budżet całkowity.</p>
|
||||
<ul class="regions"><li><a href="/pages/region.html" class="hero" style="background-image:linear-gradient(rgba(15,23,42,.62),rgba(15,23,42,.72)),url('/pages/img/alpenparks-sonnleiten/01.jpg')">Saalbach (baza)<small>Budżet grupy: ~14 000–18 000 €</small></a></li><li><a href="/pages/ischgl/region.html" class="hero" style="background-image:linear-gradient(rgba(15,23,42,.62),rgba(15,23,42,.72)),url('/pages/img/apartment-brandau-kappl/01.jpg')">Ischgl/Paznaun<small>Budżet grupy: ~13 500–16 000 €</small></a></li><li><a href="/pages/kronplatz/region.html" class="hero" style="background-image:linear-gradient(rgba(15,23,42,.62),rgba(15,23,42,.72)),url('/pages/img/haeuslerhof-valdaora/01.jpg')">Kronplatz<small>Budżet grupy: ~13 200–16 300 €</small></a></li><li><a href="/pages/sfl/region.html" class="hero" style="background-image:linear-gradient(rgba(15,23,42,.62),rgba(15,23,42,.72)),url('/pages/img/haus-central-ladis/01.jpg')">SFL<small>Budżet grupy: ~12 800–15 700 €</small></a></li></ul>
|
||||
<footer>Wygenerowane z bundle'a OKF przez <code>gen_pages.py</code> — nie edytuj
|
||||
tych plików, edytuj <code>.md</code> i przegeneruj.</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="index.html">← Wszystkie karty</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<nav><a href="start.html">← Start</a> · <a href="index.html">Wszystkie karty</a> · <a href="porownanie.html">Porównanie ofert</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<span class="chip">narzedzie</span>
|
||||
<h1>Szablon maila — zapytanie grupowe (EN)</h1>
|
||||
<p class="lead">Gotowy angielski mail z zapytaniem o 4 apartamenty dla 14 osób w terminie 16–23.01.2027 wraz z notatkami negocjacyjnymi.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>narzedzie</td></tr><tr><th>title</th><td>Szablon zapytania grupowego (EN)</td></tr><tr><th>description</th><td>Gotowy angielski mail z zapytaniem o 4 apartamenty dla 14 osób w terminie 16–23.01.2027 wraz z notatkami negocjacyjnymi.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>szablon, komunikacja, rezerwacja</td></tr><tr><th>id</th><td>szablon-maila</td></tr><tr><th>nazwa</th><td>Szablon zapytania grupowego (EN)</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Wysłać ten sam mail do 2–3 obiektów równolegle, podmieniając [pola]. Wersja uniwersalna pod 4 apartamenty; przy Sportchalet/Glemm Lodge dopisać pytanie o zajęcie całego obiektu.</p>
|
||||
|
|
|
|||
|
|
@ -57,13 +57,84 @@ th { background: var(--chip); font-weight: 600; }
|
|||
.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(3, 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); }
|
||||
.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); }
|
||||
|
||||
/* 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; }
|
||||
|
||||
/* 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="index.html">← Wszystkie karty</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<nav><a href="start.html">← Start</a> · <a href="index.html">Wszystkie karty</a> · <a href="porownanie.html">Porównanie ofert</a> · <a href="../viz.html">Graf</a></nav>
|
||||
<span class="chip">Trip</span>
|
||||
<h1>Ski 2027 — Skicircus Saalbach</h1>
|
||||
<p class="lead">Wyjazd narciarski 4 rodzin (14 osób) z Warszawy do Skicircus Saalbach w styczniu 2027, z celem budżetowym ~2 000 € za nocleg na rodzinę.</p>
|
||||
|
||||
<details class="meta" open><summary>Metadane (frontmatter)</summary><div class="table-wrap"><table><tbody><tr><th>type</th><td>Trip</td></tr><tr><th>title</th><td>Ski 2027 — Skicircus Saalbach</td></tr><tr><th>description</th><td>Wyjazd narciarski 4 rodzin (14 osób) z Warszawy do Skicircus Saalbach w styczniu 2027, z celem budżetowym ~2 000 € za nocleg na rodzinę.</td></tr><tr><th>timestamp</th><td>2026-07-31T00:00:00Z</td></tr><tr><th>tags</th><td>wyjazd, saalbach, 2027, planowanie</td></tr><tr><th>nazwa</th><td>Ski 2027 — Skicircus Saalbach: baza wiedzy wyjazdu</td></tr><tr><th>grupa</th><td>4 rodziny (2×2+2, 2×2+1) = 14 osób, wyjazd z Warszawy</td></tr><tr><th>termin</th><td>16–23.01.2027 (alt. 9–16.01)</td></tr><tr><th>ostatnia_aktualizacja</th><td>2026-07-31</td></tr></tbody></table></div></details>
|
||||
|
||||
<p>Wyjazd narciarski 4 rodzin: 2×(2+2 nastolatki) + 2×(2+1 dziecko) = <strong>14 osób</strong>, wyjazd samochodami z Warszawy.</p>
|
||||
|
|
|
|||
Loading…
Reference in a new issue