What To Cook (WTC) v4.5: Print 256mm Safe-Height fuer Apple/Android, Echtzeit-Rezeptsuche, Kaffee & Kuchen

This commit is contained in:
2026-10-14 20:48:00 +02:00
parent c01d2d7639
commit d4e8a4dc6b
3 changed files with 679 additions and 339 deletions
+409 -88
View File
@@ -16,17 +16,47 @@
}
}
/* Im Druck: Millimetergenaue DIN A4 Seitenhöhe (297mm) auf Apple, Android & Desktop */
/* Im Druck: Millimetergenaue DIN A4 Seitenhöhe (256mm) auf Apple, Android & Desktop */
@media print {
@page {
size: A4 portrait;
margin: 0;
}
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
color-adjust: exact !important;
box-sizing: border-box !important;
}
html, body.cookbook-body {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
height: auto !important;
min-height: auto !important;
margin: 0 !important;
padding: 0 !important;
overflow: visible !important;
background-color: var(--creme) !important;
}
.cb-nav,
.cb-filter-bar,
.no-print,
.desktop-nav-actions,
.mobile-nav-wrapper,
#search-empty-state,
.modal-backdrop,
.cb-toast {
display: none !important;
visibility: hidden !important;
height: 0 !important;
margin: 0 !important;
padding: 0 !important;
}
.book-wrapper {
display: block !important;
width: 100% !important;
@@ -36,76 +66,127 @@
padding: 0 !important;
gap: 0 !important;
}
/* Universelle 1-Seiten-Garantie: 256mm Höhe passt absolut sicher in iOS AirPrint (267mm) & Android Chrome ohne 2. Seite oder Abschneiden */
.page {
width: 100% !important;
max-width: 100% !important;
max-width: 210mm !important;
min-width: 0 !important;
height: 282mm !important;
min-height: 282mm !important;
max-height: 282mm !important;
margin: 0 !important;
padding: 0 !important;
height: 256mm !important;
min-height: 256mm !important;
max-height: 256mm !important;
margin: 0 auto !important;
padding: 4mm 8mm 2mm 8mm !important;
box-sizing: border-box !important;
background-color: var(--creme) !important;
overflow: hidden !important;
page-break-after: always !important;
break-after: page !important;
page-break-inside: avoid !important;
break-inside: avoid !important;
display: flex !important;
flex-direction: column !important;
justify-content: space-between !important;
position: relative !important;
overflow: hidden !important;
}
.page.cover {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
width: 100% !important;
max-width: 100% !important;
max-width: 210mm !important;
min-width: 0 !important;
height: 282mm !important;
min-height: 282mm !important;
max-height: 282mm !important;
padding: 16mm 10mm !important;
height: 256mm !important;
min-height: 256mm !important;
max-height: 256mm !important;
padding: 10mm 10mm !important;
box-sizing: border-box !important;
overflow: hidden !important;
page-break-after: always !important;
break-after: page !important;
page-break-inside: avoid !important;
break-inside: avoid !important;
overflow: hidden !important;
}
.cover-box {
padding: 16mm 14mm !important;
max-width: 85% !important;
padding: 18mm 14mm !important;
max-width: 82% !important;
border: 3.5px solid var(--schwarz) !important;
background-color: var(--creme) !important;
transform: rotate(-1deg) !important;
box-sizing: border-box !important;
margin: auto !important;
}
.cover-box h1 {
font-family: var(--font-hand) !important;
font-size: 3.2rem !important;
line-height: 1.15 !important;
margin: 0 !important;
}
.cover-box p {
font-family: var(--font-hand) !important;
font-size: 1.5rem !important;
color: var(--tannengruen) !important;
margin-top: 10px !important;
margin-bottom: 0 !important;
}
.page.toc-page {
display: flex !important;
flex-direction: column !important;
justify-content: space-between !important;
width: 100% !important;
max-width: 100% !important;
max-width: 210mm !important;
min-width: 0 !important;
height: 282mm !important;
min-height: 282mm !important;
max-height: 282mm !important;
padding: 12mm 10mm !important;
height: 256mm !important;
min-height: 256mm !important;
max-height: 256mm !important;
padding: 8mm 10mm 2mm 10mm !important;
box-sizing: border-box !important;
overflow: hidden !important;
page-break-after: always !important;
break-after: page !important;
page-break-inside: avoid !important;
break-inside: avoid !important;
overflow: hidden !important;
}
.toc-header {
margin-bottom: 4mm !important;
}
.toc-columns {
column-count: 2 !important;
column-gap: 8mm !important;
flex: 1 1 auto !important;
overflow: hidden !important;
margin-bottom: 2mm !important;
}
.toc-chapter-group {
break-inside: avoid !important;
margin-bottom: 3mm !important;
}
.toc-recipe-item {
margin-bottom: 2px !important;
font-size: 0.82rem !important;
line-height: 1.2 !important;
}
.divider {
width: 100% !important;
max-width: 100% !important;
max-width: 210mm !important;
min-width: 0 !important;
height: 282mm !important;
min-height: 282mm !important;
max-height: 282mm !important;
padding: 14mm 10mm !important;
height: 256mm !important;
min-height: 256mm !important;
max-height: 256mm !important;
padding: 10mm 10mm 2mm 10mm !important;
box-sizing: border-box !important;
position: relative !important;
overflow: hidden !important;
page-break-after: always !important;
break-after: page !important;
page-break-inside: avoid !important;
@@ -114,37 +195,63 @@
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
overflow: hidden !important;
}
.divider .chapter-number {
font-family: var(--font-hand) !important;
font-size: 2.8rem !important;
color: var(--altrosa) !important;
margin-bottom: -6px !important;
}
.divider h2 {
font-family: var(--font-hand) !important;
font-size: 3.4rem !important;
color: var(--tannengruen) !important;
margin: 0 0 15px 0 !important;
line-height: 1.15 !important;
}
.doodle-placeholder {
width: 140mm !important;
height: 140mm !important;
max-width: 140mm !important;
max-height: 140mm !important;
margin: 6mm auto 0 auto !important;
width: 120mm !important;
height: 120mm !important;
max-width: 120mm !important;
max-height: 120mm !important;
margin: 4mm auto !important;
border: none !important;
background: transparent !important;
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
.doodle-placeholder img {
width: 100% !important;
height: 100% !important;
max-width: 100% !important;
max-height: 100% !important;
max-width: 120mm !important;
max-height: 120mm !important;
object-fit: contain !important;
}
.divider .page-footer {
position: absolute !important;
bottom: 2mm !important;
left: 0 !important;
right: 0 !important;
padding: 0 10mm !important;
}
.recipe-page {
width: 100% !important;
max-width: 100% !important;
max-width: 210mm !important;
min-width: 0 !important;
height: 282mm !important;
min-height: 282mm !important;
max-height: 282mm !important;
padding: 6mm 10mm !important;
height: 256mm !important;
min-height: 256mm !important;
max-height: 256mm !important;
padding: 4mm 8mm 2mm 8mm !important;
box-sizing: border-box !important;
position: relative !important;
overflow: hidden !important;
page-break-after: always !important;
break-after: page !important;
page-break-inside: avoid !important;
@@ -152,98 +259,222 @@
display: flex !important;
flex-direction: column !important;
justify-content: flex-start !important;
overflow: hidden !important;
}
.recipe-header {
margin-bottom: 2.5mm !important;
}
.recipe-title {
font-size: 1.65rem !important;
line-height: 1.15 !important;
margin: 0 0 1.5mm 0 !important;
}
.recipe-meta-row {
display: flex !important;
flex-wrap: wrap !important;
gap: 2mm 5mm !important;
font-size: 0.82rem !important;
margin-bottom: 1.5mm !important;
}
.recipe-header-top {
display: none !important;
}
.recipe-source-line {
display: none !important;
}
.notes-box-empty {
display: block !important;
padding: 4px 8px !important;
margin-top: 3px !important;
background: #FFFDF0 !important;
border: 1px dashed #D4C165 !important;
}
.notes-blank-lines {
display: flex !important;
flex-direction: column !important;
gap: 8px !important;
margin-top: 3px !important;
}
.notes-blank-line {
border-bottom: 1px dotted rgba(100, 90, 70, 0.4) !important;
height: 10px !important;
width: 100% !important;
}
.print-only {
display: block !important;
}
.no-print {
display: none !important;
}
.recipe-grid {
display: grid !important;
grid-template-columns: 58mm 1fr !important;
gap: 8mm !important;
gap: 6mm !important;
width: 100% !important;
min-width: 0 !important;
flex: 1 1 auto !important;
margin-bottom: 1.5mm !important;
}
.recipe-grid:has(.has-portion-matrix) {
grid-template-columns: 68mm 1fr !important;
gap: 6mm !important;
}
.recipe-photo-box {
height: 32mm !important;
max-height: 32mm !important;
margin-bottom: 2mm !important;
}
.recipe-photo-box img {
width: 100% !important;
height: 32mm !important;
max-height: 32mm !important;
object-fit: cover !important;
border-radius: 6px !important;
}
.recipe-photo-empty {
display: none !important;
}
.ingredients-box {
padding: 4px 6px !important;
}
.ingredients-list {
display: flex !important;
flex-direction: column !important;
gap: 1.5px !important;
}
.ingredients-list li {
font-size: 0.76rem !important;
line-height: 1.2 !important;
}
.ingredients-list li.ing-subheading {
font-size: 0.80rem !important;
font-weight: 700 !important;
margin-top: 2px !important;
padding-bottom: 1px !important;
}
.portion-matrix-table {
font-size: 0.70rem !important;
line-height: 1.18 !important;
}
.portion-matrix-table th {
padding: 1px 2px !important;
font-size: 0.68rem !important;
}
.portion-matrix-table td {
padding: 1px 2px !important;
}
.instructions-steps {
display: flex !important;
flex-direction: column !important;
gap: 2.5px !important;
}
.step-card {
padding: 2.5px 5px !important;
margin-bottom: 2px !important;
font-size: 0.80rem !important;
line-height: 1.22 !important;
display: flex !important;
gap: 6px !important;
align-items: flex-start !important;
}
.step-badge {
width: 16px !important;
height: 16px !important;
font-size: 0.72rem !important;
flex-shrink: 0 !important;
}
.step-text {
flex: 1 1 auto !important;
}
.notes-box {
margin-top: 2mm !important;
padding: 3px 6px !important;
font-size: 0.76rem !important;
line-height: 1.2 !important;
background: #FFFDF0 !important;
border: 1px dashed #D4C165 !important;
border-radius: 6px !important;
}
.notes-box-title {
font-weight: 700 !important;
font-size: 0.78rem !important;
margin-bottom: 2px !important;
}
.notes-box-empty {
display: block !important;
padding: 3px 6px !important;
margin-top: 2mm !important;
background: #FFFDF0 !important;
border: 1px dashed #D4C165 !important;
}
.notes-blank-lines {
display: flex !important;
flex-direction: column !important;
gap: 6px !important;
margin-top: 2px !important;
}
.notes-blank-line {
border-bottom: 1px dotted rgba(100, 90, 70, 0.4) !important;
height: 8px !important;
width: 100% !important;
}
.print-only {
display: block !important;
}
.no-print {
display: none !important;
}
.page-footer {
display: flex !important;
margin-top: auto !important;
position: relative !important;
bottom: auto !important;
border: none !important;
padding: 3mm 0 2mm 0 !important;
padding: 1.5mm 0 1mm 0 !important;
width: 100% !important;
box-sizing: border-box !important;
z-index: 100 !important;
}
.page-footer.page-even {
left: auto !important;
right: auto !important;
justify-content: flex-start !important;
text-align: left !important;
padding-left: 4mm !important;
padding-left: 3mm !important;
padding-right: 0 !important;
}
.page-footer.page-odd {
right: auto !important;
left: auto !important;
justify-content: flex-end !important;
text-align: right !important;
padding-right: 4mm !important;
padding-right: 3mm !important;
padding-left: 0 !important;
}
.page-footer .page-number {
font-size: 0.95rem !important;
font-family: var(--font-hand), 'Caveat', cursive !important;
font-size: 1.15rem !important;
font-weight: 700 !important;
color: var(--tannengruen) !important;
line-height: 1 !important;
}
.page:last-child {
page-break-after: auto !important;
break-after: auto !important;
}
}
</style>
<script>
(function() {
var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) ||
(window.CSS && CSS.supports && CSS.supports('-webkit-touch-callout', 'none'));
if (isIOS) {
document.documentElement.classList.add('is-ios');
if (document.body) document.body.classList.add('is-ios');
document.addEventListener('DOMContentLoaded', function() {
if (document.body) document.body.classList.add('is-ios');
});
}
})();
</script>
</head>
<body class="cookbook-body {% if is_ios %}is-ios{% endif %}">
<body class="cookbook-body">
<!-- Navigation Bar -->
<header class="cb-nav">
@@ -252,6 +483,13 @@
<h1 class="cb-nav-title">Kochbuch</h1>
</a>
<!-- Rezept-Titel Live-Suche -->
<div class="cb-search-box no-print">
<span class="cb-search-icon">🔍</span>
<input type="search" id="cb-recipe-search" class="cb-search-input" placeholder="Rezept suchen..." oninput="filterRecipesByTitle(this.value)" autocomplete="off">
<button type="button" id="cb-search-clear" class="cb-search-clear" onclick="clearRecipeSearch()" style="display: none;" title="Suche leeren">✕</button>
</div>
<!-- Desktop Buttons (auf Bildschirmen > 768px) -->
<div class="cb-nav-actions desktop-nav-actions">
<button type="button" onclick="openMealPlanModal(event)" class="cb-nav-btn" title="Wochenmenü-Plan öffnen">
@@ -314,6 +552,16 @@
<!-- Virtual Book Container -->
<main class="book-wrapper">
<!-- Leerer Suchergebnis-Zustand -->
<div id="search-empty-state" class="no-print" style="display: none;">
<div style="font-size: 2.2rem; margin-bottom: 8px;">🔍</div>
<h3 style="font-family: var(--font-hand); font-size: 1.6rem; color: var(--tannengruen); margin: 0 0 6px 0;">Kein passendes Rezept gefunden</h3>
<p style="color: #666; font-size: 0.95rem; margin: 0 0 14px 0;">Es gibt leider kein Rezept mit dem Titel &bdquo;<span id="search-empty-term" style="font-weight: bold; color: var(--schwarz);"></span>&ldquo;.</p>
<button type="button" onclick="clearRecipeSearch()" class="cb-btn cb-btn-creme" style="background: var(--tannengruen); color: var(--creme); font-size: 0.9rem;">
Alle Rezepte anzeigen
</button>
</div>
<!-- SEITE 1: COVER -->
{% if current_chapter is none or current_chapter == 1 %}
<div class="page cover" id="page-cover">
@@ -415,7 +663,7 @@
<div class="doodle-placeholder {% if chap.illustration_url %}has-illustration{% endif %}">
{% if chap.illustration_url %}
<img src="{{ chap.illustration_url }}" alt="{{ chap.title }} Illustration">
<img src="{{ chap.illustration_url }}" alt="{{ chap.title }} Illustration" loading="lazy" decoding="async">
{% else %}
<span class="placeholder-text">[ {{ chap.doodle_hint }} ]</span>
{% endif %}
@@ -481,7 +729,7 @@
<aside class="recipe-side">
<div class="recipe-photo-box">
{% if recipe.image_url %}
<img src="{{ recipe.image_url }}" alt="{{ recipe.title }}">
<img src="{{ recipe.image_url }}" alt="{{ recipe.title }}" loading="lazy" decoding="async">
{% else %}
<div class="recipe-photo-empty">
<span>📸</span>
@@ -607,7 +855,7 @@
<h2>{{ chap.title }}</h2>
<div class="doodle-placeholder {% if chap.illustration_url %}has-illustration{% endif %}" style="margin: 15px auto;">
{% if chap.illustration_url %}
<img src="{{ chap.illustration_url }}" alt="{{ chap.title }} Illustration">
<img src="{{ chap.illustration_url }}" alt="{{ chap.title }} Illustration" loading="lazy" decoding="async">
{% else %}
<span class="placeholder-text">[ {{ chap.doodle_hint }} ]</span>
{% endif %}
@@ -991,6 +1239,79 @@
}
});
// ==========================================
// REZEPT-TITEL LIVE-SUCHE
// ==========================================
function filterRecipesByTitle(query) {
const q = (query || '').trim().toLowerCase();
const clearBtn = document.getElementById('cb-search-clear');
if (clearBtn) {
clearBtn.style.display = q ? 'block' : 'none';
}
const recipeCards = document.querySelectorAll('.recipe-page');
const dividers = document.querySelectorAll('.page.divider');
const emptyState = document.getElementById('search-empty-state');
if (!q) {
recipeCards.forEach(p => p.classList.remove('search-hidden'));
dividers.forEach(d => d.classList.remove('search-hidden'));
if (emptyState) emptyState.style.display = 'none';
return;
}
let matchedCount = 0;
const chaptersWithMatches = new Set();
recipeCards.forEach(card => {
const titleEl = card.querySelector('.recipe-title');
const titleText = titleEl ? titleEl.textContent.toLowerCase() : '';
const match = titleText.includes(q);
if (match) {
card.classList.remove('search-hidden');
matchedCount++;
let prev = card.previousElementSibling;
while (prev) {
if (prev.classList.contains('divider')) {
chaptersWithMatches.add(prev.id);
break;
}
prev = prev.previousElementSibling;
}
} else {
card.classList.add('search-hidden');
}
});
dividers.forEach(div => {
if (chaptersWithMatches.has(div.id)) {
div.classList.remove('search-hidden');
} else {
div.classList.add('search-hidden');
}
});
if (emptyState) {
if (matchedCount === 0) {
emptyState.style.display = 'block';
const termEl = document.getElementById('search-empty-term');
if (termEl) termEl.textContent = query;
} else {
emptyState.style.display = 'none';
}
}
}
function clearRecipeSearch() {
const searchInput = document.getElementById('cb-recipe-search');
if (searchInput) {
searchInput.value = '';
filterRecipesByTitle('');
searchInput.focus();
}
}
// ==========================================
// DRUCKMODAL
// ==========================================