1037b84eaa
scripts/cover.js computes the exact cover canvas dimensions from the page count in book-meta.json (written by build.js), using the KDP Premium Color spine formula (0.002347 in/page), and renders a Nunjucks template to a single PDF containing back cover, spine, and front cover with bleed (0.125 in) and safe-zone overlay guides. - `pnpm cover` — generate output/cover.pdf - `pnpm all` — build interior + both PDFs in one command - Cover artwork slots: images/cover/front.png, images/cover/back.png Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
438 lines
15 KiB
HTML
438 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Cover – Das Kaleidoskop der Schlummerwelten</title>
|
||
<style>
|
||
:root {
|
||
--bleed: {{ bleed }}in;
|
||
--trim-w: {{ trimW }}in;
|
||
--trim-h: {{ trimH }}in;
|
||
--spine-w: {{ spineWidth }}in;
|
||
--total-w: {{ totalWidth }}in;
|
||
--total-h: {{ totalHeight }}in;
|
||
--safe: {{ safe }}in;
|
||
--spine-safe: {{ spineSafe }}in;
|
||
|
||
/* Derived positions (left edge of each zone) */
|
||
--back-left: 0in;
|
||
--spine-left: calc(var(--bleed) + var(--trim-w));
|
||
--front-left: calc(var(--bleed) + var(--trim-w) + var(--spine-w));
|
||
|
||
/* Design tokens */
|
||
--color-bg: #080820;
|
||
--color-mid: #111138;
|
||
--color-gold: #c8a84b;
|
||
--color-gold2: #f0d080;
|
||
--color-text: #e8e4d8;
|
||
--color-muted: rgba(200,168,75,0.45);
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
html, body {
|
||
width: var(--total-w);
|
||
height: var(--total-h);
|
||
background: var(--color-bg);
|
||
-webkit-print-color-adjust: exact;
|
||
print-color-adjust: exact;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* ── Canvas ─────────────────────────────────────────────── */
|
||
.canvas {
|
||
position: relative;
|
||
width: var(--total-w);
|
||
height: var(--total-h);
|
||
background: var(--color-bg);
|
||
}
|
||
|
||
/* Star-field background (pure CSS, no images required) */
|
||
.canvas::before {
|
||
content: '';
|
||
position: absolute;
|
||
inset: 0;
|
||
background-image:
|
||
radial-gradient(1px 1px at 12% 18%, rgba(255,255,255,0.7) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 28% 42%, rgba(255,255,255,0.5) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 44% 11%, rgba(255,255,255,0.6) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 60% 73%, rgba(255,255,255,0.4) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 76% 29%, rgba(255,255,255,0.7) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 88% 55%, rgba(255,255,255,0.5) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 7% 66%, rgba(255,255,255,0.4) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 51% 88%, rgba(255,255,255,0.6) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 33% 77%, rgba(255,255,255,0.3) 0%, transparent 100%),
|
||
radial-gradient(1px 1px at 93% 14%, rgba(255,255,255,0.5) 0%, transparent 100%),
|
||
radial-gradient(2px 2px at 20% 5%, rgba(200,168,75,0.5) 0%, transparent 100%),
|
||
radial-gradient(2px 2px at 68% 90%, rgba(200,168,75,0.4) 0%, transparent 100%),
|
||
radial-gradient(2px 2px at 85% 38%, rgba(200,168,75,0.5) 0%, transparent 100%);
|
||
}
|
||
|
||
/* ── Zones ──────────────────────────────────────────────── */
|
||
.zone {
|
||
position: absolute;
|
||
top: 0;
|
||
height: var(--total-h);
|
||
}
|
||
|
||
.zone--back {
|
||
left: var(--back-left);
|
||
width: calc(var(--bleed) + var(--trim-w));
|
||
}
|
||
|
||
.zone--spine {
|
||
left: var(--spine-left);
|
||
width: var(--spine-w);
|
||
background: var(--color-mid);
|
||
border-left: 0.5px solid rgba(200,168,75,0.2);
|
||
border-right: 0.5px solid rgba(200,168,75,0.2);
|
||
}
|
||
|
||
.zone--front {
|
||
left: var(--front-left);
|
||
width: calc(var(--trim-w) + var(--bleed));
|
||
}
|
||
|
||
/* ── Back cover content ─────────────────────────────────── */
|
||
.back-content {
|
||
position: absolute;
|
||
/* safe zone: bleed + safe from left, safe from right, bleed+safe from top/bottom */
|
||
top: calc(var(--bleed) + var(--safe));
|
||
left: calc(var(--bleed) + var(--safe));
|
||
right: var(--safe);
|
||
bottom: calc(var(--bleed) + var(--safe));
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.back-headline {
|
||
font-family: Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 0.22in;
|
||
color: var(--color-gold);
|
||
letter-spacing: 0.02em;
|
||
margin-bottom: 0.12in;
|
||
}
|
||
|
||
.back-synopsis {
|
||
font-family: Georgia, serif;
|
||
font-size: 0.145in;
|
||
line-height: 1.7;
|
||
color: var(--color-text);
|
||
flex: 1;
|
||
}
|
||
|
||
.back-synopsis p { margin-bottom: 0.1in; }
|
||
|
||
.back-bottom {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.back-tagline {
|
||
font-family: Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 0.115in;
|
||
color: var(--color-muted);
|
||
max-width: 60%;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* Barcode placeholder — KDP places the barcode here if left blank */
|
||
.barcode-placeholder {
|
||
width: 1in;
|
||
height: 0.6in;
|
||
background: white;
|
||
border: 1px solid rgba(200,168,75,0.3);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.barcode-placeholder span {
|
||
font-size: 0.07in;
|
||
color: #aaa;
|
||
text-align: center;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
/* Optional back cover artwork */
|
||
.back-artwork {
|
||
position: absolute;
|
||
inset: 0;
|
||
object-fit: cover;
|
||
opacity: 0.35;
|
||
}
|
||
|
||
/* ── Spine content ──────────────────────────────────────── */
|
||
.spine-content {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.spine-text {
|
||
transform: rotate(90deg);
|
||
white-space: nowrap;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.15in;
|
||
}
|
||
|
||
.spine-title {
|
||
font-family: Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 0.13in;
|
||
color: var(--color-gold);
|
||
letter-spacing: 0.04em;
|
||
}
|
||
|
||
.spine-divider {
|
||
font-size: 0.1in;
|
||
color: var(--color-muted);
|
||
}
|
||
|
||
.spine-author {
|
||
font-family: Georgia, serif;
|
||
font-size: 0.1in;
|
||
color: var(--color-text);
|
||
letter-spacing: 0.05em;
|
||
}
|
||
|
||
.spine-too-narrow {
|
||
transform: rotate(90deg);
|
||
font-size: 0.08in;
|
||
color: rgba(255,80,80,0.8);
|
||
white-space: nowrap;
|
||
font-family: monospace;
|
||
}
|
||
|
||
/* ── Front cover content ────────────────────────────────── */
|
||
.front-artwork {
|
||
position: absolute;
|
||
/* art fills from left trim edge (skip the right bleed) */
|
||
top: 0; left: 0;
|
||
width: var(--trim-w);
|
||
height: var(--total-h);
|
||
object-fit: cover;
|
||
}
|
||
|
||
.front-content {
|
||
position: absolute;
|
||
top: calc(var(--bleed) + var(--safe));
|
||
left: var(--safe);
|
||
right: calc(var(--safe) + var(--bleed));
|
||
bottom: calc(var(--bleed) + var(--safe));
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
text-align: center;
|
||
gap: 0.15in;
|
||
}
|
||
|
||
.front-ornament-top {
|
||
font-size: 0.18in;
|
||
color: var(--color-gold);
|
||
letter-spacing: 0.3em;
|
||
}
|
||
|
||
.front-title {
|
||
font-family: Georgia, serif;
|
||
font-style: italic;
|
||
font-size: 0.52in;
|
||
line-height: 1.2;
|
||
color: var(--color-gold2);
|
||
text-shadow: 0 0 0.15in rgba(200,168,75,0.4), 0 2px 8px rgba(0,0,0,0.8);
|
||
}
|
||
|
||
.front-subtitle {
|
||
font-family: Georgia, serif;
|
||
font-size: 0.18in;
|
||
letter-spacing: 0.08em;
|
||
color: var(--color-text);
|
||
text-shadow: 0 1px 4px rgba(0,0,0,0.8);
|
||
}
|
||
|
||
/* Placeholder box shown when no front artwork is provided */
|
||
.front-art-placeholder {
|
||
position: absolute;
|
||
top: var(--bleed);
|
||
left: 0;
|
||
width: var(--trim-w);
|
||
height: var(--trim-h);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: radial-gradient(ellipse at center, #1a1a4a 0%, #080820 70%);
|
||
}
|
||
|
||
.front-art-placeholder span {
|
||
font-family: monospace;
|
||
font-size: 0.12in;
|
||
color: rgba(200,168,75,0.4);
|
||
border: 1px dashed rgba(200,168,75,0.2);
|
||
padding: 0.15in 0.3in;
|
||
}
|
||
|
||
.front-author {
|
||
font-family: Georgia, serif;
|
||
font-size: 0.16in;
|
||
letter-spacing: 0.1em;
|
||
color: rgba(200,168,75,0.7);
|
||
text-shadow: 0 1px 4px rgba(0,0,0,0.8);
|
||
}
|
||
|
||
.front-ornament-bottom {
|
||
font-size: 0.14in;
|
||
color: var(--color-muted);
|
||
letter-spacing: 0.4em;
|
||
}
|
||
|
||
/* ── Safe-zone & bleed guides overlay ───────────────────── */
|
||
/*
|
||
* These dashed lines are visual guides only — they mark the trim lines
|
||
* and safe zones. Remove or comment out this section before final upload
|
||
* if you prefer a clean proof. KDP will print nothing outside the trim.
|
||
*/
|
||
.guides {
|
||
position: absolute;
|
||
inset: 0;
|
||
pointer-events: none;
|
||
}
|
||
|
||
/* Outer bleed boundary = trim line */
|
||
.guide--trim-top { position: absolute; top: var(--bleed); left: 0; right: 0; border-top: 0.5px dashed rgba(255,80,80,0.5); }
|
||
.guide--trim-bottom { position: absolute; bottom: var(--bleed); left: 0; right: 0; border-top: 0.5px dashed rgba(255,80,80,0.5); }
|
||
.guide--trim-left { position: absolute; left: var(--bleed); top: 0; bottom: 0; border-left: 0.5px dashed rgba(255,80,80,0.5); }
|
||
.guide--trim-right { position: absolute; right: var(--bleed); top: 0; bottom: 0; border-left: 0.5px dashed rgba(255,80,80,0.5); }
|
||
|
||
/* Safe zone (bleed + safe from edge) */
|
||
.guide--safe-top { position: absolute; top: calc(var(--bleed) + var(--safe)); left: 0; right: 0; border-top: 0.5px dashed rgba(255,220,50,0.4); }
|
||
.guide--safe-bottom { position: absolute; bottom: calc(var(--bleed) + var(--safe)); left: 0; right: 0; border-top: 0.5px dashed rgba(255,220,50,0.4); }
|
||
.guide--safe-left { position: absolute; left: calc(var(--bleed) + var(--safe)); top: 0; bottom: 0; border-left: 0.5px dashed rgba(255,220,50,0.4); }
|
||
.guide--safe-right { position: absolute; right: calc(var(--bleed) + var(--safe)); top: 0; bottom: 0; border-left: 0.5px dashed rgba(255,220,50,0.4); }
|
||
|
||
/* Spine boundaries */
|
||
.guide--spine-left { position: absolute; left: var(--spine-left); top: 0; bottom: 0; border-left: 0.5px dashed rgba(100,200,255,0.5); }
|
||
.guide--spine-right { position: absolute; left: calc(var(--front-left)); top: 0; bottom: 0; border-left: 0.5px dashed rgba(100,200,255,0.5); }
|
||
|
||
/* Guide labels */
|
||
.guide-label {
|
||
position: absolute;
|
||
font-family: monospace;
|
||
font-size: 0.07in;
|
||
background: rgba(0,0,0,0.6);
|
||
padding: 1px 3px;
|
||
border-radius: 2px;
|
||
white-space: nowrap;
|
||
}
|
||
.guide-label--bleed { color: rgba(255,80,80,0.9); top: 2px; left: calc(var(--bleed) + 2px); }
|
||
.guide-label--safe { color: rgba(255,220,50,0.9); top: calc(var(--bleed) + var(--safe) + 2px); left: calc(var(--bleed) + var(--safe) + 2px); }
|
||
.guide-label--spine { color: rgba(100,200,255,0.9); top: 2px; left: calc(var(--spine-left) + 2px); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="canvas">
|
||
|
||
{# ── Back cover ─────────────────────────────────────────── #}
|
||
<div class="zone zone--back">
|
||
|
||
{% if backImage %}
|
||
<img class="back-artwork" src="{{ backImage }}" alt="Back cover artwork">
|
||
{% endif %}
|
||
|
||
<div class="back-content">
|
||
<div>
|
||
<p class="back-headline">Zwölf magische Reisen in die Welt der Träume</p>
|
||
<div class="back-synopsis">
|
||
<p>
|
||
Jede Nacht beginnt eine neue Reise – und dieses Buch hat gleich zwölf davon.
|
||
Ob gläserne Wälder, schlafende Riesen oder ein Teetassen-Boot auf einem
|
||
Fluss aus Sternenlicht: Jede Geschichte führt sanft in eine neue Zauberwelt
|
||
und begleitet dein Kind liebevoll in den Schlaf.
|
||
</p>
|
||
<p>
|
||
Zwölf unabhängige Einschlafgeschichten · Traumhafte Aquarell-Illustrationen<br>
|
||
Für Kinder von 3 bis 8 Jahren · Ideal zum Vorlesen
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="back-bottom">
|
||
<p class="back-tagline">
|
||
„Die schönsten Welten sind nur<br>mit geschlossenen Augen zu erreichen."
|
||
</p>
|
||
{# Leave this box empty — KDP will place the barcode here automatically #}
|
||
<div class="barcode-placeholder">
|
||
<span>Barcode<br>(KDP)</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>{# end back #}
|
||
|
||
{# ── Spine ──────────────────────────────────────────────── #}
|
||
<div class="zone zone--spine">
|
||
<div class="spine-content">
|
||
{% if hasSpineText %}
|
||
<div class="spine-text">
|
||
<span class="spine-title">Das Kaleidoskop der Schlummerwelten</span>
|
||
<span class="spine-divider">✦</span>
|
||
<span class="spine-author">[AUTOR]</span>
|
||
</div>
|
||
{% else %}
|
||
<span class="spine-too-narrow">too narrow ({{ pageCount }}p < 79)</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>{# end spine #}
|
||
|
||
{# ── Front cover ─────────────────────────────────────────── #}
|
||
<div class="zone zone--front">
|
||
|
||
{% if frontImage %}
|
||
<img class="front-artwork" src="{{ frontImage }}" alt="Front cover artwork">
|
||
{% else %}
|
||
<div class="front-art-placeholder">
|
||
<span>Cover-Illustration<br>→ images/cover/front.png</span>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="front-content">
|
||
<p class="front-ornament-top">✦ ✦ ✦</p>
|
||
<h1 class="front-title">Das Kaleidoskop<br>der Schlummerwelten</h1>
|
||
<p class="front-subtitle">Zwölf magische Einschlafgeschichten</p>
|
||
<p class="front-author">[AUTOR]</p>
|
||
<p class="front-ornament-bottom">· · ·</p>
|
||
</div>
|
||
|
||
</div>{# end front #}
|
||
|
||
{# ── Guides overlay (trim lines, safe zones, spine) ──────── #}
|
||
{# Remove this block before final upload to KDP #}
|
||
<div class="guides">
|
||
<div class="guide--trim-top"></div>
|
||
<div class="guide--trim-bottom"></div>
|
||
<div class="guide--trim-left"></div>
|
||
<div class="guide--trim-right"></div>
|
||
<div class="guide--safe-top"></div>
|
||
<div class="guide--safe-bottom"></div>
|
||
<div class="guide--safe-left"></div>
|
||
<div class="guide--safe-right"></div>
|
||
<div class="guide--spine-left"></div>
|
||
<div class="guide--spine-right"></div>
|
||
|
||
<span class="guide-label guide-label--bleed">← Beschnitt (3,2 mm)</span>
|
||
<span class="guide-label guide-label--safe">← Sicherheitsabstand (3,2 mm)</span>
|
||
<span class="guide-label guide-label--spine">← Rücken ({{ spineWidth }} Zoll / {{ pageCount }} S.)</span>
|
||
</div>
|
||
|
||
</div>{# end canvas #}
|
||
</body>
|
||
</html>
|