Initial Fleckenhilfe website
This commit is contained in:
+24
@@ -0,0 +1,24 @@
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"recommendations": ["astro-build.astro-vscode"],
|
||||
"unwantedRecommendations": []
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
# Fleckenhilfe
|
||||
|
||||
Fleckenhilfe ist eine Astro-Website für eine praktische, ad-taugliche SEO-Nische: Flecken auf Kleidung, Sofa, Teppich und anderen Oberflächen entfernen.
|
||||
|
||||
## Warum diese Nische
|
||||
- wiederkehrendes Alltagsproblem
|
||||
- keine einzelne offizielle Quelle macht die Idee sinnlos
|
||||
- viele Long-Tail-Seiten möglich
|
||||
- Inhalte lassen sich urheberrechtlich sauber originär erstellen
|
||||
|
||||
## Kernrouten
|
||||
- `/`
|
||||
- `/tools/flecken-entferner`
|
||||
- `/flecken/[slug]`
|
||||
- `/materialien/[slug]`
|
||||
- `/ratgeber/[slug]`
|
||||
- `/privacy-policy`
|
||||
- `/legal-notice`
|
||||
|
||||
## Lokal starten
|
||||
```bash
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Preview
|
||||
```bash
|
||||
npm run build
|
||||
npm run preview -- --host 0.0.0.0 --port 4488
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://fleckenhilfe.de',
|
||||
});
|
||||
Generated
+4736
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "fleckenhilfe",
|
||||
"type": "module",
|
||||
"version": "0.1.0",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "^6.1.8"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 655 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" fill="none">
|
||||
<rect width="64" height="64" rx="14" fill="#146EF5"/>
|
||||
<path d="M18 46c9-10 18-18 28-28" stroke="white" stroke-width="6" stroke-linecap="round"/>
|
||||
<circle cx="22" cy="42" r="6" fill="#FFAE13"/>
|
||||
<circle cx="42" cy="22" r="6" fill="#ED52CB"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 327 B |
@@ -0,0 +1,4 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://fleckenhilfe.de/sitemap-index.xml
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
interface Props {
|
||||
slot?: string;
|
||||
label?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const { slot, label = 'Anzeige', className = '' } = Astro.props;
|
||||
const adsenseClient = import.meta.env.PUBLIC_ADSENSE_CLIENT;
|
||||
---
|
||||
|
||||
<div class={`ad-shell ${className}`.trim()}>
|
||||
<div class="ad-shell__label">{label}</div>
|
||||
{
|
||||
adsenseClient ? (
|
||||
<ins
|
||||
class="adsbygoogle"
|
||||
style="display:block"
|
||||
data-ad-client={adsenseClient}
|
||||
data-ad-slot={slot}
|
||||
data-ad-format="auto"
|
||||
data-full-width-responsive="true"></ins>
|
||||
) : (
|
||||
<div class="ad-shell__placeholder">Platz für eine spätere Anzeige oder einen gesponserten Hinweis.</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{adsenseClient && <script is:inline>(adsbygoogle = window.adsbygoogle || []).push({});</script>}
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
import type { StainItem } from '../data/content';
|
||||
|
||||
interface Props {
|
||||
stains: StainItem[];
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
const { stains, compact = false } = Astro.props;
|
||||
const visibleStains = compact ? stains.slice(0, 8) : stains;
|
||||
const payload = JSON.stringify(visibleStains);
|
||||
---
|
||||
|
||||
<section class={`finder ${compact ? 'finder--compact' : ''}`} data-stains={payload}>
|
||||
<div class="finder__controls">
|
||||
<label class="field">
|
||||
<span>Fleck oder Problem</span>
|
||||
<input type="search" placeholder="z. B. Rotwein, Kaffee, Öl, Blut" data-search />
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Kategorie</span>
|
||||
<select data-category>
|
||||
<option value="alle">Alle</option>
|
||||
<option value="Getränke">Getränke</option>
|
||||
<option value="Fett & Küche">Fett & Küche</option>
|
||||
<option value="Outdoor & Alltag">Outdoor & Alltag</option>
|
||||
<option value="Kosmetik">Kosmetik</option>
|
||||
<option value="Schwierige Flecken">Schwierige Flecken</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="finder__results" data-results>
|
||||
{visibleStains.map((stain) => (
|
||||
<article class="result-card">
|
||||
<div class="result-card__top">
|
||||
<span class="result-icon">{stain.icon}</span>
|
||||
<span class="result-category">{stain.category}</span>
|
||||
</div>
|
||||
<h3>{stain.name}</h3>
|
||||
<p>{stain.short}</p>
|
||||
<a class="button-link" href={`/flecken/${stain.slug}`}>Zur Anleitung</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p class="finder__empty" data-empty hidden>Keine Treffer. Versuch es mit Kaffee, Rotwein, Deo oder Blut.</p>
|
||||
|
||||
{compact && (
|
||||
<div class="finder__footer">
|
||||
<a class="cta-outline" href="/tools/flecken-entferner">Alle Flecken öffnen</a>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<script is:inline>
|
||||
const root = document.currentScript?.previousElementSibling;
|
||||
if (root && root.matches('.finder')) {
|
||||
const stains = JSON.parse(root.getAttribute('data-stains') || '[]');
|
||||
const search = root.querySelector('[data-search]');
|
||||
const category = root.querySelector('[data-category]');
|
||||
const results = root.querySelector('[data-results]');
|
||||
const empty = root.querySelector('[data-empty]');
|
||||
|
||||
const render = () => {
|
||||
const query = (search?.value || '').trim().toLowerCase();
|
||||
const categoryValue = category?.value || 'alle';
|
||||
const filtered = stains.filter((stain) => {
|
||||
const haystack = [stain.name, stain.short, stain.category, stain.summary, ...stain.bestFor, ...stain.firstAid]
|
||||
.join(' ')
|
||||
.toLowerCase();
|
||||
const queryMatch = !query || haystack.includes(query);
|
||||
const categoryMatch = categoryValue === 'alle' || stain.category === categoryValue;
|
||||
return queryMatch && categoryMatch;
|
||||
});
|
||||
|
||||
results.innerHTML = filtered.map((stain) => `
|
||||
<article class="result-card">
|
||||
<div class="result-card__top">
|
||||
<span class="result-icon">${stain.icon}</span>
|
||||
<span class="result-category">${stain.category}</span>
|
||||
</div>
|
||||
<h3>${stain.name}</h3>
|
||||
<p>${stain.short}</p>
|
||||
<a class="button-link" href="/flecken/${stain.slug}">Zur Anleitung</a>
|
||||
</article>
|
||||
`).join('');
|
||||
|
||||
empty.hidden = filtered.length > 0;
|
||||
};
|
||||
|
||||
search?.addEventListener('input', render);
|
||||
category?.addEventListener('change', render);
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,341 @@
|
||||
export type StainItem = {
|
||||
slug: string;
|
||||
name: string;
|
||||
icon: string;
|
||||
category: 'Getränke' | 'Fett & Küche' | 'Outdoor & Alltag' | 'Kosmetik' | 'Schwierige Flecken';
|
||||
short: string;
|
||||
summary: string;
|
||||
bestFor: string[];
|
||||
firstAid: string[];
|
||||
method: string[];
|
||||
avoid: string[];
|
||||
faqs: { question: string; answer: string }[];
|
||||
relatedMaterials: string[];
|
||||
};
|
||||
|
||||
export type MaterialGuide = {
|
||||
slug: string;
|
||||
name: string;
|
||||
short: string;
|
||||
sensitivity: string;
|
||||
safeMoves: string[];
|
||||
avoid: string[];
|
||||
goodFor: string[];
|
||||
};
|
||||
|
||||
export type GuideItem = {
|
||||
slug: string;
|
||||
title: string;
|
||||
short: string;
|
||||
intro: string;
|
||||
steps: string[];
|
||||
tips: string[];
|
||||
};
|
||||
|
||||
export const stains: StainItem[] = [
|
||||
{
|
||||
slug: 'rotwein',
|
||||
name: 'Rotweinfleck entfernen',
|
||||
icon: '🍷',
|
||||
category: 'Getränke',
|
||||
short: 'Schnell abtupfen, nicht verreiben, dann materialgerecht vorgehen.',
|
||||
summary: 'Rotwein wirkt dramatisch, wird aber oft erst dann richtig schwierig, wenn er eingerieben oder zu spät behandelt wird.',
|
||||
bestFor: ['Baumwolle', 'Tischdecken', 'Sofa mit robustem Bezug', 'Teppich mit kurzer Faser'],
|
||||
firstAid: ['Überschüssige Flüssigkeit sofort abtupfen.', 'Von außen nach innen arbeiten.', 'Mit kaltem oder lauwarmem Wasser testen, wenn das Material es verträgt.'],
|
||||
method: ['Fleck sanft abtupfen.', 'Milde Reinigungslösung lokal auftragen.', 'Kurz einwirken lassen.', 'Mit sauberem Tuch wieder aufnehmen.', 'Vor dem Trocknen prüfen, ob noch Farbe sichtbar ist.'],
|
||||
avoid: ['Nicht rubbeln.', 'Keine Hitze am frischen Fleck.', 'Nicht sofort in den Trockner oder unter heiße Luft geben.'],
|
||||
faqs: [
|
||||
{ question: 'Hilft Salz immer?', answer: 'Nicht zuverlässig. Es kann kurzfristig Flüssigkeit ziehen, ersetzt aber keine saubere Fleckbehandlung.' },
|
||||
{ question: 'Warum ist Trocknen so riskant?', answer: 'Weil Restpigmente sich mit Hitze oft stärker festsetzen.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'sofa', 'teppich', 'matratze']
|
||||
},
|
||||
{
|
||||
slug: 'kaffee',
|
||||
name: 'Kaffeefleck entfernen',
|
||||
icon: '☕',
|
||||
category: 'Getränke',
|
||||
short: 'Kaffee lässt sich oft gut lösen, solange der Fleck nicht eintrocknet.',
|
||||
summary: 'Bei Kaffee ist Zeit wichtig. Frische Flecken sind deutlich leichter zu behandeln als eingetrocknete Ränder.',
|
||||
bestFor: ['Hemden', 'T-Shirts', 'Sofabezug', 'Teppich'],
|
||||
firstAid: ['Flüssigkeit abtupfen.', 'Mit etwas kaltem Wasser verdünnen.', 'Fleck nicht tiefer in die Faser drücken.'],
|
||||
method: ['Mildes Reinigungsmittel auftragen.', 'Kurz warten.', 'Mit sauberem Tuch aufnehmen.', 'Bei Bedarf wiederholen.', 'Danach normal waschen oder trocknen lassen und prüfen.'],
|
||||
avoid: ['Keine aggressive Seife auf empfindlichen Stoffen.', 'Nicht heiß ausspülen, wenn der Fleck frisch ist.'],
|
||||
faqs: [
|
||||
{ question: 'Ist Milchkaffee schwieriger?', answer: 'Ja, weil Fett und Eiweiß den Fleck etwas komplexer machen können.' },
|
||||
{ question: 'Kann ich Mineralwasser nehmen?', answer: 'Für den ersten Schritt ja, solange du nur tupfst und nicht einreibst.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'wolle', 'sofa', 'teppich']
|
||||
},
|
||||
{
|
||||
slug: 'blut',
|
||||
name: 'Blutfleck entfernen',
|
||||
icon: '🩸',
|
||||
category: 'Schwierige Flecken',
|
||||
short: 'Kalt behandeln, ruhig arbeiten und Hitze konsequent vermeiden.',
|
||||
summary: 'Blut reagiert empfindlich auf Wärme. Deshalb ist kaltes Vorgehen fast immer der sichere Start.',
|
||||
bestFor: ['Kleidung', 'Bettwäsche', 'Matratzenbezug'],
|
||||
firstAid: ['Immer mit kaltem Wasser starten.', 'Frisches Blut vorsichtig herausspülen oder abtupfen.', 'Nicht mit heißem Wasser testen.'],
|
||||
method: ['Kalten Vorlauf nutzen.', 'Mild vorbehandeln.', 'Kurz einwirken lassen.', 'Erneut kalt ausspülen.', 'Vor dem Trocknen kontrollieren.'],
|
||||
avoid: ['Keine Hitze.', 'Nicht föhnen.', 'Nicht sofort bügeln oder in den Trockner geben.'],
|
||||
faqs: [
|
||||
{ question: 'Warum kein warmes Wasser?', answer: 'Weil Eiweißbestandteile sich dadurch stärker festsetzen können.' },
|
||||
{ question: 'Ist eingetrocknetes Blut aussichtslos?', answer: 'Nein, aber es braucht meist mehr Geduld und mehrere sanfte Durchgänge.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'matratze', 'wolle']
|
||||
},
|
||||
{
|
||||
slug: 'oel-fett',
|
||||
name: 'Öl- und Fettfleck entfernen',
|
||||
icon: '🫒',
|
||||
category: 'Fett & Küche',
|
||||
short: 'Öl sofort abnehmen und nicht durch Wärme fixieren.',
|
||||
summary: 'Fettflecken wirken klein, verteilen sich aber leicht tief in Stoff oder Polster, wenn zu stark gerieben wird.',
|
||||
bestFor: ['Baumwolle', 'Arbeitsplattenbezüge', 'Sofa', 'Tischtextilien'],
|
||||
firstAid: ['Überschuss vorsichtig abnehmen.', 'Nicht verreiben.', 'Material prüfen, bevor du Reiniger nutzt.'],
|
||||
method: ['Lokale Vorbehandlung.', 'Sanft abtupfen.', 'Bei Textilien anschließend passend waschen.', 'Bei Polstern mit wenig Feuchtigkeit arbeiten.'],
|
||||
avoid: ['Keine Hitze vor dem Prüfen.', 'Nicht mit viel Wasser in Polster drücken.'],
|
||||
faqs: [
|
||||
{ question: 'Warum sieht der Fleck nach dem Trocknen manchmal größer aus?', answer: 'Weil sich Fett beim falschen Reinigen weiter ausbreiten kann.' },
|
||||
{ question: 'Hilft Spülmittel immer?', answer: 'Oft ja, aber nicht auf jedem Material und nicht in beliebiger Menge.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'sofa', 'teppich']
|
||||
},
|
||||
{
|
||||
slug: 'grass',
|
||||
name: 'Grasfleck entfernen',
|
||||
icon: '🌿',
|
||||
category: 'Outdoor & Alltag',
|
||||
short: 'Grünpigmente sind hartnäckig, aber frühe Vorbehandlung hilft viel.',
|
||||
summary: 'Grasflecken bestehen nicht nur aus Schmutz, sondern oft auch aus starken Pflanzenfarbstoffen.',
|
||||
bestFor: ['Kinderkleidung', 'Jeans', 'Sporttextilien'],
|
||||
firstAid: ['Erst losen Schmutz trocknen lassen oder vorsichtig entfernen.', 'Dann gezielt vorbehandeln.', 'Nicht sofort wild rubbeln.'],
|
||||
method: ['Fleck lokal anlösen.', 'Ein paar Minuten arbeiten lassen.', 'Sanft ausreiben oder abtupfen.', 'Danach passend waschen.'],
|
||||
avoid: ['Keine aggressive Bürste auf feinen Stoffen.', 'Nicht mit heißen Methoden starten.'],
|
||||
faqs: [
|
||||
{ question: 'Warum bleibt oft ein grüner Schatten?', answer: 'Weil Pflanzenpigmente tief in die Faser ziehen können.' },
|
||||
{ question: 'Geht das aus weißer Kleidung besser raus?', answer: 'Nicht automatisch. Weiße Stoffe zeigen Restschatten oft sogar stärker.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'polyester']
|
||||
},
|
||||
{
|
||||
slug: 'schokolade',
|
||||
name: 'Schokoladenfleck entfernen',
|
||||
icon: '🍫',
|
||||
category: 'Fett & Küche',
|
||||
short: 'Erst Reste abnehmen, dann Fett- und Farbbestandteile vorsichtig lösen.',
|
||||
summary: 'Schokolade ist eine Mischung aus Fett, Zucker und Farbe. Deshalb hilft ein ruhiger Mehr-Schritt-Ansatz.',
|
||||
bestFor: ['Kleidung', 'Kindertextilien', 'Sofa'],
|
||||
firstAid: ['Feste Reste abheben.', 'Nicht tiefer in die Faser drücken.', 'Fleck erst dann mit Feuchtigkeit behandeln.'],
|
||||
method: ['Reste entfernen.', 'Mild entfetten.', 'Sanft abtupfen.', 'Bei Textilien waschbar nachbehandeln.'],
|
||||
avoid: ['Nicht mit heißem Wasser starten.', 'Nichts verschmieren, solange Reste noch aufliegen.'],
|
||||
faqs: [
|
||||
{ question: 'Warum ist Schokolade nicht nur ein normaler Fleck?', answer: 'Weil Fett und Farbstoff zusammen oft mehr als einen Reinigungsschritt brauchen.' },
|
||||
{ question: 'Kann ich sofort in die Wäsche geben?', answer: 'Besser vorher lokal behandeln, damit der Fleck nicht nur mitwandert.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'sofa', 'teppich']
|
||||
},
|
||||
{
|
||||
slug: 'tomatensauce',
|
||||
name: 'Tomatensaucenfleck entfernen',
|
||||
icon: '🍅',
|
||||
category: 'Fett & Küche',
|
||||
short: 'Tomate färbt stark, besonders wenn Öl und Gewürze dabei sind.',
|
||||
summary: 'Tomatensauce wirkt harmlos, ist aber wegen Farbe, Öl und Gewürzen oft hartnäckiger als gedacht.',
|
||||
bestFor: ['Baumwolle', 'Tischdecke', 'Küchentextilien'],
|
||||
firstAid: ['Feste Reste abheben.', 'Flüssigkeit abtupfen.', 'Nicht einreiben.'],
|
||||
method: ['Fleck kühlen oder mit kaltem Wasser anlösen.', 'Mild vorbehandeln.', 'Kurz einwirken lassen.', 'Dann materialgerecht reinigen.'],
|
||||
avoid: ['Keine direkte Hitze.', 'Keine übereilte Trocknung.'],
|
||||
faqs: [
|
||||
{ question: 'Warum bleiben orange Schatten?', answer: 'Weil Farbstoffe und Fett zusammen tief in die Faser ziehen können.' },
|
||||
{ question: 'Ist das bei Kunstfaser anders?', answer: 'Ja, je nach Faser kann der Farbstoff unterschiedlich haften.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'polyester']
|
||||
},
|
||||
{
|
||||
slug: 'make-up',
|
||||
name: 'Make-up-Fleck entfernen',
|
||||
icon: '💄',
|
||||
category: 'Kosmetik',
|
||||
short: 'Kosmetikflecken brauchen oft eine Mischung aus Fett- und Farblösung.',
|
||||
summary: 'Foundation, Concealer oder Lippenprodukt sitzen gern an Kragen, Ärmeln oder Kissen und verhalten sich je nach Produkt anders.',
|
||||
bestFor: ['Kragen', 'Blusen', 'Kissenbezüge', 'helle Kleidung'],
|
||||
firstAid: ['Überschuss aufnehmen.', 'Nicht verstreichen.', 'Empfindliche Stoffe zuerst an unauffälliger Stelle testen.'],
|
||||
method: ['Fleck lokal lösen.', 'Sanft von außen nach innen arbeiten.', 'Danach normal nachreinigen.'],
|
||||
avoid: ['Keine starken Lösemittel ohne Test.', 'Nicht auf Seide oder Wolle experimentieren.'],
|
||||
faqs: [
|
||||
{ question: 'Warum sind helle Kragen so schwierig?', answer: 'Weil dort oft mehrere Produktreste gleichzeitig sitzen.' },
|
||||
{ question: 'Hilft Abschminkprodukt?', answer: 'Nicht pauschal. Manche Produkte hinterlassen selbst Rückstände.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'wolle', 'matratze']
|
||||
},
|
||||
{
|
||||
slug: 'deo',
|
||||
name: 'Deofleck entfernen',
|
||||
icon: '🧴',
|
||||
category: 'Kosmetik',
|
||||
short: 'Deospuren bauen sich oft langsam auf und brauchen eher Geduld als Aggression.',
|
||||
summary: 'Deoflecken sind häufig keine einmaligen Flecken, sondern Rückstände, die sich mit der Zeit sichtbar aufbauen.',
|
||||
bestFor: ['T-Shirts', 'Hemden', 'Sportkleidung'],
|
||||
firstAid: ['Bereich anfeuchten.', 'Nicht zu stark reiben.', 'Farbe des Stoffes mitdenken.'],
|
||||
method: ['Milde lokale Vorbehandlung.', 'Kurz einwirken lassen.', 'Normal waschen und vor dem Trocknen prüfen.'],
|
||||
avoid: ['Keine aggressive Behandlung auf dunklen Stoffen.', 'Keine Hitze vor der Kontrolle.'],
|
||||
faqs: [
|
||||
{ question: 'Warum kommen Deoflecken immer wieder?', answer: 'Weil Produktreste, Schweiß und Waschverhalten zusammenspielen.' },
|
||||
{ question: 'Hilft mehr Waschmittel?', answer: 'Nicht unbedingt. Oft ist gezielte Vorbehandlung wichtiger.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'polyester']
|
||||
},
|
||||
{
|
||||
slug: 'tinte',
|
||||
name: 'Tintenfleck entfernen',
|
||||
icon: '🖊️',
|
||||
category: 'Schwierige Flecken',
|
||||
short: 'Tinte schnell begrenzen, sonst wandert sie leicht weiter.',
|
||||
summary: 'Tinte verbreitet sich gern und reagiert je nach Stift oder Untergrund unterschiedlich.',
|
||||
bestFor: ['Hemden', 'Schulkleidung', 'Arbeitsflächenbezüge'],
|
||||
firstAid: ['Nicht verreiben.', 'Überschüssige Farbe aufnehmen.', 'Behandlung an unauffälliger Stelle testen.'],
|
||||
method: ['Fleck lokalisieren.', 'Mit passender milder Lösung arbeiten.', 'Aufnehmen statt drücken.', 'Bei Bedarf wiederholen.'],
|
||||
avoid: ['Nicht unter fließendem Wasser verschmieren.', 'Nicht mit zu vielen Mitteln gleichzeitig arbeiten.'],
|
||||
faqs: [
|
||||
{ question: 'Warum wird der Fleck manchmal größer?', answer: 'Weil flüssige Farbstoffe beim falschen Tupfen weiterziehen.' },
|
||||
{ question: 'Ist Kugelschreiber anders als Füller?', answer: 'Ja, Zusammensetzung und Verhalten können unterschiedlich sein.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'sofa']
|
||||
},
|
||||
{
|
||||
slug: 'kerzenwachs',
|
||||
name: 'Kerzenwachs entfernen',
|
||||
icon: '🕯️',
|
||||
category: 'Schwierige Flecken',
|
||||
short: 'Erst aushärten lassen, dann mechanisch und danach rückstandsarm weiterarbeiten.',
|
||||
summary: 'Wachs ist kein klassischer Nassfleck. Erst die feste Schicht, dann eventuelle Farbreste oder Fettspuren behandeln.',
|
||||
bestFor: ['Tischdecke', 'Kleidung', 'Teppich mit Vorsicht'],
|
||||
firstAid: ['Wachs fest werden lassen.', 'Vorsichtig abheben.', 'Material vor weiterer Behandlung prüfen.'],
|
||||
method: ['Feste Reste lösen.', 'Restspuren lokal behandeln.', 'Danach Oberfläche oder Stoff kontrollieren.'],
|
||||
avoid: ['Nicht frisches Wachs verschmieren.', 'Keine unkontrollierte Hitze auf empfindlichen Materialien.'],
|
||||
faqs: [
|
||||
{ question: 'Warum nicht sofort wischen?', answer: 'Weil du flüssiges Wachs damit meist nur größer verteilst.' },
|
||||
{ question: 'Bleiben oft Farbreste zurück?', answer: 'Ja, besonders bei farbigen Kerzen.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'teppich', 'holz']
|
||||
},
|
||||
{
|
||||
slug: 'rost',
|
||||
name: 'Rostfleck entfernen',
|
||||
icon: '🧲',
|
||||
category: 'Schwierige Flecken',
|
||||
short: 'Rost ist heikel und sollte nicht mit beliebigen Hausmitteln behandelt werden.',
|
||||
summary: 'Rostflecken sehen klein aus, reagieren aber auf falsche Mittel oft mit noch stärkeren Verfärbungen.',
|
||||
bestFor: ['helle Textilien', 'robustere Stoffe'],
|
||||
firstAid: ['Trockenen Fleck erst prüfen.', 'Materialempfindlichkeit beachten.', 'Gezielte Behandlung statt Experimente.'],
|
||||
method: ['Geeignetes Mittel sparsam nutzen.', 'Lokal arbeiten.', 'Danach gründlich prüfen und eventuell nachspülen.'],
|
||||
avoid: ['Nicht mit Chlorprodukten experimentieren.', 'Nicht wild kombinieren.'],
|
||||
faqs: [
|
||||
{ question: 'Warum ist Rost so speziell?', answer: 'Weil es keine normale Alltagsverschmutzung, sondern eine chemische Verfärbung ist.' },
|
||||
{ question: 'Kann ich einfach stark schrubben?', answer: 'Nein, damit beschädigst du eher das Material als den Fleck.' }
|
||||
],
|
||||
relatedMaterials: ['baumwolle', 'leinen']
|
||||
}
|
||||
];
|
||||
|
||||
export const materials: MaterialGuide[] = [
|
||||
{
|
||||
slug: 'baumwolle',
|
||||
name: 'Baumwolle',
|
||||
short: 'Robust und oft gut behandelbar, solange Hitze und aggressive Mittel nicht unnötig früh kommen.',
|
||||
sensitivity: 'niedrig bis mittel',
|
||||
safeMoves: ['Erst tupfen, dann lokal behandeln.', 'Vor dem Trocknen immer prüfen.', 'Bei starken Flecken lieber zwei sanfte Durchgänge.'],
|
||||
avoid: ['Nicht mit Hitze starten.', 'Nicht alles sofort maximal behandeln.'],
|
||||
goodFor: ['Rotwein', 'Kaffee', 'Blut', 'Tomatensauce']
|
||||
},
|
||||
{
|
||||
slug: 'wolle',
|
||||
name: 'Wolle',
|
||||
short: 'Empfindlich gegen Reibung, Temperaturwechsel und zu viel Wasser.',
|
||||
sensitivity: 'hoch',
|
||||
safeMoves: ['So wenig Bewegung wie möglich.', 'Kalt oder lauwarm denken.', 'Nur lokal und sehr ruhig arbeiten.'],
|
||||
avoid: ['Nicht rubbeln.', 'Nicht heiß behandeln.', 'Nicht stark durchfeuchten.'],
|
||||
goodFor: ['Kaffee', 'Make-up', 'Blut']
|
||||
},
|
||||
{
|
||||
slug: 'polyester',
|
||||
name: 'Polyester',
|
||||
short: 'Pflegeleicht, aber fettige oder farbige Flecken können je nach Oberfläche hartnäckig sein.',
|
||||
sensitivity: 'mittel',
|
||||
safeMoves: ['Lokal behandeln.', 'Nicht unnötig heiß werden lassen.', 'Sporttextilien zügig nachbehandeln.'],
|
||||
avoid: ['Keine starke Hitze.', 'Keine ungetesteten Lösemittel.'],
|
||||
goodFor: ['Gras', 'Deo', 'Tomatensauce']
|
||||
},
|
||||
{
|
||||
slug: 'sofa',
|
||||
name: 'Sofabezug',
|
||||
short: 'Polster brauchen wenig Feuchtigkeit und sauberes, kontrolliertes Tupfen.',
|
||||
sensitivity: 'mittel bis hoch',
|
||||
safeMoves: ['Immer wenig Flüssigkeit.', 'Von außen nach innen arbeiten.', 'Schnell trocknen lassen.'],
|
||||
avoid: ['Nichts tief einarbeiten.', 'Nicht zu nass reinigen.'],
|
||||
goodFor: ['Rotwein', 'Kaffee', 'Öl', 'Schokolade']
|
||||
},
|
||||
{
|
||||
slug: 'teppich',
|
||||
name: 'Teppich',
|
||||
short: 'Flecken sitzen oft tiefer als auf glatten Flächen und dürfen nicht großflächig verteilt werden.',
|
||||
sensitivity: 'mittel',
|
||||
safeMoves: ['Tupfen statt reiben.', 'Nur punktuell reinigen.', 'Trocknungsphase ernst nehmen.'],
|
||||
avoid: ['Keine nassen Kreise ziehen.', 'Nicht mit Bürsten eskalieren.'],
|
||||
goodFor: ['Rotwein', 'Kaffee', 'Kerzenwachs']
|
||||
},
|
||||
{
|
||||
slug: 'matratze',
|
||||
name: 'Matratze',
|
||||
short: 'Hier zählt vor allem kontrollierte Feuchtigkeit und gründliches Nachtrocknen.',
|
||||
sensitivity: 'hoch',
|
||||
safeMoves: ['Sehr sparsam arbeiten.', 'Feuchtigkeit aufnehmen.', 'Lüftung und Trocknung einplanen.'],
|
||||
avoid: ['Keine starke Durchnässung.', 'Nicht sofort wieder abdecken.'],
|
||||
goodFor: ['Blut', 'Rotwein', 'Make-up']
|
||||
},
|
||||
{
|
||||
slug: 'holz',
|
||||
name: 'Holzoberfläche',
|
||||
short: 'Holz reagiert schnell auf Feuchtigkeit, Hitze und aggressive Reiniger.',
|
||||
sensitivity: 'mittel bis hoch',
|
||||
safeMoves: ['Sofort abnehmen.', 'Sehr wenig Feuchtigkeit.', 'Finish und Versiegelung mitdenken.'],
|
||||
avoid: ['Nichts stehen lassen.', 'Keine scheuernden Mittel.'],
|
||||
goodFor: ['Kerzenwachs', 'Kaffee']
|
||||
}
|
||||
];
|
||||
|
||||
export const guides: GuideItem[] = [
|
||||
{
|
||||
slug: 'frischer-fleck-erste-hilfe',
|
||||
title: 'Erste Hilfe bei frischen Flecken',
|
||||
short: 'Was fast immer hilft, bevor du irgendetwas verschlimmerst.',
|
||||
intro: 'Der größte Fehler bei Flecken passiert oft in den ersten zwei Minuten: zu viel Reibung, zu viel Wasser oder zu viel Eile.',
|
||||
steps: ['Fleck identifizieren.', 'Überschuss abtupfen oder abheben.', 'Material prüfen.', 'Lokal und ruhig arbeiten.', 'Vor Hitze immer kontrollieren.'],
|
||||
tips: ['Ein sauberer heller Lappen ist oft besser als Küchenrolle mit Farbe.', 'Mehr Mittel ist nicht automatisch besser.', 'Wenn du unsicher bist, erst an einer unauffälligen Stelle testen.']
|
||||
},
|
||||
{
|
||||
slug: 'hitze-vermeiden',
|
||||
title: 'Warum Hitze viele Flecken schlimmer macht',
|
||||
short: 'Trockner, Föhn und Bügeleisen sind oft zu früh im Spiel.',
|
||||
intro: 'Viele Flecken werden erst dann richtig hartnäckig, wenn sie zu früh getrocknet oder erhitzt werden.',
|
||||
steps: ['Fleck nach Behandlung im Licht prüfen.', 'Nur trocknen, wenn kaum noch Rückstand sichtbar ist.', 'Lieber noch einmal sanft nacharbeiten als zu früh Hitze nutzen.'],
|
||||
tips: ['Das gilt besonders für Blut, Rotwein, Tomate und Fettspuren.', 'Auch warme Raumluft kann Restschatten sichtbarer machen.']
|
||||
},
|
||||
{
|
||||
slug: 'polster-richtig-reinigen',
|
||||
title: 'Polster reinigen, ohne Wasserränder zu erzeugen',
|
||||
short: 'Wenig Feuchtigkeit, klare Kanten und sauberes Nachtrocknen.',
|
||||
intro: 'Bei Sofa und Matratze ist nicht nur der Fleck das Problem, sondern oft auch der Rand danach.',
|
||||
steps: ['Nur wenig Lösung verwenden.', 'Immer von außen nach innen arbeiten.', 'Mit trockenem Tuch nachgehen.', 'Gut nachtrocknen lassen.'],
|
||||
tips: ['Zu viel Druck verteilt den Fleck oft tiefer.', 'Große Flächen möglichst gleichmäßig behandeln.']
|
||||
},
|
||||
{
|
||||
slug: 'hausmittel-mit-vorsicht',
|
||||
title: 'Hausmittel bei Flecken: was oft hilft und was riskant ist',
|
||||
short: 'Nicht jedes virale Hausmittel ist eine gute Idee.',
|
||||
intro: 'Viele Hausmittel funktionieren manchmal, aber oft nur auf bestimmten Materialien oder bei bestimmten Flecken.',
|
||||
steps: ['Immer erst Material prüfen.', 'Nicht mehrere Mittel gleichzeitig mischen.', 'Nur lokal testen.', 'Ergebnis beobachten statt blind nachkippen.'],
|
||||
tips: ['Je empfindlicher der Stoff, desto weniger experimentieren.', 'Ein gutes Ergebnis braucht oft eher Ruhe als Härte.']
|
||||
}
|
||||
];
|
||||
@@ -0,0 +1,10 @@
|
||||
export const privacyHighlights = [
|
||||
'Für das Lesen der Inhalte ist kein Konto nötig.',
|
||||
'Die Hinweise dienen der allgemeinen Information und ersetzen nicht die Pflegehinweise von Herstellern.',
|
||||
'Später eingebundene Werbung kann Cookies oder ähnliche Technologien verwenden.',
|
||||
];
|
||||
|
||||
export const copyrightSafetyNote = {
|
||||
title: 'Hinweis zu Inhalten und Grafiken',
|
||||
text: 'Die Inhalte, Tabellen und vereinfachten Icons auf dieser Website sind originär erstellt. Es werden keine offiziellen Fremdgrafiken oder kopierten Herstellertexte als Kernelement der Website verwendet.'
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
export const site = {
|
||||
name: 'Fleckenhilfe',
|
||||
domain: 'https://fleckenhilfe.de',
|
||||
language: 'de',
|
||||
title: 'Fleckenhilfe | Flecken entfernen ohne Material zu ruinieren',
|
||||
description:
|
||||
'Fleckenhilfe zeigt dir einfache erste Schritte gegen Rotwein, Öl, Kaffee, Blut, Make-up und andere Alltagsflecken. Klar erklärt, materialschonend und schnell nutzbar.',
|
||||
tagline: 'Klare Hilfe bei Flecken auf Kleidung, Sofa, Teppich und mehr.',
|
||||
heroTitle: 'Flecken entfernen, ohne gleich alles schlimmer zu machen.',
|
||||
heroText:
|
||||
'Fleckenhilfe erklärt, was du bei typischen Flecken zuerst tun solltest, was du besser lässt und welche Methode zu Material und Oberfläche passt.',
|
||||
};
|
||||
|
||||
export const company = {
|
||||
name: 'Fleckenhilfe',
|
||||
owner: 'Justin Michael Eckenweber',
|
||||
email: 'hey@unverpackt-finder.de',
|
||||
phone: '028160031948',
|
||||
addressLine1: 'Rühlestraße 5',
|
||||
addressLine2: '45147 Essen',
|
||||
addressLine3: 'Deutschland',
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import '../styles/global.css';
|
||||
import { company, site } from '../data/site';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
const { title = site.title, description = site.description } = Astro.props;
|
||||
const canonical = new URL(Astro.url.pathname, site.domain).toString();
|
||||
const adsenseClient = import.meta.env.PUBLIC_ADSENSE_CLIENT;
|
||||
---
|
||||
|
||||
<html lang={site.language}>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<meta name="description" content={description} />
|
||||
<link rel="canonical" href={canonical} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content={site.name} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonical} />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<title>{title}</title>
|
||||
{adsenseClient && (
|
||||
<script async src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${adsenseClient}`} crossorigin="anonymous"></script>
|
||||
)}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="container nav">
|
||||
<a class="brand" href="/">{site.name}</a>
|
||||
<nav class="nav-links" aria-label="Hauptnavigation">
|
||||
<a href="/#finder">Finder</a>
|
||||
<a href="/#flecken">Flecken</a>
|
||||
<a href="/#materialien">Materialien</a>
|
||||
<a href="/#ratgeber">Ratgeber</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<slot />
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-grid">
|
||||
<div>
|
||||
<strong>{site.name}</strong>
|
||||
<p>{site.tagline}</p>
|
||||
</div>
|
||||
<div class="footer-links">
|
||||
<a href="/">Start</a>
|
||||
<a href="/tools/flecken-entferner">Fleck-Finder</a>
|
||||
<a href="/privacy-policy">Datenschutz</a>
|
||||
<a href="/legal-notice">Impressum</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import AdSlot from '../../components/AdSlot.astro';
|
||||
import { stains, materials } from '../../data/content';
|
||||
|
||||
export function getStaticPaths() {
|
||||
return stains.map((stain) => ({ params: { slug: stain.slug }, props: { stain } }));
|
||||
}
|
||||
|
||||
const { stain } = Astro.props;
|
||||
const relatedMaterials = materials.filter((material) => stain.relatedMaterials.includes(material.slug));
|
||||
---
|
||||
|
||||
<BaseLayout title={`${stain.name} | Fleckenhilfe`} description={stain.short}>
|
||||
<main class="section">
|
||||
<div class="container detail-layout">
|
||||
<article class="article-shell">
|
||||
<div class="eyebrow-row">
|
||||
<span class="eyebrow">{stain.category}</span>
|
||||
<span class="chip">schnelle Hilfe</span>
|
||||
</div>
|
||||
<h1>{stain.name}</h1>
|
||||
<p class="lead-copy">{stain.summary}</p>
|
||||
|
||||
<div class="hero-tip-box">
|
||||
<span class="result-icon result-icon--large">{stain.icon}</span>
|
||||
<div>
|
||||
<h2>Ersthilfe in kurz</h2>
|
||||
<p>{stain.short}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Dafür ist diese Anleitung besonders geeignet</h2>
|
||||
<div class="tag-row">
|
||||
{stain.bestFor.map((item) => <span class="chip">{item}</span>)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Was du sofort tun solltest</h2>
|
||||
<ol class="number-list">
|
||||
{stain.firstAid.map((step) => <li>{step}</li>)}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>So gehst du weiter vor</h2>
|
||||
<ol class="number-list">
|
||||
{stain.method.map((step) => <li>{step}</li>)}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content-panel content-panel--warn">
|
||||
<h2>Lieber vermeiden</h2>
|
||||
<ul class="simple-list">
|
||||
{stain.avoid.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Häufige Fragen</h2>
|
||||
<div class="faq-stack">
|
||||
{stain.faqs.map((faq) => (
|
||||
<article class="faq-card">
|
||||
<h3>{faq.question}</h3>
|
||||
<p>{faq.answer}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<aside class="sidebar-shell">
|
||||
<div class="sidebar-card">
|
||||
<h2>Material zuerst prüfen</h2>
|
||||
<ul class="simple-list">
|
||||
{relatedMaterials.map((material) => (
|
||||
<li><a class="button-link" href={`/materialien/${material.slug}`}>{material.name}</a></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<AdSlot className="sidebar-ad" />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,134 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import StainFinder from '../components/StainFinder.astro';
|
||||
import { site } from '../data/site';
|
||||
import { stains, materials, guides } from '../data/content';
|
||||
import { copyrightSafetyNote } from '../data/legal';
|
||||
---
|
||||
|
||||
<BaseLayout>
|
||||
<main>
|
||||
<section class="hero-block">
|
||||
<div class="container hero-grid hero-grid--single-focus">
|
||||
<div>
|
||||
<div class="eyebrow-row">
|
||||
<span class="eyebrow">Haushaltshilfe statt Hersteller-Blabla</span>
|
||||
<span class="chip chip--accent">neu gedacht</span>
|
||||
</div>
|
||||
<h1>{site.heroTitle}</h1>
|
||||
<p class="hero-copy">{site.heroText}</p>
|
||||
<div class="hero-actions">
|
||||
<a class="cta-primary" href="#finder">Fleck suchen</a>
|
||||
<a class="cta-secondary" href="#materialien">Material zuerst prüfen</a>
|
||||
</div>
|
||||
<div class="hero-stats">
|
||||
<div><strong>{stains.length}</strong><span>Fleck-Anleitungen</span></div>
|
||||
<div><strong>{materials.length}</strong><span>Material-Guides</span></div>
|
||||
<div><strong>{guides.length}</strong><span>praktische Ratgeber</span></div>
|
||||
</div>
|
||||
<div class="hero-inline-note">
|
||||
<strong>Sinnvoller als Herstellerseiten:</strong> erst der sichere erste Schritt, dann die passende Methode für Material und Oberfläche.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="finder" class="section section--overlap">
|
||||
<div class="container feature-shell feature-shell--primary">
|
||||
<div class="section-head section-head--tool">
|
||||
<div>
|
||||
<span class="eyebrow">Fleck-Finder</span>
|
||||
<h2>Wähle Fleckart und geh direkt zur passenden Ersthilfe</h2>
|
||||
</div>
|
||||
<p>
|
||||
Für den Alltag gebaut: erst suchen, dann ruhig und materialgerecht handeln.
|
||||
</p>
|
||||
</div>
|
||||
<StainFinder stains={stains} compact={true} />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container note-box">
|
||||
<strong>{copyrightSafetyNote.title}</strong>
|
||||
<p>{copyrightSafetyNote.text}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="materialien" class="section section--tinted">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">Material zuerst mitdenken</span>
|
||||
<h2>Was auf Baumwolle funktioniert, ist auf Wolle oft schon zu viel</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="material-list">
|
||||
{materials.map((material) => (
|
||||
<article class="material-row">
|
||||
<div>
|
||||
<h3>{material.name}</h3>
|
||||
<p>{material.short}</p>
|
||||
</div>
|
||||
<div class="material-meta">
|
||||
<span class="chip">Empfindlichkeit: {material.sensitivity}</span>
|
||||
<a class="button-link" href={`/materialien/${material.slug}`}>Guide öffnen</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="flecken" class="section">
|
||||
<div class="container">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<span class="eyebrow">Beliebte Flecken</span>
|
||||
<h2>Die häufigsten Problemfälle auf einen Blick</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quick-link-grid">
|
||||
{stains.slice(0, 6).map((stain) => (
|
||||
<a class="quick-link-card" href={`/flecken/${stain.slug}`}>
|
||||
<span class="result-icon">{stain.icon}</span>
|
||||
<div>
|
||||
<strong>{stain.name}</strong>
|
||||
<p>{stain.short}</p>
|
||||
</div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="ratgeber" class="section">
|
||||
<div class="container two-column-feature">
|
||||
<div>
|
||||
<span class="eyebrow">Ratgeber</span>
|
||||
<h2>Die Fehler passieren meistens vor dem eigentlichen Reinigen</h2>
|
||||
<p>
|
||||
Darum deckt Fleckenhilfe nicht nur Fleckarten ab, sondern auch die typischen Fehlgriffe: Hitze zu früh,
|
||||
Hausmittel ohne Test oder zu viel Wasser auf Polstern.
|
||||
</p>
|
||||
</div>
|
||||
<div class="guide-stack">
|
||||
{guides.map((guide) => (
|
||||
<article class="guide-card">
|
||||
<h3>{guide.title}</h3>
|
||||
<p>{guide.short}</p>
|
||||
<a class="button-link" href={`/ratgeber/${guide.slug}`}>Weiterlesen</a>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container note-box">
|
||||
<strong>{copyrightSafetyNote.title}</strong>
|
||||
<p>{copyrightSafetyNote.text}</p>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { company } from '../data/site';
|
||||
---
|
||||
|
||||
<BaseLayout title="Impressum | Fleckenhilfe" description="Impressum von Fleckenhilfe.">
|
||||
<main class="section legal-page">
|
||||
<div class="container legal-shell">
|
||||
<span class="eyebrow">Rechtliches</span>
|
||||
<h1>Impressum</h1>
|
||||
<p>Angaben gemäß § 5 TMG</p>
|
||||
<p>
|
||||
{company.owner}<br />
|
||||
{company.addressLine1}<br />
|
||||
{company.addressLine2}<br />
|
||||
{company.addressLine3}
|
||||
</p>
|
||||
<p>
|
||||
Kontakt<br />
|
||||
E-Mail: {company.email}<br />
|
||||
Telefon: {company.phone}
|
||||
</p>
|
||||
<p>Inhaltlich verantwortlich: {company.owner}</p>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import AdSlot from '../../components/AdSlot.astro';
|
||||
import { materials, stains } from '../../data/content';
|
||||
|
||||
export function getStaticPaths() {
|
||||
return materials.map((material) => ({ params: { slug: material.slug }, props: { material } }));
|
||||
}
|
||||
|
||||
const { material } = Astro.props;
|
||||
const fittingStains = stains.filter((stain) => material.goodFor.some((name) => stain.name.toLowerCase().includes(name.toLowerCase()) || stain.slug.includes(name.toLowerCase()))).slice(0, 5);
|
||||
---
|
||||
|
||||
<BaseLayout title={`${material.name} bei Flecken | Fleckenhilfe`} description={material.short}>
|
||||
<main class="section">
|
||||
<div class="container detail-layout">
|
||||
<article class="article-shell">
|
||||
<span class="eyebrow">Material-Guide</span>
|
||||
<h1>{material.name} bei Flecken richtig behandeln</h1>
|
||||
<p class="lead-copy">{material.short}</p>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Empfindlichkeit</h2>
|
||||
<p>{material.sensitivity}</p>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Sichere Züge</h2>
|
||||
<ul class="simple-list">
|
||||
{material.safeMoves.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="content-panel content-panel--warn">
|
||||
<h2>Darauf lieber verzichten</h2>
|
||||
<ul class="simple-list">
|
||||
{material.avoid.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<aside class="sidebar-shell">
|
||||
<div class="sidebar-card">
|
||||
<h2>Typische Flecken für dieses Material</h2>
|
||||
<ul class="simple-list">
|
||||
{material.goodFor.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
<AdSlot className="sidebar-ad" />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { company } from '../data/site';
|
||||
import { privacyHighlights, copyrightSafetyNote } from '../data/legal';
|
||||
---
|
||||
|
||||
<BaseLayout title="Datenschutz | Fleckenhilfe" description="Datenschutzhinweise für Fleckenhilfe.">
|
||||
<main class="section legal-page">
|
||||
<div class="container legal-shell">
|
||||
<span class="eyebrow">Rechtliches</span>
|
||||
<h1>Datenschutz</h1>
|
||||
<p>Diese Website bietet allgemeine Hinweise zum Entfernen von Flecken auf unterschiedlichen Materialien und Oberflächen.</p>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Verantwortlich</h2>
|
||||
<p>
|
||||
{company.owner}<br />
|
||||
{company.addressLine1}<br />
|
||||
{company.addressLine2}<br />
|
||||
{company.addressLine3}
|
||||
</p>
|
||||
<p>E-Mail: {company.email}</p>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Wichtige Hinweise</h2>
|
||||
<ul class="simple-list">
|
||||
{privacyHighlights.map((item) => <li>{item}</li>)}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>{copyrightSafetyNote.title}</h2>
|
||||
<p>{copyrightSafetyNote.text}</p>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,45 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import AdSlot from '../../components/AdSlot.astro';
|
||||
import { guides } from '../../data/content';
|
||||
|
||||
export function getStaticPaths() {
|
||||
return guides.map((guide) => ({ params: { slug: guide.slug }, props: { guide } }));
|
||||
}
|
||||
|
||||
const { guide } = Astro.props;
|
||||
---
|
||||
|
||||
<BaseLayout title={`${guide.title} | Fleckenhilfe`} description={guide.short}>
|
||||
<main class="section">
|
||||
<div class="container detail-layout">
|
||||
<article class="article-shell">
|
||||
<span class="eyebrow">Ratgeber</span>
|
||||
<h1>{guide.title}</h1>
|
||||
<p class="lead-copy">{guide.intro}</p>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Praktische Schritte</h2>
|
||||
<ol class="number-list">
|
||||
{guide.steps.map((step) => <li>{step}</li>)}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="content-panel">
|
||||
<h2>Wichtige Hinweise</h2>
|
||||
<ul class="simple-list">
|
||||
{guide.tips.map((tip) => <li>{tip}</li>)}
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<aside class="sidebar-shell">
|
||||
<div class="sidebar-card">
|
||||
<h2>Warum das nützlich ist</h2>
|
||||
<p>{guide.short}</p>
|
||||
</div>
|
||||
<AdSlot className="sidebar-ad" />
|
||||
</aside>
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import StainFinder from '../../components/StainFinder.astro';
|
||||
import AdSlot from '../../components/AdSlot.astro';
|
||||
import { stains } from '../../data/content';
|
||||
---
|
||||
|
||||
<BaseLayout title="Fleck-Finder | Fleckenhilfe" description="Finde schnell die passende Anleitung für Rotwein, Kaffee, Fett, Blut, Deo und andere Alltagsflecken.">
|
||||
<main class="section">
|
||||
<div class="container page-intro">
|
||||
<span class="eyebrow">Tool</span>
|
||||
<h1>Fleck-Finder</h1>
|
||||
<p>Suche nach Fleckart oder filtere nach Kategorie. Die Ergebnisse führen direkt zu einer klaren Anleitung.</p>
|
||||
</div>
|
||||
|
||||
<div class="container feature-shell">
|
||||
<StainFinder stains={stains} />
|
||||
</div>
|
||||
|
||||
<div class="container section">
|
||||
<AdSlot label="Gesponserter Bereich" />
|
||||
</div>
|
||||
</main>
|
||||
</BaseLayout>
|
||||
@@ -0,0 +1,446 @@
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(180deg, #f7f9fc 0%, #ffffff 24%);
|
||||
color: #080808;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
|
||||
.container {
|
||||
width: min(1180px, calc(100% - 32px));
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 30;
|
||||
backdrop-filter: blur(10px);
|
||||
background: rgba(247, 249, 252, 0.88);
|
||||
border-bottom: 1px solid #d8d8d8;
|
||||
}
|
||||
|
||||
.nav {
|
||||
min-height: 74px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #080808;
|
||||
}
|
||||
|
||||
.nav-links,
|
||||
.footer-links {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 18px;
|
||||
color: #363636;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.hero-block {
|
||||
padding: 88px 0 32px;
|
||||
}
|
||||
|
||||
.hero-grid,
|
||||
.detail-layout,
|
||||
.two-column-feature,
|
||||
.footer-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.15fr 0.85fr;
|
||||
gap: 28px;
|
||||
}
|
||||
|
||||
.hero-grid--single-focus {
|
||||
grid-template-columns: minmax(0, 920px);
|
||||
}
|
||||
|
||||
.hero-grid h1,
|
||||
h1 {
|
||||
font-size: clamp(3rem, 6vw, 5.2rem);
|
||||
line-height: 1.02;
|
||||
letter-spacing: -0.06em;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: clamp(1.6rem, 3vw, 2.6rem);
|
||||
line-height: 1.08;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.15rem;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
p, li {
|
||||
color: #222222;
|
||||
font-size: 1.04rem;
|
||||
line-height: 1.72;
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 38px 0 64px;
|
||||
}
|
||||
|
||||
.section--overlap { padding-top: 8px; }
|
||||
.section--tinted { background: linear-gradient(180deg, rgba(20,110,245,0.05), rgba(122,61,255,0.03)); }
|
||||
|
||||
.eyebrow {
|
||||
display: inline-block;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.3px;
|
||||
color: #146ef5;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.eyebrow-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
min-height: 30px;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #d8d8d8;
|
||||
background: #fff;
|
||||
color: #222;
|
||||
font-size: 0.86rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.chip--accent {
|
||||
border-color: rgba(20,110,245,0.2);
|
||||
background: rgba(20,110,245,0.1);
|
||||
color: #146ef5;
|
||||
}
|
||||
|
||||
.hero-copy { max-width: 720px; font-size: 1.08rem; }
|
||||
|
||||
.hero-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin: 22px 0 24px;
|
||||
}
|
||||
|
||||
.cta-primary,
|
||||
.cta-secondary,
|
||||
.cta-outline,
|
||||
.button-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 48px;
|
||||
padding: 0 18px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
transition: transform 160ms ease, border-color 160ms ease, background 160ms ease;
|
||||
}
|
||||
|
||||
.cta-primary {
|
||||
background: #146ef5;
|
||||
color: #fff;
|
||||
box-shadow: rgba(0,0,0,0) 0px 84px 24px, rgba(0,0,0,0.01) 0px 54px 22px, rgba(0,0,0,0.04) 0px 30px 18px, rgba(0,0,0,0.08) 0px 13px 13px, rgba(0,0,0,0.09) 0px 3px 7px;
|
||||
}
|
||||
|
||||
.cta-secondary,
|
||||
.cta-outline,
|
||||
.button-link {
|
||||
background: #fff;
|
||||
color: #080808;
|
||||
border: 1px solid #d8d8d8;
|
||||
}
|
||||
|
||||
.cta-primary:hover,
|
||||
.cta-secondary:hover,
|
||||
.cta-outline:hover,
|
||||
.button-link:hover { transform: translateY(-2px); border-color: #898989; }
|
||||
|
||||
.hero-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hero-stats div,
|
||||
.hero-panel,
|
||||
.feature-shell,
|
||||
.teaser-card,
|
||||
.result-card,
|
||||
.material-row,
|
||||
.guide-card,
|
||||
.content-panel,
|
||||
.sidebar-card,
|
||||
.note-box,
|
||||
.ad-shell,
|
||||
.faq-card,
|
||||
.legal-shell {
|
||||
background: #fff;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0,0,0,0) 0px 84px 24px, rgba(0,0,0,0.01) 0px 54px 22px, rgba(0,0,0,0.04) 0px 30px 18px, rgba(0,0,0,0.08) 0px 13px 13px, rgba(0,0,0,0.09) 0px 3px 7px;
|
||||
}
|
||||
|
||||
.hero-stats div {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.hero-inline-note {
|
||||
margin-top: 16px;
|
||||
padding: 14px 16px;
|
||||
border-left: 4px solid #146ef5;
|
||||
background: rgba(20,110,245,0.06);
|
||||
border-radius: 8px;
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.hero-stats strong {
|
||||
display: block;
|
||||
font-size: 1.55rem;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.hero-stats span { color: #5a5a5a; font-size: 0.92rem; }
|
||||
|
||||
.feature-shell {
|
||||
padding: 24px;
|
||||
background: linear-gradient(180deg, #ffffff, #fbfdff);
|
||||
}
|
||||
|
||||
.feature-shell--primary {
|
||||
border-color: rgba(20,110,245,0.24);
|
||||
}
|
||||
|
||||
.section-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr minmax(240px, 340px);
|
||||
gap: 18px;
|
||||
align-items: end;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
|
||||
.section-head--tool {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.field { display: grid; gap: 8px; }
|
||||
.field span { font-size: 0.9rem; font-weight: 600; color: #222; }
|
||||
|
||||
.finder__controls {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.7fr) minmax(200px, 0.7fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.field input,
|
||||
.field select {
|
||||
min-height: 50px;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 8px;
|
||||
padding: 0 14px;
|
||||
font: inherit;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.finder__results,
|
||||
.card-grid,
|
||||
.guide-stack,
|
||||
.faq-stack {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.finder__results,
|
||||
.card-grid--three {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.result-card,
|
||||
.teaser-card,
|
||||
.guide-card,
|
||||
.faq-card { padding: 18px; }
|
||||
|
||||
.result-card__top,
|
||||
.teaser-card__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.result-icon {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, rgba(20,110,245,0.14), rgba(122,61,255,0.14));
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.result-icon--large {
|
||||
width: 74px;
|
||||
height: 74px;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.result-category {
|
||||
color: #146ef5;
|
||||
font-size: 0.82rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.finder__footer { margin-top: 18px; }
|
||||
.finder__empty { margin-top: 18px; }
|
||||
|
||||
.material-list { display: grid; gap: 14px; }
|
||||
|
||||
.quick-link-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.quick-link-card {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 14px;
|
||||
align-items: start;
|
||||
padding: 18px;
|
||||
background: #fff;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0,0,0,0) 0px 84px 24px, rgba(0,0,0,0.01) 0px 54px 22px, rgba(0,0,0,0.04) 0px 30px 18px, rgba(0,0,0,0.08) 0px 13px 13px, rgba(0,0,0,0.09) 0px 3px 7px;
|
||||
}
|
||||
|
||||
.quick-link-card strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.quick-link-card p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.material-row {
|
||||
padding: 18px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
}
|
||||
.material-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.two-column-feature { align-items: start; }
|
||||
.guide-stack { grid-template-columns: 1fr; }
|
||||
|
||||
.note-box, .legal-shell, .article-shell { padding: 24px; }
|
||||
.note-box strong { display: block; margin-bottom: 10px; }
|
||||
|
||||
.detail-layout { align-items: start; }
|
||||
.article-shell { display: grid; gap: 18px; }
|
||||
.sidebar-shell { display: grid; gap: 18px; }
|
||||
.sidebar-card { padding: 20px; position: sticky; top: 92px; }
|
||||
.sidebar-ad { position: sticky; top: 320px; }
|
||||
|
||||
.hero-tip-box {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
border: 1px solid #d8d8d8;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(180deg, rgba(20,110,245,0.06), rgba(122,61,255,0.05));
|
||||
}
|
||||
|
||||
.lead-copy { font-size: 1.08rem; }
|
||||
.tag-row { display: flex; flex-wrap: wrap; gap: 10px; }
|
||||
.number-list { padding-left: 20px; }
|
||||
.simple-list { padding-left: 18px; }
|
||||
.content-panel { padding: 20px; }
|
||||
.content-panel--warn { background: linear-gradient(180deg, rgba(238,29,54,0.04), #fff); }
|
||||
|
||||
.ad-shell { padding: 18px; }
|
||||
.ad-shell__label {
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.4px;
|
||||
color: #7a3dff;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.ad-shell__placeholder {
|
||||
min-height: 96px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border: 1px dashed #ababab;
|
||||
border-radius: 8px;
|
||||
color: #5a5a5a;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: linear-gradient(180deg, #fff, #f7f9fc);
|
||||
}
|
||||
|
||||
.page-intro { margin-bottom: 22px; }
|
||||
.legal-shell { width: min(820px, 100%); }
|
||||
|
||||
.site-footer {
|
||||
border-top: 1px solid #d8d8d8;
|
||||
background: #080808;
|
||||
color: #fff;
|
||||
margin-top: 48px;
|
||||
}
|
||||
.site-footer p, .site-footer a { color: rgba(255,255,255,0.76); }
|
||||
.footer-grid {
|
||||
padding: 28px 0 40px;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.hero-grid,
|
||||
.detail-layout,
|
||||
.two-column-feature,
|
||||
.footer-grid,
|
||||
.section-head,
|
||||
.finder__results,
|
||||
.card-grid--three,
|
||||
.hero-stats,
|
||||
.material-row,
|
||||
.quick-link-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.material-meta { justify-content: flex-start; }
|
||||
.sidebar-card, .sidebar-ad { position: static; }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.finder__controls { grid-template-columns: 1fr; }
|
||||
.hero-block { padding-top: 56px; }
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user