From 0d0acdb3a4ad0169bdaeb2a4ab1256cf099052ce Mon Sep 17 00:00:00 2001 From: oskar Date: Mon, 3 Aug 2026 17:37:14 +0200 Subject: [PATCH] =?UTF-8?q?pages/linki.html:=20spis=20wszystkich=20adres?= =?UTF-8?q?=C3=B3w=20wystawki=20do=20wys=C5=82ania?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nowa strona z pełnymi URL-ami wszystkich 24 stron na https://narty27.kapala.org (BASE_URL — jedyne miejsce z domeną w kodzie), w sekcjach: Wejście, Regiony, Oferty per region z priorytetem PLAN A/B z frontmattera, Pozostałe. Każda pozycja to klikalny link, widoczny adres tekstem do skopiowania i krótki opis. Lista powstaje z tych samych danych co porownanie.html i start.html — regiony z wyjazd.md, oferty z katalogów i frontmatterów, "Pozostałe" to koncepty, których nie złapała żadna wcześniejsza sekcja. Nowa oferta wpada na listę sama przy regeneracji. Przyciski "Kopiuj wszystkie" (plain text: nazwa sekcji + adresy) i "Kopiuj link startowy"; navigator.clipboard z odwrotem na zaznaczenie, bo po http z adresu IP nie ma bezpiecznego kontekstu. Do nawigacji każdej strony i na start.html doszła pozycja "Linki". Co-Authored-By: Claude Opus 5 (1M context) --- gen_pages.py | 259 +++++++++++++++++- pages/index.html | 22 +- .../oferty/apartment-brandau-kappl.html | 22 +- pages/ischgl/oferty/apartments-kappl.html | 22 +- pages/ischgl/region.html | 22 +- .../oferty/haeuslerhof-valdaora.html | 22 +- .../oferty/residence-corones-valdaora.html | 22 +- pages/kronplatz/region.html | 22 +- pages/linki.html | 219 +++++++++++++++ pages/oferty/alpenparks-rehrenberg.html | 22 +- pages/oferty/alpenparks-sonnleiten.html | 22 +- pages/oferty/dom-dla-grupy.html | 22 +- pages/oferty/glemm-lodge.html | 22 +- pages/oferty/kristall-schattbergxpress.html | 22 +- pages/oferty/sportchalet-viehhofen.html | 22 +- pages/porownanie.html | 22 +- pages/region.html | 22 +- pages/sfl/oferty/apart-ideal-serfaus.html | 22 +- pages/sfl/oferty/haus-central-ladis.html | 22 +- pages/sfl/region.html | 22 +- pages/start.html | 24 +- pages/szablon-maila.html | 22 +- pages/wyjazd.html | 22 +- 23 files changed, 898 insertions(+), 44 deletions(-) create mode 100644 pages/linki.html diff --git a/gen_pages.py b/gen_pages.py index 574899f..309e439 100644 --- a/gen_pages.py +++ b/gen_pages.py @@ -54,6 +54,13 @@ PAGES_DIR = BUNDLE_ROOT / PAGES_DIRNAME BUNDLE_NAME = "Ski 2027 — Skicircus Saalbach" RESERVED = {"index.md", "log.md"} +# Publiczny adres wystawki — jedyne miejsce, w którym siedzi domena. Używa go +# WYŁĄCZNIE pages/linki.html (spis do kopiowania i wysyłania grupie); wszystkie +# pozostałe strony linkują względnie albo root-absolutnie, żeby działały tak +# samo lokalnie (python3 -m http.server) i po deployu. Zmiana domeny = zmiana +# tej jednej stałej i regeneracja. +BASE_URL = "https://narty27.kapala.org" + try: # renderer preferowany, jeśli dostępny w systemie import markdown as _markdown_lib except ModuleNotFoundError: @@ -382,7 +389,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -440,6 +447,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -504,6 +529,7 @@ def _page_shell( f'← Start · ' f'Wszystkie karty · ' f'Porównanie ofert · ' + f'Linki · ' f'Graf' ) css_class = f' class="{body_class}"' if body_class else "" @@ -1043,6 +1069,9 @@ TILES = [ ("/viz.html", "Graf powiązań", "Struktura bazy wiedzy — koncepty, linki między nimi i adresy " "zewnętrzne. Załącznik, nie punkt wejścia."), + ("/pages/linki.html", "Linki do wysłania", + "Pełne adresy wszystkich stron wystawki, z przyciskiem „kopiuj” — " + "do wrzucenia grupie."), ] @@ -1092,6 +1121,7 @@ def render_start(concepts: list[Concept], regions: list[Region]) -> str: nav = ( 'Wszystkie karty · ' 'Porównanie ofert · ' + 'Linki · ' 'Graf' ) return _page_shell("Start", 0, content, nav=nav) @@ -1119,6 +1149,232 @@ def region_hero(concepts: list[Concept], region: Region) -> str: return "" +# --- spis linków do wysłania ------------------------------------------ + + +@dataclass +class Link: + url: str + label: str + description: str = "" + + +def site_url(path: str) -> str: + """Ścieżka w bundle'u → pełny adres publiczny.""" + return f"{BASE_URL}/{path.lstrip('/')}" + + +def _describe(concept: Concept | None, limit: int = 110) -> str: + """Opis konceptu przycięty do jednej linijki — bez skracania po separatorze, + bo w opisach nawias i myślnik niosą treść (inaczej niż w polach tabeli).""" + if not concept: + return "" + text = str(concept.frontmatter.get("description") or "") + if len(text) > limit: + text = text[: limit - 1].rsplit(" ", 1)[0] + "…" + return text + + +def link_sections( + concepts: list[Concept], regions: list[Region] +) -> list[tuple[str, list[Link]]]: + """Spis wszystkich stron wystawki — z tych samych danych co reszta stron. + + Nic tu nie jest wypisane ręcznie: regiony biorą się z `wyjazd.md`, oferty + z katalogów i frontmatterów, a „Pozostałe” to po prostu koncepty, których + nie złapała żadna wcześniejsza sekcja. Nowa oferta czy piąty wariant + pojawiają się na liście przy najbliższej regeneracji, same z siebie. + """ + by_id = {c.id: c for c in concepts} + used: set[str] = set() + + entry = [ + Link(site_url(""), "Start", "Landing decyzyjny — od tego zaczyna grupa."), + Link( + site_url("pages/porownanie.html"), + "Porównanie ofert", + "Wszystkie noclegi w jednej sortowalnej tabeli, ze zdjęciami.", + ), + Link( + site_url("pages/wyjazd.html"), + "Analiza wariantów", + _describe(by_id.get("wyjazd")) or "Cztery regiony obok siebie.", + ), + Link( + site_url("pages/index.html"), + "Spis wszystkich kart", + "Każdy dokument bazy wiedzy w jednym miejscu.", + ), + Link(site_url("viz.html"), "Graf powiązań", "Struktura bazy — załącznik."), + Link(site_url("pages/linki.html"), "Ten spis linków", "Strona, którą czytasz."), + ] + used.add("wyjazd") + + sections: list[tuple[str, list[Link]]] = [("Wejście", entry)] + + region_links = [] + for region in regions: + concept = by_id.get(region.concept_id) + if not concept: + continue + used.add(region.concept_id) + budget = f"Budżet grupy {region.budget}. " if region.budget else "" + region_links.append( + Link( + site_url(f"{PAGES_DIRNAME}/{concept.page}"), + region.label, + budget + _describe(concept, 90), + ) + ) + if region_links: + sections.append(("Regiony", region_links)) + + for region in regions: + offers = [ + c + for c in concepts + if is_offer(c) + and posixpath.dirname(posixpath.dirname(c.id)) == region.dirname + ] + if not offers: + continue + links = [] + for concept in sorted(offers, key=_priority_rank): + used.add(concept.id) + priority = _shorten(str(concept.frontmatter.get("priorytet") or "")) + place = str(concept.frontmatter.get("miejscowosc") or "") + links.append( + Link( + site_url(f"{PAGES_DIRNAME}/{concept.page}"), + str(concept.frontmatter.get("nazwa") or concept.title), + " · ".join(part for part in (priority, place) if part), + ) + ) + sections.append((f"Oferty — {region.label}", links)) + + rest = [ + Link( + site_url(f"{PAGES_DIRNAME}/{c.page}"), + c.title, + _describe(c), + ) + for c in concepts + if c.id not in used + ] + rest.append( + Link( + site_url(f"{PAGES_DIRNAME}/start.html"), + "Start pod własną ścieżką", + "Ten sam landing co adres główny — przydaje się w zakładkach.", + ) + ) + sections.append(("Pozostałe", rest)) + return sections + + +def _priority_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.title) + return (9, concept.title) + + +LINKS_JS = """ +function collectAll() { + var lines = []; + document.querySelectorAll('section[data-section]').forEach(function (section) { + lines.push(section.dataset.section + ':'); + section.querySelectorAll('.url').forEach(function (code) { + lines.push(code.textContent.trim()); + }); + lines.push(''); + }); + return lines.join('\\n').trim() + '\\n'; +} + +function say(message) { + var status = document.querySelector('.copy-status'); + status.textContent = message; + window.setTimeout(function () { + if (status.textContent === message) status.textContent = ''; + }, 4000); +} + +// navigator.clipboard istnieje tylko w bezpiecznym kontekście (https albo +// localhost) — po http z adresu IP trzeba starą drogą przez zaznaczenie. +function copyLegacy(text) { + var area = document.createElement('textarea'); + area.value = text; + area.setAttribute('readonly', ''); + area.style.position = 'fixed'; + area.style.top = '-1000px'; + document.body.appendChild(area); + area.select(); + var copied = false; + try { copied = document.execCommand('copy'); } catch (error) { copied = false; } + document.body.removeChild(area); + return copied; +} + +function copyText(text, what) { + var ok = function () { say('Skopiowane: ' + what); }; + var no = function () { say('Nie udało się skopiować — zaznacz adres ręcznie.'); }; + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then(ok, function () { + if (copyLegacy(text)) { ok(); } else { no(); } + }); + } else if (copyLegacy(text)) { ok(); } else { no(); } +} + +document.getElementById('copy-all').addEventListener('click', function () { + copyText(collectAll(), 'cała lista'); +}); +document.getElementById('copy-start').addEventListener('click', function () { + copyText(this.dataset.url, 'link startowy'); +}); +""" + + +def render_links(concepts: list[Concept], regions: list[Region]) -> str: + sections = link_sections(concepts, regions) + blocks = [] + total = 0 + for title, links in sections: + if not links: + continue + items = [] + for link in links: + total += 1 + escaped = html.escape(link.url, quote=True) + description = ( + f"

{html.escape(link.description)}

" if link.description else "" + ) + items.append( + f'
  • {html.escape(link.label)}' + f'{html.escape(link.url)}{description}
  • ' + ) + blocks.append( + f'
    ' + f"

    {html.escape(title)}

    " + f'
    ' + ) + + content = ( + "

    Linki do wysłania

    \n" + f'

    Wszystkie {total} stron wystawki jako pełne adresy — ' + "do skopiowania i wrzucenia grupie. Lista powstaje z tych samych danych " + "co reszta stron, więc nowa oferta pojawi się tu sama.

    \n" + '
    ' + '' + f'" + '' + "
    \n" + "\n".join(blocks) + ) + return _page_shell("Linki", 0, content, script=LINKS_JS) + + # --- raport braków ---------------------------------------------------- @@ -1181,6 +1437,7 @@ def main() -> int: for name, markup in ( ("index.html", render_index(concepts)), ("porownanie.html", render_comparison(concepts, regions)), + ("linki.html", render_links(concepts, regions)), ("start.html", render_start(concepts, regions)), ): path = PAGES_DIR / name diff --git a/pages/index.html b/pages/index.html index 5de6ea4..67478b6 100644 --- a/pages/index.html +++ b/pages/index.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - +

    Ski 2027 — Skicircus Saalbach

    Karty konceptów bundle'a — pełna treść każdego dokumentu (ceny, dostępność, plusy/minusy). Struktura powiązań: graf.

    Wyjazd i wiedza ogólna

    diff --git a/pages/ischgl/oferty/apartment-brandau-kappl.html b/pages/ischgl/oferty/apartment-brandau-kappl.html index 400ab81..615492a 100644 --- a/pages/ischgl/oferty/apartment-brandau-kappl.html +++ b/pages/ischgl/oferty/apartment-brandau-kappl.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Apartment Brandau (Kappl) — PLAN A

    Rodzinny dom z sześcioma apartamentami w Kappl — bierzemy cztery, ceny doliny zamiast cen Ischgl, PLAN A wariantu Ischgl.

    diff --git a/pages/ischgl/oferty/apartments-kappl.html b/pages/ischgl/oferty/apartments-kappl.html index de7c260..db019df 100644 --- a/pages/ischgl/oferty/apartments-kappl.html +++ b/pages/ischgl/oferty/apartments-kappl.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Apartments Kappl — PLAN B

    Tyrolski dom apartamentowy 100 m od przystanku skibusa bez przesiadki do Ischgl — liczba wolnych jednostek do potwierdzenia, PLAN B wariantu Ischgl.

    diff --git a/pages/ischgl/region.html b/pages/ischgl/region.html index 2fe8a64..fbd7bfd 100644 --- a/pages/ischgl/region.html +++ b/pages/ischgl/region.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + region

    Ischgl / Paznaun — informacje o regionie

    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.

    diff --git a/pages/kronplatz/oferty/haeuslerhof-valdaora.html b/pages/kronplatz/oferty/haeuslerhof-valdaora.html index 0f5e867..417893d 100644 --- a/pages/kronplatz/oferty/haeuslerhof-valdaora.html +++ b/pages/kronplatz/oferty/haeuslerhof-valdaora.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Häuslerhof (Valdaora) — PLAN A

    Gospodarstwo z dokładnie czterema apartamentami — grupa zajmuje cały obiekt, anulacja bezpłatna do 2 miesięcy przed, PLAN A wariantu Kronplatz.

    diff --git a/pages/kronplatz/oferty/residence-corones-valdaora.html b/pages/kronplatz/oferty/residence-corones-valdaora.html index bfbe1c8..5610ed0 100644 --- a/pages/kronplatz/oferty/residence-corones-valdaora.html +++ b/pages/kronplatz/oferty/residence-corones-valdaora.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Residence Corones (Valdaora) — PLAN B

    Rezydencja w centrum Valdaory z przystankiem skibusa przed domem i Digital Guestpassem w cenie — PLAN B wariantu Kronplatz.

    diff --git a/pages/kronplatz/region.html b/pages/kronplatz/region.html index 0ffd6be..c80a673 100644 --- a/pages/kronplatz/region.html +++ b/pages/kronplatz/region.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + region

    Kronplatz — informacje o regionie

    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.

    diff --git a/pages/linki.html b/pages/linki.html new file mode 100644 index 0000000..d65e2a6 --- /dev/null +++ b/pages/linki.html @@ -0,0 +1,219 @@ + + + + + +Linki — Ski 2027 — Skicircus Saalbach + + + + +

    Linki do wysłania

    +

    Wszystkie 24 stron wystawki jako pełne adresy — do skopiowania i wrzucenia grupie. Lista powstaje z tych samych danych co reszta stron, więc nowa oferta pojawi się tu sama.

    +
    +

    Wejście

    +

    Regiony

    +

    Oferty — Saalbach (baza)

    +

    Oferty — Ischgl/Paznaun

    +

    Oferty — Kronplatz

    +

    Oferty — SFL

    +

    Pozostałe

    +
    Wygenerowane z bundle'a OKF przez gen_pages.py — nie edytuj +tych plików, edytuj .md i przegeneruj.
    + + + diff --git a/pages/oferty/alpenparks-rehrenberg.html b/pages/oferty/alpenparks-rehrenberg.html index d7394a1..0f485b1 100644 --- a/pages/oferty/alpenparks-rehrenberg.html +++ b/pages/oferty/alpenparks-rehrenberg.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    AlpenParks Rehrenberg Viehhofen — PLAN C

    Najtańszy nowy obiekt na liście — przestronne apartamenty z prywatnymi saunami w Viehhofen, wariant rezerwowy o mieszanych opiniach, PLAN C.

    diff --git a/pages/oferty/alpenparks-sonnleiten.html b/pages/oferty/alpenparks-sonnleiten.html index 7858476..d265ebe 100644 --- a/pages/oferty/alpenparks-sonnleiten.html +++ b/pages/oferty/alpenparks-sonnleiten.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    AlpenParks Sonnleiten Saalbach

    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.

    diff --git a/pages/oferty/dom-dla-grupy.html b/pages/oferty/dom-dla-grupy.html index 32a4274..214454a 100644 --- a/pages/oferty/dom-dla-grupy.html +++ b/pages/oferty/dom-dla-grupy.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta-koncept

    Dom dla całej grupy (12–14 os.) — koncept

    Koncept wynajęcia jednego domu wakacyjnego dla całej grupy zamiast czterech osobnych apartamentów — klimat chaletowy w połowie ceny Glemm Lodge.

    diff --git a/pages/oferty/glemm-lodge.html b/pages/oferty/glemm-lodge.html index 1f5e5ae..a6984eb 100644 --- a/pages/oferty/glemm-lodge.html +++ b/pages/oferty/glemm-lodge.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Glemm Lodge Apartments — poza budżetem (dane referencyjne)

    Jedyna oferta z w pełni zweryfikowanym cennikiem (~4 370 €/rodzinę) — odrzucona budżetowo, służy jako punkt odniesienia cenowego.

    diff --git a/pages/oferty/kristall-schattbergxpress.html b/pages/oferty/kristall-schattbergxpress.html index c42680c..80bbf3c 100644 --- a/pages/oferty/kristall-schattbergxpress.html +++ b/pages/oferty/kristall-schattbergxpress.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Appartementhaus Kristall at SchattbergXpress — PLAN A

    Apartamenty 50 m od gondoli Schattberg X-press w centrum Saalbach — najlepsza lokalizacja w rozważanym budżecie, PLAN A.

    diff --git a/pages/oferty/sportchalet-viehhofen.html b/pages/oferty/sportchalet-viehhofen.html index 8ca46bb..fd543ef 100644 --- a/pages/oferty/sportchalet-viehhofen.html +++ b/pages/oferty/sportchalet-viehhofen.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Sportchalet Viehhofen — PLAN B (budżetowy faworyt)

    Kameralny obiekt holenderskich gospodarzy w Viehhofen z oceną 5.0 — najlepszy stosunek ceny do jakości, PLAN B.

    diff --git a/pages/porownanie.html b/pages/porownanie.html index 3f6745e..5b0b1ef 100644 --- a/pages/porownanie.html +++ b/pages/porownanie.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - +

    Porównanie ofert

    Wszystkie 12 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.

    Regiony w skrócie

    diff --git a/pages/region.html b/pages/region.html index 19f4607..a1130da 100644 --- a/pages/region.html +++ b/pages/region.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + region

    Skicircus Saalbach — informacje o regionie

    Wszystko poza noclegiem: teren narciarski, skipassy ALPIN CARD, dojazd z Warszawy, analiza terminów, koszty życia i budżet całkowity grupy.

    diff --git a/pages/sfl/oferty/apart-ideal-serfaus.html b/pages/sfl/oferty/apart-ideal-serfaus.html index 0b567f8..440cd3d 100644 --- a/pages/sfl/oferty/apart-ideal-serfaus.html +++ b/pages/sfl/oferty/apart-ideal-serfaus.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Apart Ideal (Serfaus) — PLAN B

    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.

    diff --git a/pages/sfl/oferty/haus-central-ladis.html b/pages/sfl/oferty/haus-central-ladis.html index 8ca15e4..b8560af 100644 --- a/pages/sfl/oferty/haus-central-ladis.html +++ b/pages/sfl/oferty/haus-central-ladis.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + oferta

    Haus Central / Apart Central (Ladis) — PLAN A

    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.

    diff --git a/pages/sfl/region.html b/pages/sfl/region.html index a6ddb21..c03f3d2 100644 --- a/pages/sfl/region.html +++ b/pages/sfl/region.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + region

    Serfaus-Fiss-Ladis — informacje o regionie

    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.

    diff --git a/pages/start.html b/pages/start.html index f49c354..9275d65 100644 --- a/pages/start.html +++ b/pages/start.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,10 +148,10 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - +

    Ski 2027 — Skicircus Saalbach

    16–23.01.2027, 4 rodziny / 14 osób, wyjazd z Warszawy. Od czego zacząć:

    - +

    Regiony

    Karta regionu = skipassy, dojazd, terminy, koszty życia i budżet całkowity.

    diff --git a/pages/szablon-maila.html b/pages/szablon-maila.html index a10b3b6..b1b5616 100644 --- a/pages/szablon-maila.html +++ b/pages/szablon-maila.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + narzedzie

    Szablon maila — zapytanie grupowe (EN)

    Gotowy angielski mail z zapytaniem o 4 apartamenty dla 14 osób w terminie 16–23.01.2027 wraz z notatkami negocjacyjnymi.

    diff --git a/pages/wyjazd.html b/pages/wyjazd.html index 551771d..0886b2c 100644 --- a/pages/wyjazd.html +++ b/pages/wyjazd.html @@ -64,7 +64,7 @@ body.wide { max-width: 72rem; } .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); } + .tiles { grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); } .regions { grid-template-columns: repeat(2, 1fr); } } .tiles a, .regions a { display: block; border: 1px solid var(--line); border-radius: 10px; @@ -122,6 +122,24 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius .gallery-thumbs img { width: 5.5rem; height: 3.6rem; object-fit: cover; border-radius: 4px; display: block; } +/* spis linków do kopiowania */ +.copybar { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; + margin: 1rem 0 1.5rem; } +.copybar button { font: inherit; font-size: .9rem; padding: .45rem .9rem; + border: 1px solid var(--accent); border-radius: 8px; + background: var(--accent); color: #f8fafc; cursor: pointer; } +.copybar button + button { background: none; color: var(--accent); } +.copybar button:hover { filter: brightness(1.08); } +.copy-status { font-size: .85rem; color: var(--muted); } +.links { list-style: none; padding: 0; margin: .5rem 0 0; } +.links li { padding: .55rem 0; border-bottom: 1px solid var(--line); } +.links li:last-child { border-bottom: 0; } +.links a { font-weight: 600; text-decoration: none; } +.links code.url { display: block; margin: .2rem 0; font-size: .82rem; + background: none; padding: 0; color: var(--muted); + word-break: break-all; user-select: all; } +.links p { margin: 0; font-size: .85rem; color: var(--muted); } + /* kafel regionu ze zdjęciem w tle */ .regions a.hero { background-size: cover; background-position: center; border-color: transparent; color: #f8fafc; min-height: 6.5rem; @@ -130,7 +148,7 @@ td.thumb .placeholder { display: flex; width: 120px; height: 80px; border-radius - + Trip

    Ski 2027 — Skicircus Saalbach

    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ę.