8 <title>Karta obiektu — {{ entity.label }}</title>
9 <meta name="viewport" content="width=device-width, initial-scale=1">
10 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.css" />
11 <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.min.js"></script>
12 <script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
14 <link rel="preconnect" href="https://fonts.googleapis.com">
15 <link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,500;0,6..72,600;1,6..72,400&family=Courier+Prime:wght@400;700&display=swap" rel="stylesheet">
18 *{ box-sizing:border-box; }
23 font-family:'Newsreader', serif;
27 /* ---------- PAGE / PRINT ---------- */
28 @page{ size:A4; margin:0; }
34 padding:16mm 16mm 12mm 16mm;
38 /* ---------- HEADER ---------- */
41 justify-content:space-between;
42 align-items:flex-start;
43 border-bottom:1.5px solid #000;
50 font-family:'Courier Prime', monospace;
53 text-transform:uppercase;
64 /* pieczęć z kodem QR do Wikipedii — obrazek dostarczony przez serwer */
71 width:26mm; height:26mm;
73 border:1.4px solid #000;
74 outline:1px dashed #000;
76 display:flex; align-items:center; justify-content:center;
79 .stamp .seal-ring img{
80 width:100%; height:100%;
89 font-family:'Courier Prime', monospace;
92 text-transform:uppercase;
96 /* ---------- MAPA + ZDJĘCIA ---------- */
99 grid-template-columns:1.1fr 0.9fr;
103 /* gdy nie ma mapy: zdjęcia zajmują całą szerokość */
104 .page.no-map .visuals{
105 grid-template-columns:1fr;
107 .page.no-map .illustrations{
112 .illustrations .label{
113 font-family:'Courier Prime', monospace;
115 letter-spacing:.12em;
116 text-transform:uppercase;
124 border:1px solid #000;
128 font-family:'Courier Prime', monospace;
133 /* zdjęcia "przyklejone", dowolne proporcje — CSS columns robi
134 naturalny układ pod różne wysokości (portrait/landscape mix) */
144 padding:2mm 2mm 5mm 2mm;
145 box-shadow:0 2px 6px rgba(0,0,0,.22);
146 border:1px solid #000;
148 .illu:nth-child(odd){ transform:rotate(-2deg); }
149 .illu:nth-child(even){ transform:rotate(1.6deg); }
151 position:absolute; top:-2mm; left:50%; transform:translateX(-50%) rotate(-2deg);
152 width:14mm; height:5mm;
153 background:rgba(0,0,0,.35);
154 border:1px solid rgba(0,0,0,.5);
157 width:100%; height:auto; display:block; /* naturalne proporcje, bez cropu */
161 font-family:'Courier Prime', monospace;
167 /* ---------- PRZERWA ---------- */
169 display:flex; align-items:center; gap:2mm;
172 .tear .dots{ flex:1; border-top:2px dashed #000; }
174 font-family:'Courier Prime', monospace;
177 text-transform:uppercase;
181 /* ---------- FRAGMENTY (bilety) ---------- */
184 grid-template-columns:16mm 1fr 22mm;
188 border-bottom:1px dotted #000;
191 .fragment:last-child{ border-bottom:none; }
194 width:16mm; height:22mm;
195 border:1px solid #000;
196 box-shadow:0 2px 5px rgba(0,0,0,.28);
199 .cover img{ width:100%; height:100%; object-fit:cover; display:block; }
202 font-family:'Courier Prime', monospace;
207 .frag-text .book b{ color:#000; font-weight:700; }
215 font-family:'Courier Prime', monospace;
220 .qr-mini{ width:22mm; text-align:center; }
222 width:20mm; height:20mm;
225 .qr-mini .box img{ width:100%; height:100%; display:block; }
228 font-family:'Courier Prime', monospace;
230 letter-spacing:.06em;
231 text-transform:uppercase;
239 <div class="page {% if not entity.lat %}no-map{% endif %}" id="page">
241 <div class="title-block">
242 <div class="kicker">Mapa Wolnych Lektur</div>
243 <h1>{{ entity.label }}</h1>
245 {% if entity.wikipedia_link %}
247 <div class="seal-ring qr" data-qr="{{ entity.wikipedia_link }}">
249 <div class="cap">Wikipedia →</div>
254 <div class="visuals">
257 <div class="map-frame">
258 <div class="label">Lokalizacja</div>
260 <div id="map" data-lat="{{ entity.lat }}" data-lon="{{ entity.lon }}"></div>
262 <div class="coords">{{ coords_label }}</div>
266 {% if entity.get_images %}
267 <div class="illustrations">
268 <div class="label">Ilustracje</div>
269 {% for illu in entity.get_images|slice:":4" %}
271 <div class="tape"></div>
272 <img src="{{ illu.url }}">
273 <div class="cap">{{ illu.caption }}</div>
280 <div class="tear"><div class="dots"></div><div class="txt">fragmenty tekstu</div><div class="dots"></div></div>
282 <div class="fragments">
283 {% for ref in entity.reference_set.all|slice:":3" %}
284 <div class="fragment">
286 <img src="{{ ref.book.cover_clean.url }}" alt="Okładka: {{ ref.book.pretty_title }}">
288 <div class="frag-text">
289 <div class="book"><b>
290 {% for author in ref.book.authors %}
291 {{ author.name }}{% if not forloop.last %}, {% endif %}
293 </b> — {{ ref.book.title }}</div>
294 {% for occ in ref.occurence_set.all|slice:":3" %}
295 <div class="quote">{{ occ.html|safe }}</div>
298 {% with occ=ref.occurence_set.all.first %}
299 <div class="qr-mini">
300 <div class="box qr" data-qr="https://wolnelektury.pl/katalog/lektura/{{ ref.book.slug }}.html#sec{{ occ.section }}">
302 <div class="cap">zobacz w treści</div>
311 // Jedyny JS w tym pliku: inicjalizacja mapy, jeśli jest obecna.
312 const mapEl = document.getElementById('map');
314 const lat = parseFloat(mapEl.dataset.lat);
315 const lon = parseFloat(mapEl.dataset.lon);
316 const map = L.map(mapEl, {
317 zoomControl:false, attributionControl:false,
318 dragging:false, scrollWheelZoom:false
319 }).setView([lat, lon], 6);
320 L.tileLayer('https://tile.thunderforest.com/cycle/{z}/{x}/{y}@2x.png?apikey=a8a97f0ae5134403ac38c1a075b03e15').addTo(map);
321 L.marker([lat, lon]).addTo(map);
322 setTimeout(() => map.invalidateSize(), 200);
326 for (let e of document.getElementsByClassName('qr')) {
329 width: 160, height: 160,
330 colorDark: "#000000", colorLight: "#ffffff",
331 correctLevel: QRCode.CorrectLevel.M