larpaso-website/src/pages/index.astro
Lara 78ccad9c2d Reduce the readability section to the opening hours
The section showed the same information twice side by side - a dashed
"PDF" card labelled "So sieht es heute oft aus" against an hours table
labelled "So sieht es nachher aus". Reported as confusing; the file side
is dropped, the table stays.

The copy had to follow: it literally said "Dieselben Angaben, zweimal.
Einmal als Datei ... Einmal als Tabelle", which describes a comparison
that no longer exists. Rewritten to stand on its own, and the heading
"Lesbar schlägt vollständig" with it - it only made sense against the
file.

Also dropped the table's "TAG" header row. It had no counterpart on the
right and was left over from the two-column layout.

Removed throughout rather than hidden: the pdf-card/pdf-icon/pdf-name/
pdf-meta and readability-label/hours-head rules, the two-column grid
including its mobile override, and the four now-meaningless fields in
the admin schema.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 14:10:54 +02:00

876 lines
22 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
import Layout from '../layouts/Layout.astro';
import { site, rich } from '../lib/content';
const { hero, warum, preis, highlights, leistungen, mappe, ablauf, about, faq, kontakt } = site;
---
<Layout title="Startseite">
<!-- Hero -->
<section class="section hero">
<div class="container hero-grid">
<div class="hero-copy">
<p class="eyebrow">{hero.eyebrow}</p>
<h1>{hero.titleBefore}<em>{hero.titleEmphasis}</em>{hero.titleAfter}</h1>
<p class="text-lead" set:html={rich(hero.lead)} />
<div class="hero-actions">
<a class="btn btn-primary" href="#kontakt">{hero.ctaLabel}</a>
</div>
</div>
<div class="hero-visual" aria-hidden="true">
{hero.visual.customImage ? (
<img class="hero-visual-image" src={hero.visual.customImage} alt="" loading="lazy" />
) : (
<>
<p class="hero-visual-label">{hero.visual.label}</p>
<div class="search-card">
<span class="search-tag">{hero.visual.todayTag}</span>
<p class="search-url">{hero.visual.todayUrl}</p>
<p class="search-title">{hero.visual.todayTitle}</p>
<p class="search-desc">{hero.visual.todayDesc}</p>
</div>
<p class="hero-visual-divider">{hero.visual.dividerText}</p>
<div class="search-card search-card-accent">
<span class="search-tag search-tag-accent">{hero.visual.possibleTag}</span>
<p class="search-url">{hero.visual.possibleUrl}</p>
<p class="search-title">{hero.visual.possibleTitle}</p>
<p class="search-desc" set:html={rich(hero.visual.possibleDesc)} />
<p class="search-meta">{hero.visual.possibleMeta}</p>
</div>
</>
)}
</div>
</div>
</section>
<!-- Preis / Startangebot -->
<section id="preis" class="section-tight">
<div class="container price-grid">
<div class="price-main">
<p class="eyebrow">{preis.eyebrow}</p>
<h2>{preis.title}</h2>
<p class="price-tag"><span class="price-amount">{preis.amount}</span> {preis.amountNote}</p>
<p class="text-lead" set:html={rich(preis.lead)} />
<div class="price-progress" aria-hidden="true">
<span></span><span></span><span></span>
</div>
<p class="price-note">{preis.slotsNote}</p>
<ul class="price-list">
{preis.features.map((feature) => (
<li class={feature.excluded ? 'price-list-excluded' : undefined}>{feature.text}</li>
))}
</ul>
</div>
<aside class="price-aside card">
<h3>{preis.asideTitle}</h3>
<p set:html={rich(preis.asideParagraph1)} />
<p set:html={rich(preis.asideParagraph2)} />
<a class="btn btn-primary" href="#kontakt">{preis.ctaLabel}</a>
</aside>
</div>
</section>
<!-- Warum uns unterscheidet -->
<section id="warum" class="section">
<div class="container">
<p class="eyebrow">{warum.eyebrow}</p>
<h2>{warum.title}</h2>
<p class="text-lead" set:html={rich(warum.lead)} />
<div class="grid grid-3 differentiators">
{highlights.map((item) => (
<div class="differentiator">
<span class="roman">{item.num}</span>
<h3>{item.title}</h3>
<p>{item.text}</p>
</div>
))}
</div>
<div class="readability">
<h3>{warum.readability.title}</h3>
<p class="text-lead" set:html={rich(warum.readability.lead)} />
{warum.readability.customImage ? (
<img class="readability-image" src={warum.readability.customImage} alt="" loading="lazy" />
) : (
<div class="readability-hours">
<div class="hours-card">
<h4>{warum.readability.hoursTitle}</h4>
{warum.readability.hoursRows.map((row) => (
<div class="hours-row">
<span>{row.day}</span><span>{row.time}</span>
</div>
))}
</div>
</div>
)}
</div>
</div>
</section>
<!-- Leistungen -->
<section id="leistungen" class="section">
<div class="container">
<p class="eyebrow">{leistungen.eyebrow}</p>
<h2>{leistungen.title}</h2>
<p class="text-lead" set:html={rich(leistungen.lead)} />
<ul class="leistungen-list">
{leistungen.items.map((item) => (
<li>
<span class="leistungen-num">{item.num}</span>
<div>
<h3>{item.title}</h3>
<p>{item.text}</p>
</div>
</li>
))}
</ul>
<div class="nicht-machen">
<h3>{leistungen.nichtMachenTitle}</h3>
<div class="grid grid-2">
{leistungen.nichtMachen.map((text) => (
<p class="nicht-machen-item">— {text}</p>
))}
</div>
</div>
</div>
</section>
<!-- Mappe / Arbeiten -->
<section id="arbeiten" class="section">
<div class="container">
<p class="eyebrow">{mappe.eyebrow}</p>
<h2>{mappe.title}</h2>
<div class="mappe-grid">
{mappe.items.map((item) => (
<article class="mappe-item">
{item.image ? (
<div class="mappe-shot mappe-shot-image">
<img src={item.image} alt={item.titel || item.kategorie} loading="lazy" />
</div>
) : (
<div class="mappe-shot">
<span>Bildschirmfoto <span class="placeholder">einsetzen</span></span>
</div>
)}
<p class="mappe-kategorie">{item.kategorie}</p>
<h3>{item.nameHidden ? <span class="placeholder">[Studio — erst nach Freigabe zeigen]</span> : item.titel}</h3>
<p>{item.text}</p>
</article>
))}
</div>
</div>
</section>
<!-- Ablauf -->
<section id="ablauf" class="section">
<div class="container">
<p class="eyebrow">{ablauf.eyebrow}</p>
<h2>{ablauf.title}</h2>
<div class="ablauf-list">
{ablauf.steps.map((step) => (
<div class="ablauf-step">
<span class="ablauf-num">{step.num}</span>
<div class="ablauf-body">
<h3>{step.title}</h3>
<p>{step.text}</p>
</div>
{step.dauer && <span class="ablauf-dauer">{step.dauer}</span>}
</div>
))}
</div>
</div>
</section>
<!-- Über uns -->
<section class="section about">
<div class="container about-grid">
{about.photo ? (
<div class="about-photo about-photo-image">
<img src={about.photo} alt="Lara und Pascal" loading="lazy" />
</div>
) : (
<div class="about-photo">
<span>
Foto von Lara und Pascal <span class="placeholder">einsetzen</span><br />
<small>zusammen, hochkant, echtes Licht</small>
</span>
</div>
)}
<div class="about-copy">
<p class="eyebrow">{about.eyebrow}</p>
<h2>{about.title}</h2>
<p class="about-quote">{about.quote}</p>
{about.paragraphs.map((paragraph) => (
<p set:html={rich(paragraph)} />
))}
</div>
</div>
</section>
<!-- FAQ -->
<section class="section faq">
<div class="container">
<p class="eyebrow">{faq.eyebrow}</p>
<h2>{faq.title}</h2>
<div class="faq-list">
{faq.items.map((item) => (
<details class="faq-item" name="faq">
<summary>
{item.question}
<span class="faq-icon" aria-hidden="true">+</span>
</summary>
<p class={item.answer ? undefined : 'placeholder'}>
{item.answer || '[Antwort noch zu ergänzen]'}
</p>
</details>
))}
</div>
</div>
</section>
<!-- Kontakt -->
<section id="kontakt" class="section-tight contact">
<div class="container contact-grid">
<div class="contact-intro">
<p class="eyebrow">{kontakt.eyebrow}</p>
<h2>{kontakt.title}</h2>
<p class="text-lead" set:html={rich(kontakt.lead)} />
<div class="contact-detail">
<p class="contact-label">Lieber anrufen</p>
<p class={kontakt.phone ? 'contact-phone' : 'placeholder contact-phone'}>
{kontakt.phone || '[Telefonnummer eintragen]'}
</p>
<p class={kontakt.hours ? 'contact-hours' : 'placeholder contact-hours'}>
{kontakt.hours || '[Erreichbarkeit eintragen, z. B. Mo–Fr 9–18 Uhr]'}
</p>
</div>
<div class="contact-detail">
<p class="contact-label">Oder direkt schreiben</p>
<p class="contact-email"><a href={`mailto:${kontakt.email}`}>{kontakt.email}</a></p>
<p class="contact-hint">Antwort in der Regel am selben Tag.</p>
</div>
<div class="contact-detail">
<p class="contact-label">Wie wir arbeiten</p>
<p class={kontakt.howWeWork ? undefined : 'placeholder'}>
{kontakt.howWeWork || '[Wie wir arbeiten — noch zu ergänzen]'}
</p>
</div>
</div>
<form class="contact-form card" aria-label="Kontaktformular" id="contact-form">
<div class="form-field">
<label for="name">Ihr Name</label>
<input type="text" id="name" name="name" placeholder="Vor- und Nachname" required />
</div>
<div class="form-field">
<label for="betrieb">Betrieb</label>
<input type="text" id="betrieb" name="betrieb" placeholder="Wie heißt Ihr Laden?" />
</div>
<div class="form-field">
<label for="telefon">Telefon</label>
<input type="tel" id="telefon" name="telefon" placeholder="Für den Rückruf" />
</div>
<div class="form-field">
<label for="email">E-Mail</label>
<input type="email" id="email" name="email" placeholder="name@betrieb.de" required />
</div>
<div class="form-field">
<label for="message">Worum geht es?</label>
<textarea
id="message"
name="message"
rows="5"
placeholder="Zwei, drei Sätze reichen: Was machen Sie, und was stört Sie an Ihrer jetzigen Seite? Wenn Sie schon eine haben, gern die Adresse dazu."
></textarea>
</div>
<button type="submit" class="btn btn-primary form-submit">Anfrage abschicken</button>
<p class="form-note">
Das Formular öffnet Ihr E-Mail-Programm mit fertig ausgefülltem Text. Es geht nichts an
Dritte. Wie wir mit Ihren Angaben umgehen, steht in der
<a href="/datenschutz">Datenschutzerklärung</a>.
</p>
</form>
</div>
</section>
<script define:vars={{ contactEmail: kontakt.email }}>
const form = document.getElementById('contact-form');
form?.addEventListener('submit', (event) => {
event.preventDefault();
const data = new FormData(form);
const name = String(data.get('name') || '');
const betrieb = String(data.get('betrieb') || '');
const telefon = String(data.get('telefon') || '');
const email = String(data.get('email') || '');
const message = String(data.get('message') || '');
const bodyLines = [
`Name: ${name}`,
betrieb && `Betrieb: ${betrieb}`,
telefon && `Telefon: ${telefon}`,
email && `E-Mail: ${email}`,
'',
message,
].filter(Boolean);
const subject = encodeURIComponent(`Projektanfrage${betrieb ? ` – ${betrieb}` : ''}`);
const body = encodeURIComponent(bodyLines.join('\n'));
window.location.href = `mailto:${contactEmail}?subject=${subject}&body=${body}`;
});
</script>
</Layout>
<style>
/* Hero */
.hero-grid {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: var(--space-8);
align-items: center;
}
.hero-copy {
max-width: 34rem;
}
.hero h1 {
font-size: clamp(2.5rem, 5vw, 3.5rem);
}
.hero h1 em {
color: var(--color-accent);
font-style: italic;
}
.hero-actions {
margin-top: var(--space-6);
}
.hero-visual-label {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin-bottom: var(--space-3);
}
.hero-visual-image {
width: 100%;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
}
.search-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-5);
position: relative;
}
.search-card-accent {
border-color: var(--color-accent);
}
.search-tag {
position: absolute;
top: var(--space-4);
right: var(--space-4);
font-size: 0.7rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-text-muted);
}
.search-tag-accent {
background: var(--color-accent);
color: var(--color-primary-contrast);
padding: 0.15rem 0.5rem;
border-radius: var(--radius-sm);
}
.search-url {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin: 0 0 var(--space-1) 0;
}
.search-title {
color: var(--color-google-blue);
font-size: var(--font-size-md);
margin: 0 0 var(--space-2) 0;
}
.search-desc,
.search-meta {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
margin: 0;
}
.hero-visual-divider {
text-align: center;
font-size: 0.75rem;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--color-text-muted);
margin: var(--space-4) 0;
}
/* Preis */
.price-grid {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: var(--space-8);
align-items: start;
}
.price-tag {
margin: var(--space-4) 0;
}
.price-amount {
font-family: var(--font-serif);
font-size: clamp(2.5rem, 6vw, 4rem);
color: var(--color-accent);
font-weight: 600;
}
.price-progress {
display: flex;
gap: var(--space-2);
margin: var(--space-5) 0 var(--space-3);
}
.price-progress span {
height: 4px;
width: 3rem;
background: var(--color-accent);
border-radius: 2px;
}
.price-note {
color: var(--color-text-muted);
font-size: var(--font-size-sm);
}
.price-list {
list-style: none;
margin-top: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.price-list li {
padding-left: var(--space-5);
position: relative;
font-size: var(--font-size-sm);
color: var(--color-text);
}
.price-list li::before {
content: '';
position: absolute;
left: 0;
top: 0.5em;
width: 8px;
height: 8px;
background: var(--color-accent);
}
.price-list-excluded {
color: var(--color-text-muted);
}
.price-list-excluded::before {
background: transparent;
border: 1px solid var(--color-text-muted);
}
.price-aside {
position: sticky;
top: 6rem;
}
.price-aside p {
font-size: var(--font-size-sm);
}
/* Warum / Differentiators */
.differentiators {
margin-top: var(--space-7);
}
.differentiator {
border-top: 1px solid var(--color-border);
padding-top: var(--space-4);
}
.roman {
display: block;
font-family: var(--font-serif);
font-size: var(--font-size-2xl);
color: var(--color-text-muted);
margin-bottom: var(--space-3);
}
.readability {
margin-top: var(--space-8);
padding-top: var(--space-8);
border-top: 1px solid var(--color-border);
}
.readability-hours {
margin-top: var(--space-6);
max-width: 26rem;
}
.readability-image {
width: 100%;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
margin-top: var(--space-6);
}
.hours-card {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.hours-card h4 {
margin-bottom: var(--space-3);
}
.hours-row {
display: flex;
justify-content: space-between;
padding-block: var(--space-2);
border-bottom: 1px solid var(--color-border);
font-size: var(--font-size-sm);
}
.hours-row:last-child {
border-bottom: none;
}
/* Leistungen */
.leistungen-list {
list-style: none;
margin-top: var(--space-7);
}
.leistungen-list li {
display: flex;
gap: var(--space-6);
padding-block: var(--space-5);
border-bottom: 1px solid var(--color-border);
}
.leistungen-num {
font-family: var(--font-serif);
color: var(--color-accent);
flex-shrink: 0;
width: 2.5rem;
}
.leistungen-list h3 {
margin-bottom: var(--space-2);
}
.leistungen-list p {
margin: 0;
}
.nicht-machen {
margin-top: var(--space-8);
padding-top: var(--space-7);
border-top: 1px solid var(--color-border);
}
.nicht-machen-item {
color: var(--color-text);
}
/* Mappe */
.mappe-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-7);
margin-top: var(--space-7);
}
.mappe-item:first-child {
grid-column: 1 / -1;
}
.mappe-shot {
aspect-ratio: 16 / 9;
background: var(--color-surface);
border: 1px dashed var(--color-border);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
color: var(--color-text-muted);
font-size: var(--font-size-sm);
margin-bottom: var(--space-4);
overflow: hidden;
}
.mappe-shot-image {
border: none;
padding: 0;
}
.mappe-shot-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.mappe-kategorie {
font-size: 0.75rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-text-muted);
margin-bottom: var(--space-2);
}
/* Ablauf */
.ablauf-list {
margin-top: var(--space-7);
}
.ablauf-step {
display: grid;
grid-template-columns: 3rem 1fr auto;
gap: var(--space-5);
align-items: baseline;
padding-block: var(--space-6);
border-bottom: 1px solid var(--color-border);
}
.ablauf-num {
font-family: var(--font-serif);
font-size: var(--font-size-2xl);
color: var(--color-accent);
}
.ablauf-body h3 {
margin-bottom: var(--space-2);
}
.ablauf-body p {
margin: 0;
}
.ablauf-dauer {
font-size: var(--font-size-sm);
color: var(--color-text-muted);
white-space: nowrap;
}
/* About */
.about-grid {
display: grid;
grid-template-columns: 0.8fr 1.2fr;
gap: var(--space-8);
align-items: start;
}
.about-photo {
aspect-ratio: 3 / 4;
background: var(--color-surface);
border: 1px dashed var(--color-border);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
text-align: center;
padding: var(--space-5);
color: var(--color-text-muted);
font-size: var(--font-size-sm);
overflow: hidden;
}
.about-photo-image {
border: none;
padding: 0;
}
.about-photo-image img {
width: 100%;
height: 100%;
object-fit: cover;
}
.about-quote {
font-family: var(--font-serif);
font-style: italic;
color: var(--color-accent);
font-size: var(--font-size-lg);
border-left: 2px solid var(--color-accent);
padding-left: var(--space-4);
margin-bottom: var(--space-5);
}
/* FAQ */
.faq-list {
margin-top: var(--space-7);
border-top: 1px solid var(--color-border);
}
.faq-item {
border-bottom: 1px solid var(--color-border);
padding-block: var(--space-5);
}
.faq-item summary {
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
list-style: none;
font-weight: var(--font-weight-medium);
color: var(--color-text);
}
.faq-item summary::-webkit-details-marker {
display: none;
}
.faq-icon {
color: var(--color-accent);
font-size: var(--font-size-lg);
}
.faq-item[open] .faq-icon {
transform: rotate(45deg);
}
.faq-item p {
margin-top: var(--space-4);
}
/* Kontakt */
.contact-grid {
display: grid;
grid-template-columns: 1fr 1.2fr;
gap: var(--space-8);
align-items: start;
}
.contact-detail {
margin-top: var(--space-6);
}
.contact-label {
font-size: 0.75rem;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-text-muted);
margin-bottom: var(--space-2);
}
.contact-phone {
font-family: var(--font-serif);
font-size: var(--font-size-lg);
}
.contact-email {
font-family: var(--font-serif);
font-size: var(--font-size-lg);
margin-bottom: var(--space-1);
}
.contact-hint {
font-size: var(--font-size-sm);
margin: 0;
}
.contact-form {
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.form-field input,
.form-field textarea {
width: 100%;
padding: var(--space-3) var(--space-4);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: var(--color-bg);
color: var(--color-text);
}
.form-field input:focus,
.form-field textarea:focus {
outline: 2px solid var(--color-primary);
outline-offset: 1px;
}
.form-field textarea {
resize: vertical;
}
.form-submit {
align-self: flex-start;
}
.form-note {
font-size: var(--font-size-sm);
margin: 0;
}
@media (max-width: 900px) {
.hero-grid,
.price-grid,
.about-grid,
.contact-grid {
grid-template-columns: 1fr;
}
.price-aside {
position: static;
}
.mappe-grid {
grid-template-columns: 1fr;
}
.mappe-item:first-child {
grid-column: auto;
}
.ablauf-step {
grid-template-columns: 2.5rem 1fr;
}
.ablauf-dauer {
grid-column: 2;
}
}
</style>