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;
110 /* gdy nie ma mapy: zdjęcia zajmują całą szerokość */
111 .page.no-map .visuals{
112 grid-template-columns:1fr;
114 .page.no-map .illustrations{
119 .illustrations .label{
120 font-family:'Courier Prime', monospace;
122 letter-spacing:.12em;
123 text-transform:uppercase;
131 border:1px solid #000;
135 font-family:'Courier Prime', monospace;
140 /* zdjęcia "przyklejone", dowolne proporcje — CSS columns robi
141 naturalny układ pod różne wysokości (portrait/landscape mix) */
151 padding:2mm 2mm 5mm 2mm;
152 box-shadow:0 2px 6px rgba(0,0,0,.22);
153 border:1px solid #000;
155 .illu:nth-child(odd){ transform:rotate(-2deg); }
156 .illu:nth-child(even){ transform:rotate(1.6deg); }
158 position:absolute; top:-2mm; left:50%; transform:translateX(-50%) rotate(-2deg);
159 width:14mm; height:5mm;
160 background:rgba(0,0,0,.35);
161 border:1px solid rgba(0,0,0,.5);
164 width:100%; height:auto; display:block; /* naturalne proporcje, bez cropu */
168 font-family:'Courier Prime', monospace;
174 /* ---------- PRZERWA ---------- */
176 display:flex; align-items:center; gap:2mm;
179 .tear .dots{ flex:1; border-top:2px dashed #000; }
181 font-family:'Courier Prime', monospace;
184 text-transform:uppercase;
188 /* ---------- FRAGMENTY (bilety) ---------- */
191 grid-template-columns:16mm 1fr 22mm;
195 border-bottom:1px dotted #000;
198 .fragment:last-child{ border-bottom:none; }
201 width:16mm; height:22mm;
202 border:1px solid #000;
203 box-shadow:0 2px 5px rgba(0,0,0,.28);
206 .cover img{ width:100%; height:100%; object-fit:cover; display:block; }
209 font-family:'Courier Prime', monospace;
214 .frag-text .book b{ color:#000; font-weight:700; }
222 font-family:'Courier Prime', monospace;
227 .qr-mini{ width:22mm; text-align:center; }
229 width:20mm; height:20mm;
232 .qr-mini .box img{ width:100%; height:100%; display:block; }
235 font-family:'Courier Prime', monospace;
237 letter-spacing:.06em;
238 text-transform:uppercase;
246 <div class="page {% if not entity.lat %}no-map{% endif %}" id="page">
248 <div class="title-block">
249 <div class="kicker">Mapa Wolnych Lektur</div>
250 <h1>{{ entity.label }}</h1>
252 {% if entity.wikipedia_link %}
254 <div class="seal-ring qr" data-qr="{{ entity.wikipedia_link }}">
256 <div class="cap">Wikipedia →</div>
261 <div class="visuals">
264 <div class="map-frame">
265 <div class="label">Lokalizacja</div>
267 <div id="map" data-lat="{{ entity.lat }}" data-lon="{{ entity.lon }}"></div>
269 <div class="coords">{{ coords_label }}</div>
273 {% if entity.get_images %}
274 <div class="illustrations">
275 <div class="label">Ilustracje</div>
276 {% for illu in entity.get_images|slice:":4" %}
278 <div class="tape"></div>
279 <img src="{{ illu.url }}">
280 <div class="cap">{{ illu.caption }}</div>
287 <div class="tear"><div class="dots"></div><div class="txt">fragmenty tekstu</div><div class="dots"></div></div>
289 <div class="fragments">
290 {% for ref in entity.reference_set.all|slice:":3" %}
291 <div class="fragment">
293 <img src="{{ ref.book.cover_clean.url }}" alt="Okładka: {{ ref.book.pretty_title }}">
295 <div class="frag-text">
296 <div class="book"><b>
297 {% for author in ref.book.authors %}
298 {{ author.name }}{% if not forloop.last %}, {% endif %}
300 </b> — {{ ref.book.title }}</div>
301 {% for occ in ref.occurence_set.all|slice:":3" %}
302 <div class="quote">{{ occ.html|safe }}</div>
305 {% with occ=ref.occurence_set.all.first %}
306 <div class="qr-mini">
307 <div class="box qr" data-qr="https://wolnelektury.pl/katalog/lektura/{{ ref.book.slug }}.html#sec{{ occ.section }}">
309 <div class="cap">zobacz w treści</div>
318 // Jedyny JS w tym pliku: inicjalizacja mapy, jeśli jest obecna.
319 const mapEl = document.getElementById('map');
321 const lat = parseFloat(mapEl.dataset.lat);
322 const lon = parseFloat(mapEl.dataset.lon);
323 const map = L.map(mapEl, {
324 attributionControl:false,
325 }).setView([lat, lon], 11);
326 L.tileLayer('https://tile.thunderforest.com/atlas/{z}/{x}/{y}@2x.png?apikey=a8a97f0ae5134403ac38c1a075b03e15').addTo(map);
327 L.marker([lat, lon]).addTo(map);
328 setTimeout(() => map.invalidateSize(), 200);
332 for (let e of document.getElementsByClassName('qr')) {
335 width: 160, height: 160,
336 colorDark: "#000000", colorLight: "#ffffff",
337 correctLevel: QRCode.CorrectLevel.M