Design Raster: neue Startseite und alle Unterseiten, echte Fotos, neue Seiten Barber, Neu bei uns, Karriere, Gutscheine; Anfrage mit Braut- und Great-Lengths-Feldern; 301 für Ortsseiten
No files matched your search
@@ -1,68 +1,23 @@
|
|||||||
# DESIGN.md · Friseur-Team VK
|
# Design „Raster“ (ab 10.10.2026)
|
||||||
|
|
||||||
Gestaltungsregeln für die Website. Die Tokens stehen in `src/styles/tokens.css`; diese Datei erklärt, warum sie so sind.
|
Grundlage: Claude-Design-Variante D „Raster“, für den Livebetrieb verfeinert. Tokens: `src/styles/tokens.css`.
|
||||||
|
|
||||||
## Haltung
|
## Idee
|
||||||
|
Ein weißes Blatt auf kühlem Grund. Zwei feine senkrechte Linien bei 15 % und 40 % teilen das Blatt in drei Spalten (A · B · C). Inhalte richten sich an diesen Linien aus (`.r3` mit `.a`, `.b`, `.c`, `.ab`, `.bc`). Mobil fallen die Linien weg, alles läuft einspaltig.
|
||||||
Ruhiges Premium, wie ein gutes Magazin über einen echten Salon. Hell, handwerklich, selbstbewusst. Die Seite soll Menschen ansprechen, die gezielt für Brautstyling, Balayage und Great Lengths kommen, und Stammkundschaft den kurzen Weg zum Telefon lassen.
|
|
||||||
|
|
||||||
Szene: Eine Frau Anfang dreißig sitzt abends auf dem Sofa, das Handy in der Hand, und vergleicht Salons für ihre Hochzeit im nächsten Sommer. Daraus folgt: helles Thema, große Bilder, kurze Wege zu Preis und Anfrage, nichts, was nach Rabatt aussieht.
|
|
||||||
|
|
||||||
Drei Wörter für die Marke: **hell, präzise, nahbar**.
|
|
||||||
|
|
||||||
## Farbe
|
|
||||||
|
|
||||||
Alle Farben stammen aus dem Logo und aus dem Salon selbst (weiße Räume, blaue Wandpaneele). Strategie: **Restrained mit einer Committed-Fläche pro Seite**. Grund ist Papier-Weiß, Petrol trägt Akzente und genau eine vollflächige Zone (Anfrage-Band am Seitenende).
|
|
||||||
|
|
||||||
| Rolle | Token | OKLCH | Hex (Quelle) | Einsatz |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| Anthrazit | `--c-anthrazit` | `oklch(33.8% 0.012 140.5)` | #343933 | Text, Footer |
|
|
||||||
| Petrol | `--c-petrol` | `oklch(50.7% 0.105 234)` | #036E98 | Links, Knöpfe, Akzentwörter, Anfrage-Band |
|
|
||||||
| Hellblau | `--c-hellblau` | `oklch(64.9% 0.076 224.9)` | #5899B2 | nur Deko, nie für Text auf Weiß (3,2:1) |
|
|
||||||
| Silbergrau | `--c-silber` | `oklch(71.1% 0.009 225.1)` | #9CA3A6 | Rahmen, Formularränder |
|
|
||||||
| Nebelgrau | `--c-nebel` | `oklch(88.4% 0.008 216.6)` | #D3DADC | Bildgrund, Flächen |
|
|
||||||
| Papier | `--c-papier` | `oklch(98.7% 0.003 220)` | abgeleitet | Seitengrund, kein reines Weiß |
|
|
||||||
| Petrol tief | `--c-petrol-tief` | `oklch(36% 0.075 236)` | abgeleitet | Hover, Wandpaneel im Anfrage-Band |
|
|
||||||
| Text leise | `--c-text-leise` | `oklch(46% 0.012 200)` | abgeleitet | Nebentext, ≥ 4,5:1 auf Papier |
|
|
||||||
|
|
||||||
Kontraste (WCAG 2.1 AA): Anthrazit auf Papier 11,8:1, Petrol auf Papier 5,7:1, Papier auf Petrol 5,7:1. Hellblau und Silbergrau tragen nie Fließtext.
|
|
||||||
|
|
||||||
## Schrift
|
## Schrift
|
||||||
|
- Überschriften: Libre Caslon Condensed (variabel, selbst gehostet). Groß, eng, `letter-spacing: -0.018em`.
|
||||||
|
- Initiale: Pinyon Script, nur der erste Buchstabe einer Hauptüberschrift (`H.astro`), in Blau. Höchstens eine pro Bildschirm. Klammern im Hero ebenfalls Pinyon.
|
||||||
|
- Text: Albert Sans, 17 px, Zeilenhöhe 1,62, Zeilenlänge max. 36 rem. Nummern und Labels 12,5 px in `--ink-3`.
|
||||||
|
- Gegenüber dem Entwurf: Fließtext von 13,5–14,5 px auf 17 px angehoben, Überschriften fließend skaliert (h1 52–132 px, h2 44–88 px), Labels lesbarer, Abstände auf 8er-Rhythmus.
|
||||||
|
|
||||||
Selbst gehostet unter `public/fonts/` (OFL), auf Latin-1 beschnitten, Achsen begrenzt. Keine Anfragen an Google Fonts oder CDNs.
|
## Farbe
|
||||||
|
Tinte `--ink`, Blau aus den Wandpaneelen `--blue` (Akzent, Preise, Initialen), Linien `--line`, zarte Fläche `--blue-wash`. Keine Verläufe, keine Schatten außer der Kundenkarte.
|
||||||
|
|
||||||
- **Bodoni Moda** (variabel, Optical Size 11 bis 96): Überschriften, Zahlen, Zitate. Hoher Kontrast wie im Mode- und Beauty-Magazin; bei großen Größen schaltet die optische Größe automatisch auf feine Haarstriche. Kursiv nur für das Akzentwort einer Überschrift (in Petrol).
|
## Bausteine
|
||||||
- **Archivo** (variabel, Breite 68 bis 100 %, Gewicht 350 bis 700): Fließtext, Formulare. Labels in Großbuchstaben laufen schmal (72 % Breite) und greifen damit die schmale Groteskschrift des Logos („FRISEUR TEAM“) auf.
|
Ovaler Knopf (`.oval`, Caslon 19 px, Hover blau), Pfeil-Link (`.go`), Leistungszeilen (`.srow`), Zahlenkacheln (`.tile`), Vorher/Nachher (`.case`), Bewertungen (`.revs`), Schritte (`.steps`), Preisgruppen (`PriceGroup.astro`), FAQ (`details`).
|
||||||
|
|
||||||
Skala (fließend, Verhältnis ca. 1,25 bis 1,33): Label 13 px · Klein 15 px · Text 17 px · Lead 19 bis 23 px · H4 20 bis 24 px · H3 24 bis 34 px · H2 34 bis 60 px · H1 44 bis 96 px. Untergrenze überall 12 px. Zeilenlänge Fließtext höchstens 38 rem (rund 70 Zeichen).
|
## Regeln
|
||||||
|
- Keine Inline-Styles und keine Inline-Skripte (CSP `style-src 'self'`, `script-src 'self'`). Abstände über Hilfsklassen (`.mt-*`, `.gap-*`).
|
||||||
## Form und Motive
|
- Einblenden nur für Elemente unterhalb des ersten Bildschirms (`data-rv`, `data-clip`), bei `prefers-reduced-motion` aus.
|
||||||
|
- Echte Fotos aus dem Salon. Vorher/Nachher immer paarweise mit Beschriftung.
|
||||||
- **Bogen**: Bilder und Fotoflächen der Premium-Leistungen haben oben einen Halbkreis (`--arch`). Vorbild ist der Rundbogen-Durchgang im Salon (sichtbar auf salon-innen-2 und -3).
|
|
||||||
- **Wandpaneel**: Ein senkrechter Petrol-Block hinter Hero-Bildern und im Anfrage-Band, wie die blauen Wandflächen zwischen den Spiegeln.
|
|
||||||
- **Haarlinien**: Listen und Preise stehen auf 1 px Linien, Abschnittsanfänge auf 1,5 px Anthrazit. Keine Karten-Raster.
|
|
||||||
- **Knöpfe**: Pille, ein Hauptknopf pro Abschnitt (Petrol), Nebenknopf als Kontur.
|
|
||||||
- **Fotoflächen (Shotliste)**: Solange Arbeitsfotos fehlen, zeigt `Shot.astro` eine beschriftete Fläche mit Motiv und Bildidee. Das ist die Liste für das Shooting.
|
|
||||||
|
|
||||||
## Abstände
|
|
||||||
|
|
||||||
4-px-Raster (`--s-1` bis `--s-9`). Abschnitte atmen mit `--section` (72 bis 152 px). Seitenrand `--gutter` (20 bis 56 px). Rhythmus bewusst wechselnd: enge Gruppen, große Abstände zwischen Abschnitten.
|
|
||||||
|
|
||||||
## Bewegung
|
|
||||||
|
|
||||||
Ein Auftritt beim Laden (Hero-Text steigt, Wandpaneel wächst), dazu dezentes Einblenden beim Scrollen über CSS `animation-timeline: view()`. Kurve ease-out-quint, keine Federn. Bei `prefers-reduced-motion: reduce` ist alles aus. Die H1 wird nur verschoben, nie ausgeblendet (LCP).
|
|
||||||
|
|
||||||
## Barrierefreiheit
|
|
||||||
|
|
||||||
WCAG 2.1 AA. Fokus sichtbar (2 px Petrol), Touch-Ziele mindestens 44 px, Formularfelder 52 px hoch und 17 px Schrift (kein Zoom auf iOS), Fehlermeldungen als Text mit Fehlerübersicht, Menü und FAQ funktionieren ohne JavaScript (`<details>`).
|
|
||||||
|
|
||||||
## Nicht machen
|
|
||||||
|
|
||||||
- Beige, Taupe, Rosé, Lila-Verläufe, Glas-Effekte, Neon
|
|
||||||
- Inter, Roboto, Montserrat, Space Grotesk und andere Standardschriften
|
|
||||||
- Stockfotos oder KI-Bilder von Frisuren; erfundene Bewertungen oder Zitate
|
|
||||||
- Gleich große Karten im 3er- oder 6er-Raster für Leistungen
|
|
||||||
- Pulsierende Knöpfe, Pop-ups, Cookie-Banner (es gibt keine Cookies)
|
|
||||||
- Farbige Seitenstreifen an Karten, Verlaufstext, große Zahl mit kleinem Label als Statistikreihe
|
|
||||||
- Gedankenstriche als Satzzeichen in Texten, Superlative, „exklusiv“, „einzigartig“
|
|
||||||
- Inline-`style`-Attribute (die CSP erlaubt nur `style-src 'self'`)
|
|
||||||
@@ -25,6 +25,14 @@ server {
|
|||||||
location = /balayage { return 301 /balayage-farbe; }
|
location = /balayage { return 301 /balayage-farbe; }
|
||||||
location = /haarverdichtung { return 301 /haarverlaengerung; }
|
location = /haarverdichtung { return 301 /haarverlaengerung; }
|
||||||
location = /treuekarte { return 301 /kundenkarte; }
|
location = /treuekarte { return 301 /kundenkarte; }
|
||||||
|
location = /herren { return 301 /barber; }
|
||||||
|
location = /karriere-jobs { return 301 /karriere; }
|
||||||
|
# Frühere Ortsseiten (Brückenseiten-Muster) zusammenführen
|
||||||
|
location = /friseur-tostedt { return 301 /; }
|
||||||
|
location = /friseur-buchholz-in-der-nordheide { return 301 /kontakt; }
|
||||||
|
location = /friseur-handeloh { return 301 /kontakt; }
|
||||||
|
location = /friseur-hollenstedt { return 301 /kontakt; }
|
||||||
|
location = /friseur-schneverdingen { return 301 /kontakt; }
|
||||||
|
|
||||||
# Abschließenden Schrägstrich entfernen (kanonische URLs ohne /)
|
# Abschließenden Schrägstrich entfernen (kanonische URLs ohne /)
|
||||||
rewrite ^/(.+)/$ /$1 permanent;
|
rewrite ^/(.+)/$ /$1 permanent;
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
Archivo: SIL Open Font License 1.1, (c) The Archivo Project Authors
|
Alle Schriften stehen unter der SIL Open Font License 1.1 (https://openfontlicense.org)
|
||||||
Bodoni Moda: SIL Open Font License 1.1, (c) The Bodoni Moda Project Authors
|
und werden selbst gehostet (keine Verbindung zu Google).
|
||||||
Subset (Latin-1 + Typografie) und Achsen beschnitten mit fontTools; Quelle: @fontsource-variable
|
|
||||||
|
- Libre Caslon Condensed (Pablo Impallari, Rodrigo Fuenzalida), latin, variabel 400–700
|
||||||
|
- Pinyon Script (Nicole Fally), latin
|
||||||
|
- Albert Sans (Andreas Rasmussen), latin, variabel 400–700
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# Friseur-Team VK
|
# Friseur-Team VK
|
||||||
|
|
||||||
> Friseursalon und Meisterbetrieb in Tostedt (Landkreis Harburg, Niedersachsen), seit November 2017. Inhaberin: Veronika Sprangel, Friseurmeisterin. Schwerpunkte: Brautstyling, Balayage und Haarfarbe, Haarverlängerung und Haarverdichtung mit Great Lengths. Produkte: Redken, Paul Mitchell. Google: 4,9 Sterne bei 193 Bewertungen (Stand September 2026).
|
> Friseursalon und Meisterbetrieb in Tostedt (Landkreis Harburg, Niedersachsen), seit November 2017. Inhaberin: Veronika Sprangel, Friseurmeisterin. Offizieller Great-Lengths-Partner mit der Auszeichnung „Master of Great Lengths“ (Haarverlängerung und Haarverdichtung). Paul Mitchell Flagship-Salon, außerdem Redken. Schwerpunkte: Brautstyling, Balayage und Haarfarbe, Great Lengths; dazu Damen, Herren/Barber, Kinder. Google: 4,9 Sterne bei 194 Bewertungen (Stand Oktober 2026).
|
||||||
|
|
||||||
Termine werden nicht online gebucht, sondern telefonisch, per WhatsApp oder über ein Anfrageformular mit Rückruf vereinbart.
|
Termine werden nicht online gebucht. Braut, Farbe und Great Lengths beginnen mit einer Anfrage (Formular mit Fotos, Rückruf innerhalb von zwei Werktagen) und einer Beratung. Schnitt, Herren und Kinder telefonisch oder per WhatsApp.
|
||||||
|
|
||||||
## Kontakt
|
## Kontakt
|
||||||
|
|
||||||
@@ -11,19 +11,30 @@ Termine werden nicht online gebucht, sondern telefonisch, per WhatsApp oder übe
|
|||||||
- E-Mail: info@friseur-team-vk.de
|
- E-Mail: info@friseur-team-vk.de
|
||||||
- Öffnungszeiten: Di 9–18, Mi 9–20, Do 9–18, Fr 9–20, Sa 9–14 Uhr; Mo und So geschlossen
|
- Öffnungszeiten: Di 9–18, Mi 9–20, Do 9–18, Fr 9–20, Sa 9–14 Uhr; Mo und So geschlossen
|
||||||
- Instagram: https://www.instagram.com/friseur.team_vk/
|
- Instagram: https://www.instagram.com/friseur.team_vk/
|
||||||
|
- Great-Lengths-Partnereintrag: https://www.greatlengthspartner.com/partner/friseur-team-vk-tostedt
|
||||||
|
|
||||||
|
## Häufige Fragen
|
||||||
|
|
||||||
|
- Wer macht Great Lengths in Tostedt? Das Friseur-Team VK, Kastanienallee 1, als Master of Great Lengths. Beratung kostenlos, Verdichtung ab 350 €, Verlängerung ab 650 € (Richtwerte).
|
||||||
|
- Brautfrisur in Tostedt? Probestyling ab 85 €, Brautfrisur ab 150 €, Styling vor Ort im Umkreis von etwa 30 km (Richtwerte).
|
||||||
|
- Balayage in Tostedt? Preise nach Haarlänge ab 190 €, Farbberatung 25 € wird verrechnet (Richtwerte).
|
||||||
|
|
||||||
## Seiten
|
## Seiten
|
||||||
|
|
||||||
- [Start](https://vk-friseur.heidrich-digital.de/): Überblick, Schwerpunkte, Team, Öffnungszeiten
|
- [Start](https://vk-friseur.heidrich-digital.de/): Überblick, Wartezeiten, Ergebnisse, Bewertungen
|
||||||
- [Brautservice](https://vk-friseur.heidrich-digital.de/brautservice): Beratung, Probetermin, Styling am Hochzeitstag, auch vor Ort; Frisur und Make-up
|
- [Braut](https://vk-friseur.heidrich-digital.de/brautservice): Ablauf, Galerie, Preise, Fragen
|
||||||
- [Balayage & Farbe](https://vk-friseur.heidrich-digital.de/balayage-farbe): Balayage, Foliensträhnen, Coloration, Glossing
|
- [Balayage & Farbe](https://vk-friseur.heidrich-digital.de/balayage-farbe): Vorher/Nachher, Preise nach Länge
|
||||||
- [Haarverlängerung](https://vk-friseur.heidrich-digital.de/haarverlaengerung): Great Lengths, Verlängerung und Verdichtung, Beratungstermin vorab
|
- [Great Lengths](https://vk-friseur.heidrich-digital.de/haarverlaengerung): Verlängerung und Verdichtung, Vorher/Nachher, Ablauf, Preise
|
||||||
- [Leistungen & Preise](https://vk-friseur.heidrich-digital.de/leistungen-preise): alle Leistungen mit „ab“-Richtpreisen
|
- [Barber](https://vk-friseur.heidrich-digital.de/barber): Herrenschnitt, Bart, Nassrasur
|
||||||
- [Team](https://vk-friseur.heidrich-digital.de/team): Inhaberin und Team
|
- [Preise](https://vk-friseur.heidrich-digital.de/leistungen-preise): alle Leistungen mit Richtpreisen
|
||||||
- [Kundenkarte](https://vk-friseur.heidrich-digital.de/kundenkarte): digitale Stempelkarte (Apple Wallet, Google Wallet, Web-Karte, gedruckt); 6 Stempel = Pflege-Upgrade gratis
|
- [Neu bei uns](https://vk-friseur.heidrich-digital.de/neue-kundinnen): Ablauf für neue Kundinnen, Warteliste
|
||||||
- [Anfrage](https://vk-friseur.heidrich-digital.de/anfrage): Formular mit Rückruf
|
- [Team](https://vk-friseur.heidrich-digital.de/team)
|
||||||
- [Kontakt](https://vk-friseur.heidrich-digital.de/kontakt): Adresse, Telefon, WhatsApp, Öffnungszeiten
|
- [Karriere](https://vk-friseur.heidrich-digital.de/karriere): Stellen mit Gehaltsangabe, Ausbildung
|
||||||
|
- [Kundenkarte](https://vk-friseur.heidrich-digital.de/kundenkarte): digitale Stempelkarte, 6 Stempel = Pflege-Upgrade
|
||||||
|
- [Gutscheine](https://vk-friseur.heidrich-digital.de/gutscheine)
|
||||||
|
- [Anfrage](https://vk-friseur.heidrich-digital.de/anfrage)
|
||||||
|
- [Kontakt](https://vk-friseur.heidrich-digital.de/kontakt): Adresse, Öffnungszeiten, Anfahrt
|
||||||
|
|
||||||
## Hinweis
|
## Hinweis
|
||||||
|
|
||||||
Diese Adresse ist eine Vorschau der neuen Website. Preise sind Richtwerte und werden mit dem Salon abgestimmt.
|
Diese Adresse ist eine Vorschau der neuen Website. Preise und Wartezeiten sind Beispiele und werden mit dem Salon abgestimmt.
|
||||||
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 176 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 147 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 186 KiB |
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 182 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 199 KiB |
|
After Width: | Height: | Size: 193 KiB |
|
After Width: | Height: | Size: 195 KiB |
|
After Width: | Height: | Size: 188 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 161 KiB |
|
After Width: | Height: | Size: 198 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 155 KiB |
|
After Width: | Height: | Size: 171 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 177 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 428 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 128 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 133 KiB |
@@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
/** Vorher/Nachher-Vergleich. Ohne JS steht der Regler auf der Mitte. */
|
|
||||||
import Shot from './Shot.astro';
|
|
||||||
interface Props { before: string; after: string; detail?: string }
|
|
||||||
const { before, after, detail } = Astro.props;
|
|
||||||
---
|
|
||||||
<div class="ba" data-ba>
|
|
||||||
<div class="ba__stage">
|
|
||||||
<div class="ba__layer ba__after"><Shot title={after} ratio="4/5" tone="petrol" /></div>
|
|
||||||
<div class="ba__layer ba__before"><Shot title={before} ratio="4/5" /></div>
|
|
||||||
<div class="ba__line" aria-hidden="true"><span></span></div>
|
|
||||||
<span class="ba__tag ba__tag--l" aria-hidden="true">Vorher</span>
|
|
||||||
<span class="ba__tag ba__tag--r" aria-hidden="true">Nachher</span>
|
|
||||||
</div>
|
|
||||||
{detail && <p class="ba__note">{detail}</p>}
|
|
||||||
<label class="ba__ctrl">
|
|
||||||
<span class="visually-hidden">Vergleich verschieben: links vorher, rechts nachher</span>
|
|
||||||
<input type="range" min="0" max="100" value="50" step="1" />
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
document.querySelectorAll<HTMLElement>('[data-ba]').forEach((el) => {
|
|
||||||
const input = el.querySelector('input');
|
|
||||||
const set = (v: string) => el.style.setProperty('--pos', v + '%');
|
|
||||||
input?.addEventListener('input', () => set(input.value));
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<style>
|
|
||||||
.ba { --pos: 50%; max-width: 34rem; }
|
|
||||||
.ba__stage { position: relative; aspect-ratio: 4 / 5; overflow: hidden; border-radius: var(--r-m); }
|
|
||||||
.ba__layer { position: absolute; inset: 0; }
|
|
||||||
.ba__layer :global(.shot) { height: 100%; }
|
|
||||||
.ba__layer :global(.shot__title) { font-size: 1.125rem; max-width: 10ch; }
|
|
||||||
.ba__after :global(.shot__in) { align-items: flex-end; text-align: right; }
|
|
||||||
.ba__note { margin-top: var(--s-2); font-size: var(--fs-small); color: var(--c-text-leise); }
|
|
||||||
.ba__before { clip-path: inset(0 calc(100% - var(--pos)) 0 0); }
|
|
||||||
.ba__line { position: absolute; top: 0; bottom: 0; left: var(--pos); width: 2px; margin-left: -1px; background: var(--c-papier); pointer-events: none; }
|
|
||||||
.ba__line span {
|
|
||||||
position: absolute; top: 50%; left: 50%; width: 2.75rem; height: 2.75rem; margin: -1.375rem 0 0 -1.375rem;
|
|
||||||
border-radius: 50%; background: var(--c-papier); box-shadow: 0 4px 14px oklch(20% 0.03 235 / 0.25);
|
|
||||||
}
|
|
||||||
.ba__line span::before, .ba__line span::after {
|
|
||||||
content: ''; position: absolute; top: 50%; width: 0.5rem; height: 0.5rem; margin-top: -0.25rem;
|
|
||||||
border: solid var(--c-petrol); border-width: 0 0 2px 2px;
|
|
||||||
}
|
|
||||||
.ba__line span::before { left: 0.7rem; transform: rotate(45deg); }
|
|
||||||
.ba__line span::after { right: 0.7rem; transform: rotate(-135deg); }
|
|
||||||
.ba__tag {
|
|
||||||
position: absolute; top: var(--s-4); padding: 0.3rem 0.7rem; border-radius: var(--r-pill);
|
|
||||||
background: var(--c-papier); color: var(--c-anthrazit);
|
|
||||||
font-size: var(--fs-label); font-weight: 600; font-stretch: 72%; text-transform: uppercase; letter-spacing: 0.1em;
|
|
||||||
}
|
|
||||||
.ba__tag--l { left: var(--s-4); }
|
|
||||||
.ba__tag--r { right: var(--s-4); }
|
|
||||||
.ba__ctrl { display: block; margin-top: var(--s-3); }
|
|
||||||
.ba__ctrl input { width: 100%; height: var(--tap); accent-color: var(--c-petrol); cursor: ew-resize; }
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
---
|
||||||
|
/** Abschluss jeder Unterseite: große Zeile, Telefon, Anfrage. */
|
||||||
|
import H from './H.astro';
|
||||||
|
import { salon } from '../data/site';
|
||||||
|
interface Props { title?: string; text?: string; anliegen?: string; cta?: string; phoneFirst?: boolean }
|
||||||
|
const {
|
||||||
|
title = 'Bis bald im Salon',
|
||||||
|
text = 'Für Braut, Balayage und Great Lengths starten wir mit einer kurzen Anfrage. Wir rufen zurück, beraten und tragen den Termin für Sie ein.',
|
||||||
|
anliegen,
|
||||||
|
cta = 'Anfrage stellen',
|
||||||
|
phoneFirst = false,
|
||||||
|
} = Astro.props;
|
||||||
|
const href = anliegen ? `/anfrage?anliegen=${anliegen}` : '/anfrage';
|
||||||
|
---
|
||||||
|
<section class="sec bye rule" aria-label="Kontakt aufnehmen">
|
||||||
|
<div class="pad"><H text={title} class="bye__h" /></div>
|
||||||
|
<div class="r3 bye__grid">
|
||||||
|
<div class="a"></div>
|
||||||
|
<div class="b bye__phone"><a class:list={['oval', { 'oval--fill': phoneFirst }]} href={salon.phoneHref}>{salon.phoneDisplay}</a></div>
|
||||||
|
<div class="c gap-5" data-rv>
|
||||||
|
<p class="maxw-s">{text}</p>
|
||||||
|
<div class="row">
|
||||||
|
{!phoneFirst && <a class="oval oval--fill" href={href}>{cta}</a>}
|
||||||
|
<a class="go" href={salon.whatsappHref} rel="noopener">WhatsApp</a>
|
||||||
|
<a class="go" href={salon.routeUrl} rel="noopener">Route planen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
---
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
import { salon } from '../data/site';
|
|
||||||
interface Props { title?: string; text?: string; anliegen?: string; button?: string }
|
|
||||||
const {
|
|
||||||
title = 'Erzählen Sie uns, was Sie vorhaben.',
|
|
||||||
text = 'Für Brautstyling, Balayage und Haarverlängerung beginnen wir mit einer kurzen Anfrage. Wir melden uns telefonisch, besprechen alles und tragen den Termin für Sie ein.',
|
|
||||||
anliegen,
|
|
||||||
button = 'Anfrage stellen',
|
|
||||||
} = Astro.props;
|
|
||||||
const href = anliegen ? `/anfrage?anliegen=${anliegen}` : '/anfrage';
|
|
||||||
---
|
|
||||||
<section class="cta on-dark" aria-labelledby="cta-h">
|
|
||||||
<div class="wrap cta__in">
|
|
||||||
<div class="cta__panel" aria-hidden="true"></div>
|
|
||||||
<div class="cta__txt">
|
|
||||||
<p class="label cta__label">Anfrage</p>
|
|
||||||
<h2 id="cta-h">{title}</h2>
|
|
||||||
<p class="cta__p">{text}</p>
|
|
||||||
<div class="btn-row">
|
|
||||||
<a class="btn btn--light" href={href}>{button}<Icon name="arrow" /></a>
|
|
||||||
<a class="btn btn--outline-light" href={salon.phoneHref}><Icon name="phone" />{salon.phoneDisplay}</a>
|
|
||||||
</div>
|
|
||||||
<p class="cta__small">Schon Kundin oder Kunde bei uns? Rufen Sie einfach an oder schreiben Sie uns <a href={salon.whatsappHref} rel="noopener">per WhatsApp</a>.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<style>
|
|
||||||
.cta { background: var(--c-petrol); color: var(--c-papier); position: relative; overflow: hidden; }
|
|
||||||
.cta__in { position: relative; padding-block: var(--section); }
|
|
||||||
.cta__panel {
|
|
||||||
position: absolute; top: 0; bottom: 0; right: var(--gutter);
|
|
||||||
width: clamp(3rem, 12vw, 11rem);
|
|
||||||
background: var(--c-petrol-tief);
|
|
||||||
}
|
|
||||||
.cta__panel::after {
|
|
||||||
content: ''; position: absolute; top: 0; bottom: 0; left: -0.75rem; width: 1px;
|
|
||||||
background: oklch(100% 0 0 / 0.28);
|
|
||||||
}
|
|
||||||
.cta__txt { position: relative; max-width: 44rem; }
|
|
||||||
@media (max-width: 47.99rem) { .cta__panel { opacity: 0.55; } }
|
|
||||||
.cta__label { color: oklch(95% 0.025 225); }
|
|
||||||
.cta h2 { color: var(--c-papier); margin-top: var(--s-4); }
|
|
||||||
.cta__p { margin-top: var(--s-5); font-size: var(--fs-lead); line-height: 1.5; color: oklch(96% 0.01 225); max-width: 36rem; }
|
|
||||||
.cta .btn-row { margin-top: var(--s-6); }
|
|
||||||
.cta__small { margin-top: var(--s-6); font-size: var(--fs-small); color: oklch(93% 0.015 225); max-width: 34rem; }
|
|
||||||
.cta__small a { color: var(--c-papier); }
|
|
||||||
</style>
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props { text: string }
|
|
||||||
const { text } = Astro.props;
|
|
||||||
---
|
|
||||||
<div class="draft" role="note">
|
|
||||||
<p class="label">Entwurf · vor Livegang prüfen</p>
|
|
||||||
<p>{text}</p>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
.draft { padding: var(--s-5); border: 1.5px solid var(--c-petrol); border-radius: var(--r-m); background: var(--c-petrol-hell); display: grid; gap: var(--s-2); max-width: var(--measure); }
|
|
||||||
</style>
|
|
||||||
@@ -1,43 +1,18 @@
|
|||||||
---
|
---
|
||||||
/** Echte, belegbare Fragen. Erzeugt zusätzlich FAQPage-Schema über die Seite. */
|
/** Häufige Fragen als details/summary, dazu FAQPage-Schema über die Seite. */
|
||||||
interface Props { items: { q: string; a: string }[]; title?: string }
|
interface Props { items: { q: string; a: string }[]; title?: string }
|
||||||
const { items, title = 'Häufige Fragen' } = Astro.props;
|
const { items, title = 'Häufige Fragen' } = Astro.props;
|
||||||
---
|
---
|
||||||
<section class="section faq" aria-labelledby="faq-h">
|
<section class="sec-b" aria-labelledby="faq-h">
|
||||||
<div class="wrap faq__grid">
|
<div class="r3">
|
||||||
<div class="head">
|
<div class="ab"><h2 class="h3" id="faq-h" data-rv>{title}</h2></div>
|
||||||
<p class="label">Fragen</p>
|
<div class="c faq">
|
||||||
<h2 id="faq-h">{title}</h2>
|
{items.map((x) => (
|
||||||
</div>
|
<details>
|
||||||
<div class="faq__list">
|
<summary>{x.q}</summary>
|
||||||
{items.map((it) => (
|
<div class="ans"><p>{x.a}</p></div>
|
||||||
<details class="faq__item">
|
|
||||||
<summary><span>{it.q}</span><span class="faq__ico" aria-hidden="true"></span></summary>
|
|
||||||
<div class="faq__a"><p>{it.a}</p></div>
|
|
||||||
</details>
|
</details>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<style>
|
|
||||||
.faq__grid { display: grid; gap: var(--s-6); }
|
|
||||||
@media (min-width: 60rem) { .faq__grid { grid-template-columns: 1fr 1.6fr; gap: var(--s-8); } }
|
|
||||||
.faq__list { border-top: var(--hair); }
|
|
||||||
.faq__item { border-bottom: var(--hair); }
|
|
||||||
.faq__item summary {
|
|
||||||
list-style: none; cursor: pointer;
|
|
||||||
display: flex; justify-content: space-between; align-items: center; gap: var(--s-4);
|
|
||||||
min-height: 3.5rem; padding-block: var(--s-4);
|
|
||||||
font-weight: 600; font-size: 1.0625rem; line-height: 1.4;
|
|
||||||
}
|
|
||||||
.faq__item summary::-webkit-details-marker { display: none; }
|
|
||||||
.faq__item summary:hover { color: var(--c-petrol); }
|
|
||||||
.faq__ico { position: relative; width: 1rem; height: 1rem; flex: none; }
|
|
||||||
.faq__ico::before, .faq__ico::after {
|
|
||||||
content: ''; position: absolute; left: 0; right: 0; top: 50%; height: 1.5px; background: currentColor;
|
|
||||||
transition: transform var(--t-med) var(--ease-out);
|
|
||||||
}
|
|
||||||
.faq__ico::after { transform: rotate(90deg); }
|
|
||||||
.faq__item[open] .faq__ico::after { transform: rotate(0deg); }
|
|
||||||
.faq__a { padding-bottom: var(--s-5); max-width: 60ch; color: var(--c-text); }
|
|
||||||
</style>
|
|
||||||
@@ -1,92 +1,37 @@
|
|||||||
---
|
---
|
||||||
import Logo from './Logo.astro';
|
import { salon, hours, fmtHours, nav, navMore, PREVIEW } from '../data/site';
|
||||||
import Icon from './Icon.astro';
|
|
||||||
import Hours from './Hours.astro';
|
|
||||||
import { salon, nav } from '../data/site';
|
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
---
|
---
|
||||||
<footer class="ftr on-dark">
|
<footer class="ft">
|
||||||
<div class="wrap ftr__grid">
|
<div class="ft__col">
|
||||||
<div class="ftr__brand">
|
<p class="ft__brand">Friseur-Team VK <span class="paren">seit {salon.sinceYear}</span></p>
|
||||||
<div class="ftr__logo"><Logo width={120} /></div>
|
<p class="small muted mt-3">{salon.street}, {salon.zip} {salon.city}</p>
|
||||||
<p class="ftr__claim">Schön von außen, glücklich von innen.</p>
|
<ul class="ft__links mt-5" role="list">
|
||||||
<p class="ftr__addr">
|
{[...nav, ...navMore].map((n) => <li><a href={n.href}>{n.label}</a></li>)}
|
||||||
{salon.legalName}<br />
|
|
||||||
{salon.street}<br />
|
|
||||||
{salon.zip} {salon.city}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h2 class="label ftr__h">Kontakt</h2>
|
|
||||||
<ul class="ftr__list">
|
|
||||||
<li><a href={salon.phoneHref}><Icon name="phone" />{salon.phoneDisplay}</a></li>
|
|
||||||
<li><a href={salon.whatsappHref} rel="noopener"><Icon name="whatsapp" />WhatsApp</a></li>
|
|
||||||
<li><a href={`mailto:${salon.email}`}><Icon name="mail" />{salon.email}</a></li>
|
|
||||||
<li><a href={salon.instagram} rel="noopener"><Icon name="instagram" />{salon.instagramHandle}</a></li>
|
|
||||||
<li><a href={salon.facebook} rel="noopener"><Icon name="facebook" />Facebook</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ft__col">
|
||||||
<div>
|
<h2>Öffnungszeiten</h2>
|
||||||
<h2 class="label ftr__h">Öffnungszeiten</h2>
|
<div class="ft__hours">
|
||||||
<Hours compact />
|
{hours.filter((h) => h.day !== 'Sonntag').map((h) => (
|
||||||
|
<div class={h.open ? undefined : 'is-closed'}><span>{h.day}</span><span>{fmtHours(h)}</span></div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<p class="cap mt-3">Termine nach Vereinbarung, keine Online-Buchung.</p>
|
||||||
<nav aria-label="Seiten">
|
|
||||||
<h2 class="label ftr__h">Seiten</h2>
|
|
||||||
<ul class="ftr__list ftr__list--plain">
|
|
||||||
{nav.map((n) => <li><a href={n.href}>{n.label}</a></li>)}
|
|
||||||
<li><a href="/anfrage">Anfrage stellen</a></li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ft__col">
|
||||||
<div class="wrap ftr__base">
|
<h2>Bleiben wir in <span class="paren">(Kontakt)</span></h2>
|
||||||
<p>© {year} {salon.legalName}</p>
|
<div class="ft__social">
|
||||||
<p class="ftr__legal"><a href="/impressum">Impressum</a><a href="/datenschutz">Datenschutz</a></p>
|
<a href={salon.phoneHref}>{salon.phoneDisplay}</a>
|
||||||
<p class="ftr__demo">Vorschau der neuen Website. Arbeitsfotos, Preise und Rechtstexte werden vor dem Livegang mit dem Salon abgestimmt.</p>
|
<a href={salon.whatsappHref} rel="noopener">WhatsApp</a>
|
||||||
|
<a href={salon.instagram} rel="noopener">Instagram</a>
|
||||||
|
<a href={salon.facebook} rel="noopener">Facebook</a>
|
||||||
|
</div>
|
||||||
|
<a class="ft__mail" href={`mailto:${salon.email}`}>{salon.email}<span>schreiben ⟶</span></a>
|
||||||
|
</div>
|
||||||
|
<div class="ft__base">
|
||||||
|
<span>© {year} {salon.legalName}</span>
|
||||||
|
<span><a href="/impressum">Impressum</a> · <a href="/datenschutz">Datenschutz</a></span>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
{PREVIEW && <p class="preview">Vorschau von Heidrich Digital. Preise, Wartezeiten und einzelne Angaben sind Beispiele und werden mit dem Salon abgestimmt.</p>}
|
||||||
<style>
|
|
||||||
.ftr {
|
|
||||||
background: var(--c-anthrazit);
|
|
||||||
color: oklch(92% 0.006 220);
|
|
||||||
padding-block: var(--s-9) var(--s-6);
|
|
||||||
font-size: var(--fs-small);
|
|
||||||
}
|
|
||||||
.ftr a { color: var(--c-papier); text-decoration: none; }
|
|
||||||
.ftr a:hover { color: var(--c-nebel); text-decoration: underline; }
|
|
||||||
.ftr__grid { display: grid; gap: var(--s-7); grid-template-columns: 1fr; }
|
|
||||||
@media (min-width: 40rem) { .ftr__grid { grid-template-columns: 1fr 1fr; } }
|
|
||||||
@media (min-width: 68rem) { .ftr__grid { grid-template-columns: 1.4fr 1fr 1fr 0.9fr; } }
|
|
||||||
.ftr__logo {
|
|
||||||
display: inline-block; background: var(--c-papier);
|
|
||||||
padding: 0.6rem 0.9rem; border-radius: var(--r-m);
|
|
||||||
}
|
|
||||||
.ftr__logo :global(.logo) { width: 104px; }
|
|
||||||
.ftr__claim {
|
|
||||||
font-family: var(--font-display); font-style: italic;
|
|
||||||
font-size: var(--fs-h4); line-height: 1.3; color: var(--c-papier);
|
|
||||||
margin-top: var(--s-5); max-width: 18ch;
|
|
||||||
}
|
|
||||||
.ftr__addr { margin-top: var(--s-4); }
|
|
||||||
.ftr__h { color: var(--c-hellblau); margin-bottom: var(--s-4); }
|
|
||||||
/* Hellblau auf Anthrazit nur für Labels in Großbuchstaben ab 13px fett: Kontrast 3,7:1 reicht nicht für AA, daher aufgehellt */
|
|
||||||
.ftr__h { color: oklch(80% 0.06 225); }
|
|
||||||
.ftr__list { list-style: none; padding: 0; display: grid; gap: 0.15rem; }
|
|
||||||
.ftr__list a { display: inline-flex; align-items: center; gap: 0.6em; min-height: var(--tap); }
|
|
||||||
.ftr__list svg { width: 1.15em; height: 1.15em; color: oklch(80% 0.06 225); }
|
|
||||||
.ftr__list--plain a { min-height: 2.25rem; }
|
|
||||||
.ftr__note { margin-top: var(--s-3); color: oklch(80% 0.008 220); }
|
|
||||||
.ftr__base {
|
|
||||||
margin-top: var(--s-8); padding-top: var(--s-5);
|
|
||||||
border-top: 1px solid oklch(100% 0 0 / 0.14);
|
|
||||||
display: flex; flex-wrap: wrap; gap: var(--s-3) var(--s-6); align-items: center;
|
|
||||||
color: oklch(82% 0.008 220);
|
|
||||||
}
|
|
||||||
.ftr__legal { display: flex; gap: var(--s-5); }
|
|
||||||
.ftr__legal a { min-height: var(--tap); display: inline-flex; align-items: center; text-decoration: underline; text-underline-offset: 0.2em; }
|
|
||||||
.ftr__demo { flex-basis: 100%; font-size: var(--fs-label); color: oklch(78% 0.008 220); max-width: 60ch; }
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
/**
|
||||||
|
* Überschrift mit Schreibschrift-Initiale („Raster“-Signatur).
|
||||||
|
* Screenreader lesen den ganzen Text, die Initiale ist nur Gestaltung.
|
||||||
|
* Sparsam einsetzen: höchstens eine Initiale pro Bildschirm.
|
||||||
|
*/
|
||||||
|
interface Props { as?: 'h1' | 'h2' | 'h3'; text: string; class?: string; rv?: boolean; plain?: boolean }
|
||||||
|
const { as: Tag = 'h2', text, class: cls = 'h2', rv = true, plain = false } = Astro.props;
|
||||||
|
const first = text.charAt(0);
|
||||||
|
const rest = text.slice(1);
|
||||||
|
---
|
||||||
|
<Tag class={cls} data-rv={rv ? '' : undefined}>
|
||||||
|
{plain ? text : <><span class="sr">{text}</span><span aria-hidden="true"><span class="ini">{first}</span>{rest}<slot /></span></>}
|
||||||
|
</Tag>
|
||||||
@@ -1,141 +1,39 @@
|
|||||||
---
|
---
|
||||||
import Logo from './Logo.astro';
|
import { Image } from 'astro:assets';
|
||||||
import Icon from './Icon.astro';
|
import logo from '../assets/logo/logo.png';
|
||||||
import { nav, salon } from '../data/site';
|
import { nav, navMore, salon } from '../data/site';
|
||||||
interface Props { current: string }
|
interface Props { current: string }
|
||||||
const { current } = Astro.props;
|
const { current } = Astro.props;
|
||||||
const isCurrent = (href: string) => current === href || current.startsWith(href + '/');
|
const isCur = (href: string) => (current === href || current.startsWith(href + '/') ? 'page' : undefined);
|
||||||
---
|
---
|
||||||
<header class="hdr">
|
<header class="hd">
|
||||||
<div class="wrap hdr__in">
|
<a class="hd__logo" href="/" aria-label="Friseur-Team VK, zur Startseite">
|
||||||
<a href="/" class="hdr__logo" aria-label="Friseur-Team VK, zur Startseite">
|
<Image src={logo} height={44} densities={[1, 2]} format="webp" alt="Friseur-Team VK" loading="eager" />
|
||||||
<Logo eager width={104} />
|
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<nav class="hdr__nav" aria-label="Hauptnavigation">
|
<div class="hd__right hd__desk">
|
||||||
<ul>
|
<nav class="hd__nav" aria-label="Hauptmenü">
|
||||||
{nav.map((n) => (
|
<ul role="list">
|
||||||
<li><a href={n.href} aria-current={isCurrent(n.href) ? 'page' : undefined}>{n.href === '/leistungen-preise' ? 'Preise' : n.label}</a></li>
|
{nav.map((n) => <li><a href={n.href} aria-current={isCur(n.href)}>{n.label}</a></li>)}
|
||||||
))}
|
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
<a class="oval oval--sm hd__cta" href="/anfrage">Anfragen</a>
|
||||||
<div class="hdr__cta">
|
|
||||||
<a class="btn btn--ghost hdr__tel" href={salon.phoneHref}><Icon name="phone" />{salon.phoneDisplay}</a>
|
|
||||||
<a class="btn" href="/anfrage">Anfragen</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<details class="mnav">
|
<details class="menu hd__menu">
|
||||||
<summary class="mnav__btn">
|
<summary aria-label="Menü öffnen"><span>Menü</span><span class="bars" aria-hidden="true"></span></summary>
|
||||||
<span class="mnav__open"><Icon name="menu" /> Menü</span>
|
<div class="menu__panel">
|
||||||
<span class="mnav__close"><Icon name="close" /> Schließen</span>
|
<nav aria-label="Hauptmenü mobil">
|
||||||
</summary>
|
<ul role="list">
|
||||||
<nav class="mnav__panel" aria-label="Hauptnavigation mobil">
|
{[...nav, ...navMore].map((n, i) => (
|
||||||
<ul>
|
<li><a href={n.href} aria-current={isCur(n.href)}>{n.label}<span class="cap">{String(i + 1).padStart(3, '0')}</span></a></li>
|
||||||
<li><a href="/" aria-current={current === '/' ? 'page' : undefined}>Start</a></li>
|
|
||||||
{nav.map((n) => (
|
|
||||||
<li><a href={n.href} aria-current={isCurrent(n.href) ? 'page' : undefined}>{n.label}</a></li>
|
|
||||||
))}
|
))}
|
||||||
<li><a href="/anfrage" aria-current={isCurrent('/anfrage') ? 'page' : undefined}>Anfrage stellen</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p class="mnav__meta">
|
|
||||||
<a href={salon.phoneHref}>{salon.phoneDisplay}</a><br />
|
|
||||||
{salon.street}, {salon.zip} {salon.city}
|
|
||||||
</p>
|
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="row">
|
||||||
|
<a class="oval oval--fill" href="/anfrage">Anfragen</a>
|
||||||
|
<a class="oval" href={salon.phoneHref}>{salon.phoneDisplay}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<script>
|
|
||||||
// Menü mit Escape schließen; funktioniert auch ohne dieses Skript.
|
|
||||||
const d = document.querySelector<HTMLDetailsElement>('.mnav');
|
|
||||||
if (d) {
|
|
||||||
document.addEventListener('keydown', (e) => {
|
|
||||||
if (e.key === 'Escape' && d.open) {
|
|
||||||
d.open = false;
|
|
||||||
d.querySelector('summary')?.focus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.hdr {
|
|
||||||
position: relative;
|
|
||||||
z-index: 40;
|
|
||||||
background: var(--c-papier);
|
|
||||||
border-bottom: var(--hair);
|
|
||||||
}
|
|
||||||
.hdr__in {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--s-5);
|
|
||||||
min-height: 4.5rem;
|
|
||||||
padding-block: var(--s-2);
|
|
||||||
}
|
|
||||||
.hdr__logo { display: block; flex: none; }
|
|
||||||
.hdr__logo :global(.logo) { width: 88px; height: auto; }
|
|
||||||
.hdr__nav, .hdr__cta { display: none; }
|
|
||||||
|
|
||||||
/* Mobil: Menü als <details>, funktioniert ohne JavaScript */
|
|
||||||
.mnav { margin-left: auto; }
|
|
||||||
.mnav__btn {
|
|
||||||
list-style: none;
|
|
||||||
display: inline-flex; align-items: center; gap: 0.5em;
|
|
||||||
min-height: var(--tap); padding: 0 var(--s-4);
|
|
||||||
border: 1.5px solid var(--c-silber); border-radius: var(--r-pill);
|
|
||||||
font-weight: 600; font-stretch: 85%; cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
.mnav__btn::-webkit-details-marker { display: none; }
|
|
||||||
.mnav__btn span { display: inline-flex; align-items: center; gap: 0.45em; }
|
|
||||||
.mnav__btn svg { width: 1.2em; height: 1.2em; }
|
|
||||||
.mnav__close { display: none !important; }
|
|
||||||
.mnav[open] .mnav__open { display: none !important; }
|
|
||||||
.mnav[open] .mnav__close { display: inline-flex !important; }
|
|
||||||
.mnav__panel {
|
|
||||||
position: absolute; left: 0; right: 0; top: 100%;
|
|
||||||
background: var(--c-papier);
|
|
||||||
border-bottom: var(--hair);
|
|
||||||
padding: var(--s-3) var(--gutter) var(--s-6);
|
|
||||||
box-shadow: 0 24px 40px -24px oklch(30% 0.02 230 / 0.25);
|
|
||||||
}
|
|
||||||
.mnav__panel ul { list-style: none; padding: 0; }
|
|
||||||
.mnav__panel li { border-bottom: var(--hair); }
|
|
||||||
.mnav__panel a {
|
|
||||||
display: flex; align-items: center; min-height: 3.25rem;
|
|
||||||
font-family: var(--font-display); font-size: 1.5rem; line-height: 1.2;
|
|
||||||
color: var(--c-anthrazit); text-decoration: none;
|
|
||||||
}
|
|
||||||
.mnav__panel a[aria-current='page'] { color: var(--c-petrol); font-style: italic; }
|
|
||||||
.mnav__meta { margin-top: var(--s-5); font-size: var(--fs-small); color: var(--c-text-leise); }
|
|
||||||
.mnav__meta a { font-family: var(--font-text); font-size: inherit; display: inline; min-height: 0; color: var(--c-petrol); }
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.mnav[open] .mnav__panel { animation: drop var(--t-med) var(--ease-out); }
|
|
||||||
@keyframes drop { from { opacity: 0; transform: translateY(-8px); } }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 72rem) {
|
|
||||||
.hdr { position: sticky; top: 0; }
|
|
||||||
.hdr__logo :global(.logo) { width: 104px; }
|
|
||||||
.mnav { display: none; }
|
|
||||||
.hdr__nav { display: block; margin-left: auto; }
|
|
||||||
.hdr__nav ul { list-style: none; padding: 0; display: flex; gap: var(--s-1); }
|
|
||||||
.hdr__nav a {
|
|
||||||
display: inline-flex; align-items: center; min-height: var(--tap);
|
|
||||||
padding: 0 0.6rem;
|
|
||||||
font-size: 0.9375rem; font-weight: 500; font-stretch: 88%;
|
|
||||||
color: var(--c-anthrazit); text-decoration: none;
|
|
||||||
background-image: linear-gradient(var(--c-petrol), var(--c-petrol));
|
|
||||||
background-size: 0 1.5px; background-repeat: no-repeat; background-position: 0.6rem calc(100% - 0.55rem);
|
|
||||||
transition: background-size var(--t-med) var(--ease-out), color var(--t-fast);
|
|
||||||
}
|
|
||||||
.hdr__nav a:hover, .hdr__nav a[aria-current='page'] { color: var(--c-petrol); background-size: calc(100% - 1.2rem) 1.5px; }
|
|
||||||
.hdr__cta { display: flex; gap: var(--s-2); }
|
|
||||||
.hdr__cta .btn { font-size: 0.9375rem; padding-inline: 1.1em; }
|
|
||||||
}
|
|
||||||
@media (min-width: 72rem) and (max-width: 82rem) {
|
|
||||||
.hdr__tel { display: none; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
import { hours, fmtTime } from '../data/site';
|
|
||||||
interface Props { compact?: boolean }
|
|
||||||
const { compact = false } = Astro.props;
|
|
||||||
const shown = hours.filter((h) => h.schema !== 'Sunday');
|
|
||||||
---
|
|
||||||
<table class:list={['hours', { 'hours--compact': compact }]}>
|
|
||||||
<caption class="visually-hidden">Öffnungszeiten</caption>
|
|
||||||
<tbody>
|
|
||||||
{shown.map((h) => (
|
|
||||||
<tr>
|
|
||||||
<th scope="row">{compact ? h.short : h.day}</th>
|
|
||||||
<td class="num">{h.open ? `${fmtTime(h.open)}–${fmtTime(h.close!)} Uhr` : 'geschlossen'}</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<style>
|
|
||||||
.hours { border-collapse: collapse; width: 100%; max-width: 22rem; }
|
|
||||||
.hours th, .hours td { text-align: left; padding: 0.55rem 0; border-bottom: var(--hair); font-weight: 400; }
|
|
||||||
.hours th { padding-right: var(--s-5); }
|
|
||||||
.hours td { text-align: right; }
|
|
||||||
.hours--compact th, .hours--compact td { padding: 0.3rem 0; border-color: oklch(100% 0 0 / 0.14); }
|
|
||||||
</style>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
import { Image } from 'astro:assets';
|
|
||||||
import logo from '../assets/logo/logo.png';
|
|
||||||
interface Props { width?: number; eager?: boolean }
|
|
||||||
const { width = 112, eager = false } = Astro.props;
|
|
||||||
---
|
|
||||||
<Image src={logo} width={width} densities={[1, 2]} format="webp" alt="Friseur-Team VK, Tostedt" loading={eager ? 'eager' : 'lazy'} class="logo" />
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
---
|
|
||||||
/** Illustration der digitalen Stempelkarte (Muster, keine echten Daten). */
|
|
||||||
import Logo from './Logo.astro';
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
interface Props { stamps?: number }
|
|
||||||
const { stamps = 4 } = Astro.props;
|
|
||||||
// Deterministisches Muster im Stil eines QR-Codes (Illustration, nicht scannbar)
|
|
||||||
const N = 25;
|
|
||||||
let seed = 7;
|
|
||||||
const rnd = () => ((seed = (seed * 9301 + 49297) % 233280) / 233280);
|
|
||||||
const cells: [number, number][] = [];
|
|
||||||
const finder = (x: number, y: number) => (x < 8 && y < 8) || (x > N - 9 && y < 8) || (x < 8 && y > N - 9);
|
|
||||||
for (let y = 0; y < N; y++) for (let x = 0; x < N; x++) if (!finder(x, y) && rnd() > 0.52) cells.push([x, y]);
|
|
||||||
const fp = (x: number, y: number) => `M${x} ${y}h7v7h-7Z M${x + 1} ${y + 1}v5h5v-5Z M${x + 2} ${y + 2}h3v3h-3Z`;
|
|
||||||
---
|
|
||||||
<div class="lcard" role="img" aria-label={`Muster der digitalen Kundenkarte: ${stamps} von 6 Stempeln gesammelt, beim sechsten gibt es ein Pflege-Upgrade gratis.`}>
|
|
||||||
<div class="lcard__top" aria-hidden="true">
|
|
||||||
<div class="lcard__logo"><Logo width={84} /></div>
|
|
||||||
<div class="lcard__count"><span class="lcard__k">Stempel</span><span class="lcard__v">{stamps} / 6</span></div>
|
|
||||||
</div>
|
|
||||||
<ol class="lcard__stamps" aria-hidden="true">
|
|
||||||
{Array.from({ length: 6 }).map((_, i) => (
|
|
||||||
<li class:list={['lcard__stamp', { 'is-on': i < stamps, 'is-gift': i === 5 }]}>
|
|
||||||
{i === 5 ? <span>Pflege</span> : <Icon name="scissors" />}
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ol>
|
|
||||||
<div class="lcard__fields" aria-hidden="true">
|
|
||||||
<div><span class="lcard__k">Prämie</span><span class="lcard__v2">Pflege-Upgrade gratis</span></div>
|
|
||||||
<div><span class="lcard__k">Noch</span><span class="lcard__v2">{6 - stamps} Besuche</span></div>
|
|
||||||
</div>
|
|
||||||
<div class="lcard__qr" aria-hidden="true">
|
|
||||||
<svg viewBox={`-2 -2 ${N + 4} ${N + 4}`} width="120" height="120" shape-rendering="crispEdges">
|
|
||||||
<rect x="-2" y="-2" width={N + 4} height={N + 4} fill="#fff" />
|
|
||||||
<path fill="currentColor" fill-rule="evenodd" d={`${fp(0, 0)} ${fp(N - 7, 0)} ${fp(0, N - 7)}`} />
|
|
||||||
<path fill="currentColor" d={cells.map(([x, y]) => `M${x} ${y}h1v1h-1Z`).join('')} />
|
|
||||||
</svg>
|
|
||||||
<span class="lcard__hint">Muster · an der Kasse scannen lassen</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
.lcard {
|
|
||||||
width: min(100%, 21rem);
|
|
||||||
background: var(--c-papier);
|
|
||||||
border-radius: 1.1rem;
|
|
||||||
padding: 1.1rem 1.1rem 1.25rem;
|
|
||||||
box-shadow: 0 1px 0 oklch(100% 0 0 / 0.6) inset, 0 30px 60px -30px oklch(25% 0.05 235 / 0.45), 0 2px 6px oklch(25% 0.03 235 / 0.12);
|
|
||||||
color: var(--c-anthrazit);
|
|
||||||
font-size: 0.875rem;
|
|
||||||
}
|
|
||||||
.lcard__top { display: flex; justify-content: space-between; align-items: center; }
|
|
||||||
.lcard__logo :global(.logo) { width: 76px; }
|
|
||||||
.lcard__count { text-align: right; display: grid; }
|
|
||||||
.lcard__k { font-size: 0.75rem; font-weight: 600; font-stretch: 72%; text-transform: uppercase; letter-spacing: 0.12em; color: var(--c-text-leise); }
|
|
||||||
.lcard__v { font-family: var(--font-display); font-size: 1.5rem; line-height: 1.1; }
|
|
||||||
.lcard__stamps {
|
|
||||||
list-style: none; padding: 0.9rem; margin-top: 0.9rem;
|
|
||||||
display: grid; grid-template-columns: repeat(6, 1fr); gap: 0.4rem;
|
|
||||||
background: var(--c-petrol-hell); border-radius: 0.7rem;
|
|
||||||
}
|
|
||||||
.lcard__stamp {
|
|
||||||
aspect-ratio: 1; border-radius: 50%;
|
|
||||||
display: grid; place-items: center;
|
|
||||||
border: 1.5px dashed var(--c-silber); color: var(--c-silber);
|
|
||||||
}
|
|
||||||
.lcard__stamp :global(svg) { width: 58%; height: 58%; }
|
|
||||||
.lcard__stamp.is-on { background: var(--c-petrol); border: 0; color: var(--c-papier); }
|
|
||||||
.lcard__stamp.is-gift { border-style: solid; border-color: var(--c-petrol); color: var(--c-petrol); font-size: 0.75rem; font-weight: 700; font-stretch: 70%; text-transform: uppercase; letter-spacing: 0.02em; }
|
|
||||||
.lcard__fields { display: flex; justify-content: space-between; gap: 1rem; margin-top: 0.9rem; }
|
|
||||||
.lcard__fields > div { display: grid; }
|
|
||||||
.lcard__fields > div:last-child { text-align: right; }
|
|
||||||
.lcard__v2 { font-weight: 600; }
|
|
||||||
.lcard__qr { display: grid; justify-items: center; gap: 0.5rem; margin-top: 1rem; color: var(--c-anthrazit); }
|
|
||||||
.lcard__qr svg { width: 7.5rem; height: 7.5rem; }
|
|
||||||
.lcard__hint { font-size: 0.75rem; color: var(--c-text-leise); }
|
|
||||||
</style>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
---
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
import { salon } from '../data/site';
|
|
||||||
interface Props { variant?: 'default' | 'kontakt' }
|
|
||||||
const { variant = 'default' } = Astro.props;
|
|
||||||
---
|
|
||||||
<div class="mbar" role="region" aria-label="Schnellkontakt">
|
|
||||||
<a class="btn btn--ghost" href={salon.phoneHref}><Icon name="phone" />Anrufen</a>
|
|
||||||
{variant === 'kontakt'
|
|
||||||
? <a class="btn" href={salon.whatsappHref} rel="noopener"><Icon name="whatsapp" />WhatsApp</a>
|
|
||||||
: <a class="btn" href="/anfrage">Anfragen<Icon name="arrow" /></a>}
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
.mbar {
|
|
||||||
position: fixed; z-index: 50; left: 0; right: 0; bottom: 0;
|
|
||||||
display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2);
|
|
||||||
padding: var(--s-2) var(--s-3) calc(var(--s-2) + env(safe-area-inset-bottom));
|
|
||||||
background: oklch(98.7% 0.003 220 / 0.97);
|
|
||||||
border-top: var(--hair);
|
|
||||||
}
|
|
||||||
.mbar .btn { width: 100%; min-height: 3rem; }
|
|
||||||
.mbar .btn--ghost { background: var(--c-papier); }
|
|
||||||
:global(body) { padding-bottom: calc(var(--bar-h) + env(safe-area-inset-bottom)); }
|
|
||||||
@media (min-width: 72rem) {
|
|
||||||
.mbar { display: none; }
|
|
||||||
:global(body) { padding-bottom: 0; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
/** Seitenkopf der Unterseiten: Brotkrume, große Überschrift mit Initiale, Einleitung in Spalte C. */
|
||||||
|
interface Props { crumb: string; title: string; sub?: string; size?: 'big' | 'mid' }
|
||||||
|
const { crumb, title, sub, size = 'big' } = Astro.props;
|
||||||
|
const first = title.charAt(0);
|
||||||
|
const rest = title.slice(1);
|
||||||
|
---
|
||||||
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
|
<div class="pad">
|
||||||
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ {crumb}</span></p>
|
||||||
|
<h1 id="ph-h" class:list={['ph__title', { 'is-mid': size === 'mid' }]}>
|
||||||
|
<span class="sr">{title}{sub ? ` ${sub}` : ''}</span>
|
||||||
|
<span aria-hidden="true"><span class="ini">{first}</span>{rest}{sub && <><br /><span class="it">{sub}</span></>}</span>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div class="r3 ph__grid">
|
||||||
|
<div class="a"><slot name="a" /></div>
|
||||||
|
<div class="b gap-5"><slot name="b" /></div>
|
||||||
|
<div class="c gap-5"><slot /></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
/** Kopf für Unterseiten: Text links, Bild oder Platzhalter rechts mit Wandpaneel. */
|
|
||||||
interface Props { label: string; title: string; lead?: string }
|
|
||||||
const { label, title, lead } = Astro.props;
|
|
||||||
---
|
|
||||||
<section class="phero">
|
|
||||||
<div class="wrap phero__grid">
|
|
||||||
<div class="phero__txt">
|
|
||||||
<p class="label phero__label">{label}</p>
|
|
||||||
<h1 set:html={title} />
|
|
||||||
{lead && <p class="lead phero__lead">{lead}</p>}
|
|
||||||
<div class="phero__actions"><slot name="actions" /></div>
|
|
||||||
</div>
|
|
||||||
<div class="phero__media">
|
|
||||||
<div class="phero__panel" aria-hidden="true"></div>
|
|
||||||
<div class="phero__img"><slot name="media" /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<style>
|
|
||||||
.phero { padding-block: var(--s-7) var(--section); overflow: hidden; }
|
|
||||||
.phero__grid { display: grid; gap: var(--s-7); align-items: end; }
|
|
||||||
@media (min-width: 60rem) { .phero__grid { grid-template-columns: 1.15fr 0.85fr; gap: var(--s-8); padding-top: var(--s-6); } }
|
|
||||||
.phero__label { margin-bottom: var(--s-5); }
|
|
||||||
.phero__lead { margin-top: var(--s-5); }
|
|
||||||
.phero__actions:not(:empty) { margin-top: var(--s-6); }
|
|
||||||
.phero__media { position: relative; padding: 0 0 var(--s-5) var(--s-5); }
|
|
||||||
.phero__panel {
|
|
||||||
position: absolute; left: 0; bottom: 0; top: 18%; width: 46%;
|
|
||||||
background: var(--c-petrol);
|
|
||||||
}
|
|
||||||
.phero__img { position: relative; }
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.phero__txt > * { animation: rise var(--t-slow) var(--ease-out) both; }
|
|
||||||
.phero__txt > :nth-child(2) { animation-delay: 80ms; }
|
|
||||||
.phero__txt > :nth-child(3) { animation-delay: 160ms; }
|
|
||||||
.phero__txt > :nth-child(4) { animation-delay: 240ms; }
|
|
||||||
.phero__panel { animation: grow 1.1s var(--ease-out) both 120ms; transform-origin: bottom; }
|
|
||||||
/* Überschrift nur bewegen, nicht ausblenden: zählt sofort als LCP */
|
|
||||||
.phero__txt > h1 { animation-name: rise-t; }
|
|
||||||
@keyframes rise { from { opacity: 0; transform: translateY(18px); } }
|
|
||||||
@keyframes rise-t { from { transform: translateY(14px); } }
|
|
||||||
@keyframes grow { from { transform: scaleY(0); } }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -8,9 +8,9 @@ interface Props {
|
|||||||
widths?: number[];
|
widths?: number[];
|
||||||
eager?: boolean;
|
eager?: boolean;
|
||||||
class?: string;
|
class?: string;
|
||||||
position?: string;
|
pos?: 'top' | 'left' | 'bottom';
|
||||||
}
|
}
|
||||||
const { src, alt, sizes, widths = [400, 640, 900, 1280, 1700], eager = false, class: cls } = Astro.props;
|
const { src, alt, sizes, widths = [480, 720, 1000, 1400, 1900], eager = false, class: cls, pos } = Astro.props;
|
||||||
const w = widths.filter((x) => x <= src.width);
|
const w = widths.filter((x) => x <= src.width);
|
||||||
if (!w.includes(src.width) && src.width < Math.max(...widths)) w.push(src.width);
|
if (!w.includes(src.width) && src.width < Math.max(...widths)) w.push(src.width);
|
||||||
---
|
---
|
||||||
@@ -24,6 +24,7 @@ if (!w.includes(src.width) && src.width < Math.max(...widths)) w.push(src.width)
|
|||||||
loading={eager ? 'eager' : 'lazy'}
|
loading={eager ? 'eager' : 'lazy'}
|
||||||
fetchpriority={eager ? 'high' : 'auto'}
|
fetchpriority={eager ? 'high' : 'auto'}
|
||||||
decoding={eager ? 'sync' : 'async'}
|
decoding={eager ? 'sync' : 'async'}
|
||||||
quality={eager ? 62 : 58}
|
quality={eager ? 64 : 60}
|
||||||
|
pictureAttributes={{ class: pos ? `pos-${pos}` : undefined }}
|
||||||
class={cls}
|
class={cls}
|
||||||
/>
|
/>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
import { eur, type PriceGroup } from '../data/site';
|
||||||
|
interface Props { group: PriceGroup; n?: string; showLink?: boolean }
|
||||||
|
const { group, n, showLink = false } = Astro.props;
|
||||||
|
const hasTiers = group.items.some((i) => i.tiers);
|
||||||
|
---
|
||||||
|
<div class="pgroup" id={group.id} data-rv>
|
||||||
|
<span class="cap">{n}</span>
|
||||||
|
<div class="pgroup__head">
|
||||||
|
<h2 class="pgroup__h">{group.title}</h2>
|
||||||
|
{group.lead && <p class="pgroup__lead">{group.lead}</p>}
|
||||||
|
{showLink && group.href && <a class="go" href={group.href}>Mehr zu {group.title}</a>}
|
||||||
|
</div>
|
||||||
|
<ul class="pitems" role="list">
|
||||||
|
{hasTiers && (
|
||||||
|
<li class="pitem" aria-hidden="true"><span></span><span class="ptiers cap"><span>kurz</span><span>mittel</span><span>lang</span></span></li>
|
||||||
|
)}
|
||||||
|
{group.items.map((i) => (
|
||||||
|
<li class="pitem">
|
||||||
|
<span class="pitem__l">{i.label}{i.note && <small>{i.note}</small>}</span>
|
||||||
|
<span class="pitem__p">
|
||||||
|
{i.tiers ? (
|
||||||
|
<span class="ptiers">{i.tiers.map((t, k) => <span><span class="sr">{['kurz', 'mittel', 'lang'][k]}: ab </span>{eur(t)}</span>)}</span>
|
||||||
|
) : i.text ? (
|
||||||
|
<span class="price">{i.text}</span>
|
||||||
|
) : i.from != null ? (
|
||||||
|
<span>ab <span class="price">{eur(i.from)}</span></span>
|
||||||
|
) : (
|
||||||
|
<span>nach Absprache</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
---
|
|
||||||
import { priceGroups, eur } from '../data/site';
|
|
||||||
interface Props { ids?: string[]; compact?: boolean; headingLevel?: 2 | 3 }
|
|
||||||
const { ids, compact = false, headingLevel = 2 } = Astro.props;
|
|
||||||
const groups = ids ? priceGroups.filter((g) => ids.includes(g.id)) : priceGroups;
|
|
||||||
const H = headingLevel === 2 ? 'h2' : 'h3';
|
|
||||||
---
|
|
||||||
<div class:list={['plist', { 'plist--compact': compact }]}>
|
|
||||||
{groups.map((g) => (
|
|
||||||
<section class="plist__group" id={`preise-${g.id}`} aria-labelledby={`pg-${g.id}`}>
|
|
||||||
<H class="plist__h" id={`pg-${g.id}`}>{g.title}</H>
|
|
||||||
{g.lead && !compact && <p class="plist__lead leise">{g.lead}</p>}
|
|
||||||
<dl class="plist__dl">
|
|
||||||
{g.items.map((i) => (
|
|
||||||
<div class="plist__row">
|
|
||||||
<dt>{i.label}{i.note && <span class="plist__note"> · {i.note}</span>}</dt>
|
|
||||||
<dd class="num">{i.from !== null ? <>ab {eur(i.from)}<sup aria-label="Richtwert, siehe Hinweis">*</sup></> : 'nach Absprache'}</dd>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</dl>
|
|
||||||
</section>
|
|
||||||
))}
|
|
||||||
<p class="plist__foot small leise">* Richtwerte. Preise werden mit dem Salon abgestimmt. Der genaue Preis hängt von Haarlänge, Haardichte und Aufwand ab; wir nennen ihn Ihnen vor der Behandlung.</p>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
.plist { display: grid; gap: var(--s-7); }
|
|
||||||
.plist__h { font-size: var(--fs-h3); }
|
|
||||||
.plist--compact .plist__h { font-size: var(--fs-h4); font-family: var(--font-text); font-weight: 600; }
|
|
||||||
.plist__lead { margin-top: var(--s-2); }
|
|
||||||
.plist__dl { margin-top: var(--s-4); border-top: 1.5px solid var(--c-anthrazit); }
|
|
||||||
.plist__row {
|
|
||||||
display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-4);
|
|
||||||
padding-block: 0.8rem; border-bottom: var(--hair);
|
|
||||||
}
|
|
||||||
.plist__row dt { flex: 1; }
|
|
||||||
.plist__row dd { flex: none; font-weight: 600; white-space: nowrap; }
|
|
||||||
.plist__note { color: var(--c-text-leise); font-size: var(--fs-small); }
|
|
||||||
sup { font-size: 0.75em; color: var(--c-text-leise); margin-left: 1px; }
|
|
||||||
.plist__foot { max-width: 60ch; }
|
|
||||||
</style>
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
---
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
import { salon } from '../data/site';
|
|
||||||
interface Props { variant?: 'chip' | 'block' }
|
|
||||||
const { variant = 'chip' } = Astro.props;
|
|
||||||
const g = salon.google;
|
|
||||||
---
|
|
||||||
{variant === 'chip' ? (
|
|
||||||
<a class="rchip" href={g.url} rel="noopener">
|
|
||||||
<span class="rchip__stars" aria-hidden="true">{[1,2,3,4,5].map(() => <Icon name="star" />)}</span>
|
|
||||||
<span><strong>{g.rating}</strong> bei {g.count} Google-Bewertungen</span>
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
<div class="rblock">
|
|
||||||
<p class="rblock__num display" aria-hidden="true">{g.rating}</p>
|
|
||||||
<div class="rblock__txt">
|
|
||||||
<span class="rblock__stars" aria-hidden="true">{[1,2,3,4,5].map(() => <Icon name="star" />)}</span>
|
|
||||||
<p class="rblock__sent">{g.count} Menschen haben uns auf Google bewertet, im Schnitt mit {g.rating} von 5 Sternen.</p>
|
|
||||||
<p class="small leise">Stand {g.asOf}. Die einzelnen Bewertungen finden Sie bei Google.</p>
|
|
||||||
<a class="more" href={g.url} rel="noopener"><span>Bewertungen auf Google lesen</span><Icon name="external" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<style>
|
|
||||||
.rchip {
|
|
||||||
display: inline-flex; align-items: center; flex-wrap: wrap; gap: 0.25rem 0.6rem;
|
|
||||||
min-height: var(--tap);
|
|
||||||
color: var(--c-anthrazit); text-decoration: none; font-size: var(--fs-small);
|
|
||||||
}
|
|
||||||
.rchip:hover { color: var(--c-petrol); }
|
|
||||||
.rchip strong { font-weight: 700; }
|
|
||||||
.rchip > span:last-child { text-decoration: underline; text-decoration-color: var(--c-silber); text-underline-offset: 0.25em; }
|
|
||||||
.rchip__stars, .rblock__stars { display: inline-flex; gap: 2px; color: var(--c-petrol); }
|
|
||||||
.rchip__stars :global(svg) { width: 1rem; height: 1rem; }
|
|
||||||
.rblock { display: grid; gap: var(--s-5); align-items: end; }
|
|
||||||
@media (min-width: 48rem) { .rblock { grid-template-columns: auto 1fr; gap: var(--s-7); } }
|
|
||||||
.rblock__num {
|
|
||||||
font-size: var(--fs-mega); line-height: 0.82; letter-spacing: -0.04em;
|
|
||||||
color: var(--c-petrol);
|
|
||||||
}
|
|
||||||
.rblock__txt { max-width: 34rem; display: grid; gap: var(--s-3); justify-items: start; }
|
|
||||||
.rblock__stars :global(svg) { width: 1.4rem; height: 1.4rem; }
|
|
||||||
.rblock__sent { font-family: var(--font-display); font-size: var(--fs-h3); line-height: 1.25; }
|
|
||||||
</style>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
import Shot from './Shot.astro';
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
import { salon } from '../data/site';
|
|
||||||
interface Props { title?: string; items: { title: string; detail?: string }[] }
|
|
||||||
const { title = 'Kurz gezeigt', items } = Astro.props;
|
|
||||||
---
|
|
||||||
<section class="reels section--tight" aria-labelledby="reels-h">
|
|
||||||
<div class="wrap reels__head">
|
|
||||||
<div>
|
|
||||||
<p class="label">Reels · {salon.instagramHandle}</p>
|
|
||||||
<h2 id="reels-h" class="reels__h">{title}</h2>
|
|
||||||
</div>
|
|
||||||
<a class="more" href={salon.instagram} rel="noopener"><span>Auf Instagram ansehen</span><Icon name="external" /></a>
|
|
||||||
</div>
|
|
||||||
<div class="reels__track" tabindex="0" role="group" aria-label="Reels, seitlich wischen">
|
|
||||||
{items.map((it) => <Shot kind="Reel" ratio="9/16" title={it.title} detail={it.detail} class="reels__item" />)}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<style>
|
|
||||||
.reels__head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: end; gap: var(--s-4); }
|
|
||||||
.reels__h { font-size: var(--fs-h3); margin-top: var(--s-3); }
|
|
||||||
.reels__track {
|
|
||||||
margin-top: var(--s-6);
|
|
||||||
display: grid; grid-auto-flow: column;
|
|
||||||
grid-auto-columns: min(62vw, 15.5rem);
|
|
||||||
gap: var(--s-4);
|
|
||||||
overflow-x: auto; overscroll-behavior-x: contain;
|
|
||||||
scroll-snap-type: x mandatory;
|
|
||||||
padding-inline: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
|
|
||||||
scroll-padding-inline: max(var(--gutter), calc((100vw - var(--maxw)) / 2 + var(--gutter)));
|
|
||||||
padding-bottom: var(--s-4);
|
|
||||||
scrollbar-width: thin;
|
|
||||||
}
|
|
||||||
.reels__track :global(.reels__item) { scroll-snap-align: start; border-radius: var(--r-m); }
|
|
||||||
</style>
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
---
|
|
||||||
/**
|
|
||||||
* Platzhalter für ein Foto aus dem Shooting (= Shotliste).
|
|
||||||
* Bewusst sichtbar beschriftet, keine Stockfotos.
|
|
||||||
*/
|
|
||||||
import Icon from './Icon.astro';
|
|
||||||
interface Props {
|
|
||||||
title: string; // Motiv, z. B. "Braut-Hochsteckfrisur"
|
|
||||||
detail?: string; // Ausschnitt/Bildidee
|
|
||||||
ratio?: '4/5' | '3/4' | '3/2' | '1/1' | '9/16' | '16/9';
|
|
||||||
arch?: boolean;
|
|
||||||
tone?: 'nebel' | 'petrol';
|
|
||||||
kind?: 'Foto' | 'Reel';
|
|
||||||
class?: string;
|
|
||||||
}
|
|
||||||
const { title, detail, ratio = '4/5', arch = false, tone = 'nebel', kind = 'Foto', class: cls } = Astro.props;
|
|
||||||
const r = ratio.replace('/', '-');
|
|
||||||
---
|
|
||||||
<figure class:list={['shot', `shot--${r}`, `shot--${tone}`, { arch }, cls]} role="img" aria-label={`${kind} folgt: ${title}${detail ? ', ' + detail : ''}`}>
|
|
||||||
<div class="shot__in" aria-hidden="true">
|
|
||||||
<span class="shot__tag">{kind === 'Reel' ? <Icon name="play" /> : <Icon name="camera" />}{kind} folgt</span>
|
|
||||||
<span class="shot__title">{title}</span>
|
|
||||||
{detail && <span class="shot__detail">{detail}</span>}
|
|
||||||
</div>
|
|
||||||
</figure>
|
|
||||||
<style>
|
|
||||||
.shot {
|
|
||||||
position: relative; overflow: hidden; margin: 0;
|
|
||||||
background:
|
|
||||||
radial-gradient(120% 80% at 50% 0%, oklch(97% 0.006 220) 0%, transparent 70%),
|
|
||||||
var(--c-nebel-hell);
|
|
||||||
color: var(--c-anthrazit);
|
|
||||||
}
|
|
||||||
.shot--petrol {
|
|
||||||
background: radial-gradient(120% 80% at 50% 0%, oklch(56% 0.1 232) 0%, transparent 70%), var(--c-petrol);
|
|
||||||
color: var(--c-papier);
|
|
||||||
}
|
|
||||||
.shot--4-5 { aspect-ratio: 4 / 5; }
|
|
||||||
.shot--3-4 { aspect-ratio: 3 / 4; }
|
|
||||||
.shot--3-2 { aspect-ratio: 3 / 2; }
|
|
||||||
.shot--1-1 { aspect-ratio: 1; }
|
|
||||||
.shot--9-16 { aspect-ratio: 9 / 16; }
|
|
||||||
.shot--16-9 { aspect-ratio: 16 / 9; }
|
|
||||||
.shot__in {
|
|
||||||
position: absolute; inset: 0.75rem;
|
|
||||||
border: 1px solid oklch(50% 0.02 230 / 0.22);
|
|
||||||
display: flex; flex-direction: column; justify-content: flex-end; gap: 0.35rem;
|
|
||||||
padding: clamp(1rem, 0.6rem + 1.5vw, 1.75rem);
|
|
||||||
}
|
|
||||||
.shot--petrol .shot__in { border-color: oklch(100% 0 0 / 0.3); }
|
|
||||||
.arch .shot__in { border-radius: var(--arch); }
|
|
||||||
.shot__tag {
|
|
||||||
display: inline-flex; align-items: center; gap: 0.45em;
|
|
||||||
font-size: var(--fs-label); font-weight: 600; font-stretch: 72%;
|
|
||||||
text-transform: uppercase; letter-spacing: 0.12em;
|
|
||||||
color: var(--c-petrol);
|
|
||||||
}
|
|
||||||
.shot--petrol .shot__tag { color: var(--c-papier); }
|
|
||||||
.shot__tag :global(svg) { width: 1.15em; height: 1.15em; }
|
|
||||||
.shot__title {
|
|
||||||
font-family: var(--font-display); font-style: italic;
|
|
||||||
font-size: clamp(1.25rem, 1.05rem + 0.8vw, 1.75rem); line-height: 1.2;
|
|
||||||
}
|
|
||||||
.shot__detail { font-size: var(--fs-small); color: var(--c-text-leise); line-height: 1.4; }
|
|
||||||
.shot--petrol .shot__detail { color: oklch(93% 0.02 225); }
|
|
||||||
</style>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
---
|
|
||||||
interface Props { steps: { title: string; text: string }[]; label?: string }
|
|
||||||
const { steps } = Astro.props;
|
|
||||||
---
|
|
||||||
<ol class="steps">
|
|
||||||
{steps.map((s, i) => (
|
|
||||||
<li class="steps__item reveal">
|
|
||||||
<span class="steps__n display" aria-hidden="true">{String(i + 1).padStart(2, '0')}</span>
|
|
||||||
<h3 class="steps__t">{s.title}</h3>
|
|
||||||
<p class="steps__p">{s.text}</p>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ol>
|
|
||||||
<style>
|
|
||||||
.steps { list-style: none; padding: 0; display: grid; gap: var(--s-7); counter-reset: s; }
|
|
||||||
@media (min-width: 52rem) { .steps { grid-template-columns: repeat(3, 1fr); gap: var(--s-6); } }
|
|
||||||
.steps__item { border-top: 1.5px solid var(--c-anthrazit); padding-top: var(--s-4); }
|
|
||||||
.steps__n { display: block; font-size: var(--fs-h2); line-height: 1; color: var(--c-petrol); font-style: italic; }
|
|
||||||
.steps__t { font-size: var(--fs-h4); margin-top: var(--s-4); font-family: var(--font-text); font-weight: 600; }
|
|
||||||
.steps__p { margin-top: var(--s-3); color: var(--c-text); max-width: 32ch; }
|
|
||||||
</style>
|
|
||||||
@@ -1,48 +1,51 @@
|
|||||||
/**
|
/**
|
||||||
* Eine Quelle für alle Salon-Fakten.
|
* Eine Quelle für alle Salon-Fakten.
|
||||||
* Footer, Kontaktseite, Impressum und JSON-LD lesen von hier,
|
* Kopf, Fuß, Kontakt, Impressum, Preise und JSON-LD lesen von hier.
|
||||||
* damit Adresse, Telefon und Zeiten überall gleich sind.
|
* Alles mit „PRÜFEN“ vor Livegang mit dem Salon bestätigen.
|
||||||
* Mit "pruefen: true" markierte Angaben vor Livegang mit dem Salon bestätigen.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const SITE_URL = 'https://vk-friseur.heidrich-digital.de';
|
export const SITE_URL = 'https://vk-friseur.heidrich-digital.de';
|
||||||
|
export const PREVIEW = true; // Vorschau-Hinweis im Fuß; beim Livegang auf false
|
||||||
|
|
||||||
export const salon = {
|
export const salon = {
|
||||||
name: 'Friseur-Team VK',
|
name: 'Friseur-Team VK',
|
||||||
legalName: 'Friseur-Team VK Veronika Sprangel',
|
legalName: 'Friseur-Team VK Veronika Sprangel',
|
||||||
owner: 'Veronika Sprangel',
|
owner: 'Veronika Sprangel',
|
||||||
|
ownerShort: 'Vero',
|
||||||
ownerTitle: 'Friseurmeisterin',
|
ownerTitle: 'Friseurmeisterin',
|
||||||
since: 'November 2017',
|
since: 'November 2017',
|
||||||
|
sinceYear: 2017,
|
||||||
foundingDate: '2017-11',
|
foundingDate: '2017-11',
|
||||||
street: 'Kastanienallee 1',
|
street: 'Kastanienallee 1',
|
||||||
zip: '21255',
|
zip: '21255',
|
||||||
city: 'Tostedt',
|
city: 'Tostedt',
|
||||||
region: 'Niedersachsen',
|
region: 'Niedersachsen',
|
||||||
country: 'DE',
|
country: 'DE',
|
||||||
|
geo: { lat: 53.2817, lng: 9.7116 }, // PRÜFEN (Näherung)
|
||||||
phoneDisplay: '04182 2889535',
|
phoneDisplay: '04182 2889535',
|
||||||
phoneHref: 'tel:+4941822889535',
|
phoneHref: 'tel:+4941822889535',
|
||||||
phoneIntl: '+49 4182 2889535',
|
phoneIntl: '+49 4182 2889535',
|
||||||
// WhatsApp-Nummer vom Salon bestätigen lassen (pruefen)
|
whatsappHref: 'https://wa.me/4941822889535', // PRÜFEN: läuft WhatsApp über diese Nummer?
|
||||||
whatsappHref: 'https://wa.me/4941822889535',
|
|
||||||
email: 'info@friseur-team-vk.de',
|
email: 'info@friseur-team-vk.de',
|
||||||
instagram: 'https://www.instagram.com/friseur.team_vk/',
|
instagram: 'https://www.instagram.com/friseur.team_vk/',
|
||||||
instagramHandle: '@friseur.team_vk',
|
instagramHandle: '@friseur.team_vk',
|
||||||
facebook: 'https://www.facebook.com/FriseurTeamVK',
|
facebook: 'https://www.facebook.com/FriseurTeamVK',
|
||||||
|
greatLengths: 'https://www.greatlengthspartner.com/partner/friseur-team-vk-tostedt',
|
||||||
google: {
|
google: {
|
||||||
rating: '4,9',
|
rating: '4,9',
|
||||||
count: 193,
|
count: 194,
|
||||||
asOf: 'September 2026',
|
asOf: 'Oktober 2026',
|
||||||
url: 'https://www.google.com/maps/search/?api=1&query=FRISEUR-TEAM+VK&query_place_id=ChIJoaTlnmahsUcRFbgiyxAG0Fs',
|
url: 'https://www.google.com/maps/search/?api=1&query=FRISEUR-TEAM+VK&query_place_id=ChIJoaTlnmahsUcRFbgiyxAG0Fs',
|
||||||
},
|
},
|
||||||
routeUrl:
|
routeUrl: 'https://www.google.com/maps/dir/?api=1&destination=Kastanienallee+1,+21255+Tostedt',
|
||||||
'https://www.google.com/maps/dir/?api=1&destination=Kastanienallee+1,+21255+Tostedt',
|
|
||||||
appleMapsUrl: 'https://maps.apple.com/?daddr=Kastanienallee+1,+21255+Tostedt',
|
appleMapsUrl: 'https://maps.apple.com/?daddr=Kastanienallee+1,+21255+Tostedt',
|
||||||
chamber: 'Handwerkskammer Braunschweig-Lüneburg-Stade',
|
chamber: 'Handwerkskammer Braunschweig-Lüneburg-Stade',
|
||||||
chamberNo: '2250821757',
|
chamberNo: '2250821757',
|
||||||
brands: ['Redken', 'Paul Mitchell', 'Great Lengths'],
|
brands: ['Great Lengths', 'Paul Mitchell', 'Redken'],
|
||||||
|
teamSize: 8, // PRÜFEN: Gruppenfoto zeigt mehr Personen
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Öffnungszeiten (Stand März 2026, pruefen) */
|
/** Öffnungszeiten. PRÜFEN: Freitag laut Google bis 19 Uhr, laut alter Website bis 20 Uhr. */
|
||||||
export const hours = [
|
export const hours = [
|
||||||
{ day: 'Montag', short: 'Mo', schema: 'Monday', open: null, close: null },
|
{ day: 'Montag', short: 'Mo', schema: 'Monday', open: null, close: null },
|
||||||
{ day: 'Dienstag', short: 'Di', schema: 'Tuesday', open: '09:00', close: '18:00' },
|
{ day: 'Dienstag', short: 'Di', schema: 'Tuesday', open: '09:00', close: '18:00' },
|
||||||
@@ -57,88 +60,114 @@ export const fmtTime = (t: string) => {
|
|||||||
const [h, m] = t.split(':');
|
const [h, m] = t.split(':');
|
||||||
return m === '00' ? String(Number(h)) : `${Number(h)}.${m}`;
|
return m === '00' ? String(Number(h)) : `${Number(h)}.${m}`;
|
||||||
};
|
};
|
||||||
|
export const fmtHours = (h: (typeof hours)[number]) =>
|
||||||
|
h.open && h.close ? `${fmtTime(h.open)}–${fmtTime(h.close)} Uhr` : 'geschlossen';
|
||||||
|
|
||||||
/** Team (Stand März 2026, Namen und Rollen pruefen) */
|
/**
|
||||||
export const team = [
|
* Wartezeiten für neue Kundinnen. Pflegt der Salon monatlich im Cockpit.
|
||||||
{ name: 'Veronika Sprangel', role: 'Inhaberin, Friseurmeisterin' },
|
* In der Vorschau Beispielwerte.
|
||||||
{ name: 'Iris Winterstein', role: 'Friseurin' },
|
*/
|
||||||
{ name: 'Christin Gottschlich', role: 'Friseurin' },
|
export const waits = {
|
||||||
{ name: 'Marco Gollnast-Heise', role: 'Friseur und Barber' },
|
asOf: 'Oktober 2026',
|
||||||
{ name: 'Tina Sahling', role: 'Friseurin' },
|
items: [
|
||||||
{ name: 'Ella Schizipin', role: 'Friseurin' },
|
{ label: 'Balayage & Farbe', value: 'Beratung in ca. 3 Wochen' },
|
||||||
{ name: 'Fabian Gidom', role: 'Auszubildender' },
|
{ label: 'Great Lengths', value: 'Beratung in ca. 2 Wochen' },
|
||||||
{ name: 'Dennis Sprangel', role: 'Organisation' },
|
{ label: 'Braut 2027', value: 'Samstage im Mai, Juni, September frei' },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Team. PRÜFEN: Rollen und Schwerpunkte vom Salon bestätigen lassen. */
|
||||||
|
export type Person = { key: string; name: string; role: string; focus?: string };
|
||||||
|
export const team: Person[] = [
|
||||||
|
{ key: 'veronika', name: 'Veronika Sprangel', role: 'Inhaberin, Friseurmeisterin', focus: 'Braut, Farbe, Great Lengths' },
|
||||||
|
{ key: 'iris', name: 'Iris Winterstein', role: 'Friseurin' },
|
||||||
|
{ key: 'christin', name: 'Christin Gottschlich', role: 'Friseurin' },
|
||||||
|
{ key: 'marco', name: 'Marco Gollnast-Heise', role: 'Friseur und Barber', focus: 'Herren, Bart, Rasur' },
|
||||||
|
{ key: 'tina', name: 'Tina Sahling', role: 'Friseurin' },
|
||||||
|
{ key: 'ella', name: 'Ella Schizipin', role: 'Friseurin' },
|
||||||
|
{ key: 'fabian', name: 'Fabian Gidom', role: 'Auszubildender' },
|
||||||
|
{ key: 'dennis', name: 'Dennis Sprangel', role: 'Organisation und Termine' },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preise: "ab"-Richtwerte als Platzhalter. Werden mit dem Salon abgestimmt.
|
* Preise. ENTWURF: Richtwerte aus dem regionalen Marktvergleich (Oktober 2026),
|
||||||
* Ein Wert hier ändert Preisliste, Premium-Seiten und Anfrageformular zugleich.
|
* im oberen Drittel angesetzt. Die echten Preise kommen vom Salon. PRÜFEN.
|
||||||
|
* tiers = [kurz, mittel, lang]
|
||||||
*/
|
*/
|
||||||
export type Price = { label: string; from: number | null; note?: string };
|
export type Price = { label: string; note?: string; from?: number | null; tiers?: [number, number, number]; text?: string };
|
||||||
export type PriceGroup = { id: string; title: string; lead?: string; items: Price[] };
|
export type PriceGroup = { id: string; title: string; lead?: string; href?: string; items: Price[] };
|
||||||
|
|
||||||
export const priceGroups: PriceGroup[] = [
|
export const priceGroups: PriceGroup[] = [
|
||||||
{
|
{
|
||||||
id: 'braut',
|
id: 'braut',
|
||||||
title: 'Brautservice',
|
title: 'Braut',
|
||||||
lead: 'Frisur und Make-up, auf Wunsch am Ort Ihrer Hochzeit.',
|
lead: 'Frisur und Make-up, im Salon oder bei Ihnen vor Ort.',
|
||||||
|
href: '/brautservice',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Beratung und Probetermin Frisur', from: 75 },
|
{ label: 'Vorgespräch und Probestyling Frisur', note: 'wird bei Buchung verrechnet', from: 85 },
|
||||||
{ label: 'Brautfrisur am Hochzeitstag', from: 120 },
|
{ label: 'Brautfrisur am Hochzeitstag', from: 150 },
|
||||||
{ label: 'Braut-Make-up', from: 80 },
|
{ label: 'Braut-Make-up', note: 'inkl. Probe', from: 95 },
|
||||||
{ label: 'Frisur für Begleitpersonen', from: 45 },
|
{ label: 'Frisur für Begleitpersonen', note: 'Trauzeugin, Mutter, Gäste', from: 55 },
|
||||||
{ label: 'Styling vor Ort', from: null, note: 'Anfahrt nach Absprache' },
|
{ label: 'Styling vor Ort', note: 'Anfahrt im Umkreis von 30 km', text: 'nach Strecke' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'farbe',
|
id: 'farbe',
|
||||||
title: 'Balayage und Farbe',
|
title: 'Balayage & Farbe',
|
||||||
lead: 'Mit Produkten von Redken und Paul Mitchell.',
|
lead: 'Preise nach Haarlänge. Für neue Kundinnen beginnt Farbe mit einer kurzen Beratung.',
|
||||||
|
href: '/balayage-farbe',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Balayage inkl. Glossing, Schnitt und Styling', from: 140 },
|
{ label: 'Farbberatung', note: 'wird beim Termin verrechnet', from: 25 },
|
||||||
{ label: 'Foliensträhnen', from: 75 },
|
{ label: 'Balayage', note: 'inkl. Glossing, Pflege, Schnitt und Styling', tiers: [190, 230, 270] },
|
||||||
{ label: 'Coloration Ansatz', from: 50 },
|
{ label: 'Foliensträhnen', note: 'inkl. Schnitt und Styling', tiers: [110, 135, 160] },
|
||||||
{ label: 'Coloration komplett', from: 60 },
|
{ label: 'Coloration Ansatz', note: 'inkl. Föhnen', from: 59 },
|
||||||
{ label: 'Glossing', from: 30 },
|
{ label: 'Coloration komplett', note: 'inkl. Föhnen', tiers: [69, 79, 89] },
|
||||||
|
{ label: 'Glossing', from: 35 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'verlaengerung',
|
id: 'verlaengerung',
|
||||||
title: 'Haarverlängerung und -verdichtung',
|
title: 'Great Lengths',
|
||||||
lead: 'Mit Great Lengths. Vorher immer ein Beratungstermin.',
|
lead: 'Haarverlängerung und Haarverdichtung. Die Beratung ist kostenlos.',
|
||||||
|
href: '/haarverlaengerung',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Haarverdichtung', from: 350 },
|
{ label: 'Beratung mit Haaranalyse', text: 'kostenlos' },
|
||||||
{ label: 'Haarverlängerung', from: 650 },
|
{ label: 'Haarverdichtung', note: 'je nach Menge und System', from: 350 },
|
||||||
{ label: 'Nachpflege und Umsetzen', from: 90 },
|
{ label: 'Haarverlängerung', note: 'ab ca. 30 cm, Bondings oder Tressen', from: 650 },
|
||||||
|
{ label: 'Nachsetzen und Pflegetermin', from: 120 },
|
||||||
|
{ label: 'Entfernen', from: 60 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'damen',
|
id: 'damen',
|
||||||
title: 'Damen',
|
title: 'Damen',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Waschen, Schneiden, Föhnen', from: 45 },
|
{ label: 'Waschen, Schneiden, Föhnen', tiers: [49, 59, 69] },
|
||||||
{ label: 'Waschen und Föhnen', from: 28 },
|
{ label: 'Waschen und Föhnen', tiers: [29, 34, 39] },
|
||||||
{ label: 'Hochsteckfrisur', from: 50 },
|
{ label: 'Hochsteckfrisur', from: 55 },
|
||||||
{ label: 'Dauerwelle', from: 70 },
|
{ label: 'Dauerwelle', note: 'inkl. Schnitt', from: 79 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'herren',
|
id: 'herren',
|
||||||
title: 'Herren und Barbershop',
|
title: 'Herren & Barber',
|
||||||
|
href: '/barber',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Haarschnitt', from: 26 },
|
{ label: 'Haarschnitt', note: 'inkl. Waschen und Styling', from: 29 },
|
||||||
{ label: 'Bart trimmen und konturieren', from: 15 },
|
{ label: 'Maschinenschnitt', from: 19 },
|
||||||
{ label: 'Nassrasur', from: 20 },
|
{ label: 'Bart trimmen und konturieren', from: 17 },
|
||||||
|
{ label: 'Nassrasur mit heißem Tuch', from: 25 },
|
||||||
|
{ label: 'Haarschnitt und Bart', from: 42 },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'kinder',
|
id: 'kinder',
|
||||||
title: 'Kinder',
|
title: 'Kinder',
|
||||||
items: [{ label: 'Haarschnitt bis 12 Jahre', from: 18 }],
|
items: [{ label: 'Haarschnitt bis 12 Jahre', from: 19 }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'augen',
|
id: 'augen',
|
||||||
title: 'Augenbrauen und Wimpern',
|
title: 'Augenbrauen & Wimpern',
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Augenbrauen zupfen', from: 10 },
|
{ label: 'Augenbrauen zupfen', from: 10 },
|
||||||
{ label: 'Augenbrauen färben', from: 10 },
|
{ label: 'Augenbrauen färben', from: 10 },
|
||||||
@@ -148,15 +177,16 @@ export const priceGroups: PriceGroup[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const eur = (n: number) => `${n.toLocaleString('de-DE')} €`;
|
export const eur = (n: number) => `${n.toLocaleString('de-DE')} €`;
|
||||||
|
export const fromPrice = (p: Price) =>
|
||||||
|
p.text ?? (p.tiers ? `ab ${eur(p.tiers[0])}` : p.from != null ? `ab ${eur(p.from)}` : 'nach Absprache');
|
||||||
|
|
||||||
/** Anliegen im Anfrageformular (Werte exakt wie im Backend) */
|
/** Anliegen im Anfrageformular (Werte exakt wie im Backend) */
|
||||||
export const anliegen = [
|
export const anliegen = [
|
||||||
{ value: 'braut', label: 'Brautstyling', from: 75, hint: 'Probetermin ab 75 €, Brautfrisur ab 120 €' },
|
{ value: 'braut', label: 'Braut', sub: 'Frisur, Make-up, Probestyling', hint: 'Probestyling ab 85 €, Brautfrisur ab 150 €' },
|
||||||
{ value: 'balayage', label: 'Balayage oder Farbe', from: 50, hint: 'Coloration ab 50 €, Balayage ab 140 €' },
|
{ value: 'verlaengerung', label: 'Great Lengths', sub: 'Verlängerung oder Verdichtung', hint: 'Beratung kostenlos, Verdichtung ab 350 €, Verlängerung ab 650 €' },
|
||||||
{ value: 'verlaengerung', label: 'Verlängerung oder Verdichtung', from: 350, hint: 'Verdichtung ab 350 €, Verlängerung ab 650 €' },
|
{ value: 'balayage', label: 'Balayage & Farbe', sub: 'Blond, Strähnen, Farbwechsel', hint: 'Farbberatung 25 € (verrechnet), Balayage ab 190 € je nach Länge' },
|
||||||
{ value: 'typveraenderung', label: 'Typveränderung', from: 90, hint: 'Schnitt und Farbe zusammen ab 90 €' },
|
{ value: 'typveraenderung', label: 'Typveränderung', sub: 'Schnitt und Farbe neu', hint: 'Nach Beratung, meist ab 120 €' },
|
||||||
{ value: 'schnitt', label: 'Schnitt und Styling', from: 26, hint: 'Damen ab 45 €, Herren ab 26 €' },
|
{ value: 'sonstiges', label: 'Etwas anderes', sub: 'Frage, Gutschein, Sonstiges', hint: 'Preis nach Absprache' },
|
||||||
{ value: 'sonstiges', label: 'Etwas anderes', from: null, hint: 'Preis nach Absprache' },
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export const ANFRAGE_ENDPOINT = 'https://vk-karte.heidrich-digital.de/api/anfrage';
|
export const ANFRAGE_ENDPOINT = 'https://vk-karte.heidrich-digital.de/api/anfrage';
|
||||||
@@ -164,11 +194,45 @@ export const KARTE_START = 'https://vk-karte.heidrich-digital.de/start';
|
|||||||
export const KARTE_DEMO = 'https://vk-karte.heidrich-digital.de/demo';
|
export const KARTE_DEMO = 'https://vk-karte.heidrich-digital.de/demo';
|
||||||
|
|
||||||
export const nav = [
|
export const nav = [
|
||||||
{ href: '/brautservice', label: 'Brautservice' },
|
{ href: '/brautservice', label: 'Braut' },
|
||||||
{ href: '/balayage-farbe', label: 'Balayage & Farbe' },
|
{ href: '/balayage-farbe', label: 'Balayage & Farbe' },
|
||||||
{ href: '/haarverlaengerung', label: 'Haarverlängerung' },
|
{ href: '/haarverlaengerung', label: 'Great Lengths' },
|
||||||
{ href: '/leistungen-preise', label: 'Leistungen & Preise' },
|
{ href: '/barber', label: 'Barber' },
|
||||||
|
{ href: '/leistungen-preise', label: 'Preise' },
|
||||||
{ href: '/team', label: 'Team' },
|
{ href: '/team', label: 'Team' },
|
||||||
{ href: '/kundenkarte', label: 'Kundenkarte' },
|
|
||||||
{ href: '/kontakt', label: 'Kontakt' },
|
{ href: '/kontakt', label: 'Kontakt' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const navMore = [
|
||||||
|
{ href: '/neue-kundinnen', label: 'Neu bei uns' },
|
||||||
|
{ href: '/kundenkarte', label: 'Kundenkarte' },
|
||||||
|
{ href: '/gutscheine', label: 'Gutscheine' },
|
||||||
|
{ href: '/karriere', label: 'Karriere' },
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ausgewählte Google-Bewertungen (wörtlich, gekürzt nur am Satzende).
|
||||||
|
* § 5b Abs. 3 UWG: Hinweis zur Auswahl steht unter den Zitaten.
|
||||||
|
* PRÜFEN: Vor Livegang Datum je Zitat aus dem Google-Profil ergänzen.
|
||||||
|
*/
|
||||||
|
export const reviews = [
|
||||||
|
{ q: 'Toller Schnitt, tolle Farbe, tolles Team, alles perfekt!', topic: 'Farbe' },
|
||||||
|
{ q: 'Sehr sehr nette Leute, sehr kompetent, immer gute Stimmung.', topic: 'Salon' },
|
||||||
|
{ q: 'Auch das Brautstyling kann ich empfehlen!', topic: 'Braut' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const faqSchema = (items: { q: string; a: string }[]) => ({
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'FAQPage',
|
||||||
|
mainEntity: items.map((x) => ({ '@type': 'Question', name: x.q, acceptedAnswer: { '@type': 'Answer', text: x.a } })),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const serviceSchema = (name: string, description: string, minPrice?: number) => ({
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'Service',
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
provider: { '@id': `${SITE_URL}/#salon` },
|
||||||
|
areaServed: ['Tostedt', 'Buchholz in der Nordheide', 'Landkreis Harburg'],
|
||||||
|
...(minPrice ? { offers: { '@type': 'Offer', priceSpecification: { '@type': 'PriceSpecification', minPrice, priceCurrency: 'EUR' } } } : {}),
|
||||||
|
});
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import '../styles/global.css';
|
import '../styles/global.css';
|
||||||
import Header from '../components/Header.astro';
|
import Header from '../components/Header.astro';
|
||||||
import Footer from '../components/Footer.astro';
|
import Footer from '../components/Footer.astro';
|
||||||
import MobileBar from '../components/MobileBar.astro';
|
import Icon from '../components/Icon.astro';
|
||||||
import { salon, hours, SITE_URL, priceGroups } from '../data/site';
|
import { salon, hours, SITE_URL, priceGroups } from '../data/site';
|
||||||
|
|
||||||
interface Crumb { name: string; href: string }
|
interface Crumb { name: string; href: string }
|
||||||
@@ -11,21 +11,21 @@ interface Props {
|
|||||||
description: string;
|
description: string;
|
||||||
crumbs?: Crumb[];
|
crumbs?: Crumb[];
|
||||||
schema?: Record<string, unknown>[];
|
schema?: Record<string, unknown>[];
|
||||||
bar?: 'default' | 'kontakt';
|
|
||||||
ogImage?: string;
|
ogImage?: string;
|
||||||
|
bar?: boolean;
|
||||||
}
|
}
|
||||||
const { title, description, crumbs = [], schema = [], bar = 'default', ogImage = '/og.jpg' } = Astro.props;
|
const { title, description, crumbs = [], schema = [], ogImage = '/og.jpg', bar = true } = Astro.props;
|
||||||
const path = Astro.url.pathname.replace(/\/$/, '') || '/';
|
const path = Astro.url.pathname.replace(/\/$/, '') || '/';
|
||||||
const canonical = new URL(path, SITE_URL).href.replace(/\/$/, path === '/' ? '/' : '');
|
const canonical = path === '/' ? `${SITE_URL}/` : `${SITE_URL}${path}`;
|
||||||
const fullTitle = path === '/' ? title : `${title} · Friseur-Team VK Tostedt`;
|
const fullTitle = path === '/' ? title : `${title} · Friseur-Team VK Tostedt`;
|
||||||
|
|
||||||
const salonId = `${SITE_URL}/#salon`;
|
|
||||||
const hairSalon = {
|
const hairSalon = {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'HairSalon',
|
'@type': 'HairSalon',
|
||||||
'@id': salonId,
|
'@id': `${SITE_URL}/#salon`,
|
||||||
name: salon.name,
|
name: salon.name,
|
||||||
legalName: salon.legalName,
|
legalName: salon.legalName,
|
||||||
|
description: 'Meisterbetrieb in Tostedt für Brautstyling, Balayage und Great Lengths Haarverlängerung, dazu Damen, Herren und Barber.',
|
||||||
url: `${SITE_URL}/`,
|
url: `${SITE_URL}/`,
|
||||||
logo: `${SITE_URL}/logo-vk.png`,
|
logo: `${SITE_URL}/logo-vk.png`,
|
||||||
image: [`${SITE_URL}/og.jpg`],
|
image: [`${SITE_URL}/og.jpg`],
|
||||||
@@ -33,24 +33,11 @@ const hairSalon = {
|
|||||||
email: salon.email,
|
email: salon.email,
|
||||||
foundingDate: salon.foundingDate,
|
foundingDate: salon.foundingDate,
|
||||||
founder: { '@type': 'Person', name: salon.owner, jobTitle: salon.ownerTitle },
|
founder: { '@type': 'Person', name: salon.owner, jobTitle: salon.ownerTitle },
|
||||||
address: {
|
address: { '@type': 'PostalAddress', streetAddress: salon.street, postalCode: salon.zip, addressLocality: salon.city, addressRegion: salon.region, addressCountry: salon.country },
|
||||||
'@type': 'PostalAddress',
|
geo: { '@type': 'GeoCoordinates', latitude: salon.geo.lat, longitude: salon.geo.lng },
|
||||||
streetAddress: salon.street,
|
areaServed: ['Tostedt', 'Buchholz in der Nordheide', 'Hollenstedt', 'Handeloh', 'Sittensen', 'Landkreis Harburg'].map((n) => ({ '@type': 'Place', name: n })),
|
||||||
postalCode: salon.zip,
|
openingHoursSpecification: hours.filter((h) => h.open).map((h) => ({ '@type': 'OpeningHoursSpecification', dayOfWeek: `https://schema.org/${h.schema}`, opens: h.open, closes: h.close })),
|
||||||
addressLocality: salon.city,
|
sameAs: [salon.instagram, salon.facebook, salon.google.url, salon.greatLengths],
|
||||||
addressRegion: salon.region,
|
|
||||||
addressCountry: salon.country,
|
|
||||||
},
|
|
||||||
areaServed: { '@type': 'City', name: 'Tostedt' },
|
|
||||||
openingHoursSpecification: hours
|
|
||||||
.filter((h) => h.open)
|
|
||||||
.map((h) => ({
|
|
||||||
'@type': 'OpeningHoursSpecification',
|
|
||||||
dayOfWeek: `https://schema.org/${h.schema}`,
|
|
||||||
opens: h.open,
|
|
||||||
closes: h.close,
|
|
||||||
})),
|
|
||||||
sameAs: [salon.instagram, salon.facebook, salon.google.url],
|
|
||||||
brand: salon.brands.map((b) => ({ '@type': 'Brand', name: b })),
|
brand: salon.brands.map((b) => ({ '@type': 'Brand', name: b })),
|
||||||
priceRange: '€€',
|
priceRange: '€€',
|
||||||
hasOfferCatalog: {
|
hasOfferCatalog: {
|
||||||
@@ -62,24 +49,18 @@ const hairSalon = {
|
|||||||
itemListElement: g.items.map((i) => ({
|
itemListElement: g.items.map((i) => ({
|
||||||
'@type': 'Offer',
|
'@type': 'Offer',
|
||||||
itemOffered: { '@type': 'Service', name: i.label },
|
itemOffered: { '@type': 'Service', name: i.label },
|
||||||
|
...((i.tiers?.[0] ?? i.from) ? { priceSpecification: { '@type': 'PriceSpecification', minPrice: i.tiers?.[0] ?? i.from, priceCurrency: 'EUR' } } : {}),
|
||||||
})),
|
})),
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const breadcrumb = crumbs.length
|
const breadcrumb = crumbs.length
|
||||||
? {
|
? {
|
||||||
'@context': 'https://schema.org',
|
'@context': 'https://schema.org',
|
||||||
'@type': 'BreadcrumbList',
|
'@type': 'BreadcrumbList',
|
||||||
itemListElement: [{ name: 'Start', href: '/' }, ...crumbs].map((c, i) => ({
|
itemListElement: [{ name: 'Start', href: '/' }, ...crumbs].map((c, i) => ({ '@type': 'ListItem', position: i + 1, name: c.name, item: c.href === '/' ? `${SITE_URL}/` : `${SITE_URL}${c.href}` })),
|
||||||
'@type': 'ListItem',
|
|
||||||
position: i + 1,
|
|
||||||
name: c.name,
|
|
||||||
item: new URL(c.href, SITE_URL).href.replace(/\/$/, c.href === '/' ? '/' : ''),
|
|
||||||
})),
|
|
||||||
}
|
}
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
const graph = [hairSalon, ...(breadcrumb ? [breadcrumb] : []), ...schema];
|
const graph = [hairSalon, ...(breadcrumb ? [breadcrumb] : []), ...schema];
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
@@ -89,21 +70,18 @@ const graph = [hairSalon, ...(breadcrumb ? [breadcrumb] : []), ...schema];
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>{fullTitle}</title>
|
<title>{fullTitle}</title>
|
||||||
<meta name="description" content={description} />
|
<meta name="description" content={description} />
|
||||||
<!-- Vorschau: nicht indexieren. Beim Livegang diese Zeile entfernen (siehe CLAUDE.md). -->
|
<!-- Vorschau: nicht indexieren. Beim Livegang entfernen (siehe CLAUDE.md). -->
|
||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="canonical" href={canonical} />
|
<link rel="canonical" href={canonical} />
|
||||||
<meta name="theme-color" content="#f9fbfc" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
<meta name="format-detection" content="telephone=no" />
|
<meta name="format-detection" content="telephone=no" />
|
||||||
|
<link rel="preload" href="/fonts/caslon-cond-var.woff2" as="font" type="font/woff2" crossorigin />
|
||||||
<link rel="preload" href="/fonts/bodoni-moda-var.woff2" as="font" type="font/woff2" crossorigin />
|
<link rel="preload" href="/fonts/albert-sans-var.woff2" as="font" type="font/woff2" crossorigin />
|
||||||
<link rel="preload" href="/fonts/archivo-var.woff2" as="font" type="font/woff2" crossorigin />
|
<link rel="preload" href="/fonts/pinyon-script.woff2" as="font" type="font/woff2" crossorigin />
|
||||||
<link rel="preload" href="/fonts/bodoni-moda-italic-var.woff2" as="font" type="font/woff2" crossorigin />
|
|
||||||
|
|
||||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
<link rel="icon" href="/favicon-32.png" sizes="32x32" type="image/png" />
|
<link rel="icon" href="/favicon-32.png" sizes="32x32" type="image/png" />
|
||||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||||
|
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:locale" content="de_DE" />
|
<meta property="og:locale" content="de_DE" />
|
||||||
<meta property="og:site_name" content="Friseur-Team VK" />
|
<meta property="og:site_name" content="Friseur-Team VK" />
|
||||||
@@ -111,19 +89,28 @@ const graph = [hairSalon, ...(breadcrumb ? [breadcrumb] : []), ...schema];
|
|||||||
<meta property="og:description" content={description} />
|
<meta property="og:description" content={description} />
|
||||||
<meta property="og:url" content={canonical} />
|
<meta property="og:url" content={canonical} />
|
||||||
<meta property="og:image" content={new URL(ogImage, SITE_URL).href} />
|
<meta property="og:image" content={new URL(ogImage, SITE_URL).href} />
|
||||||
<meta property="og:image:width" content="1200" />
|
|
||||||
<meta property="og:image:height" content="630" />
|
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
|
||||||
{graph.map((g) => <script type="application/ld+json" set:html={JSON.stringify(g)} />)}
|
{graph.map((g) => <script type="application/ld+json" set:html={JSON.stringify(g)} />)}
|
||||||
</head>
|
</head>
|
||||||
<body data-bar={bar}>
|
<body>
|
||||||
<a class="skip" href="#inhalt">Zum Inhalt springen</a>
|
<a class="skip" href="#inhalt">Zum Inhalt springen</a>
|
||||||
|
<div class="sheet">
|
||||||
|
<div class="lines" aria-hidden="true"><span></span><span></span></div>
|
||||||
<Header current={path} />
|
<Header current={path} />
|
||||||
<main id="inhalt" tabindex="-1">
|
<main id="inhalt" tabindex="-1">
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
<MobileBar variant={bar} />
|
</div>
|
||||||
|
{bar && (
|
||||||
|
<nav class="mbar" aria-label="Schnellkontakt">
|
||||||
|
<a href={salon.phoneHref}><Icon name="phone" />Anrufen</a>
|
||||||
|
<a href="/anfrage">Anfragen</a>
|
||||||
|
</nav>
|
||||||
|
)}
|
||||||
|
<script>
|
||||||
|
import { initSite } from '../scripts/site';
|
||||||
|
initSite();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1,25 +1,26 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import Icon from '../components/Icon.astro';
|
|
||||||
import { salon } from '../data/site';
|
import { salon } from '../data/site';
|
||||||
|
const links = [
|
||||||
|
{ href: '/', t: 'Startseite' },
|
||||||
|
{ href: '/leistungen-preise', t: 'Leistungen und Preise' },
|
||||||
|
{ href: '/anfrage', t: 'Anfrage stellen' },
|
||||||
|
{ href: '/kontakt', t: 'Kontakt und Öffnungszeiten' },
|
||||||
|
];
|
||||||
---
|
---
|
||||||
<Base title="Seite nicht gefunden" description="Diese Seite gibt es nicht. Hier geht es zurück zum Friseur-Team VK in Tostedt.">
|
<Base title="Seite nicht gefunden" description="Diese Seite gibt es nicht. Hier geht es zurück zum Friseur-Team VK in Tostedt.">
|
||||||
<section class="section nf">
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
<div class="wrap nf__in">
|
<div class="pad">
|
||||||
<p class="label">Fehler 404</p>
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ 404</span></p>
|
||||||
<h1>Diese Seite <em>gibt es nicht.</em></h1>
|
<h1 id="ph-h" class="ph__title is-mid"><span class="ini">D</span>iese Seite<br /><span class="it">gibt es nicht.</span></h1>
|
||||||
<p class="lead">Vielleicht hat sich die Adresse geändert, die Website ist neu. Von hier aus finden Sie alles Wichtige:</p>
|
</div>
|
||||||
<ul class="nf__links">
|
<div class="r3 ph__grid">
|
||||||
<li><a class="more" href="/"><span>Startseite</span><Icon name="arrow" /></a></li>
|
<div class="a"></div><div class="b"></div>
|
||||||
<li><a class="more" href="/leistungen-preise"><span>Leistungen und Preise</span><Icon name="arrow" /></a></li>
|
<div class="c gap-5">
|
||||||
<li><a class="more" href="/anfrage"><span>Anfrage stellen</span><Icon name="arrow" /></a></li>
|
<p class="lead">Die Website ist neu, vielleicht hat sich die Adresse geändert. Von hier aus finden Sie alles Wichtige.</p>
|
||||||
<li><a class="more" href="/kontakt"><span>Kontakt und Öffnungszeiten</span><Icon name="arrow" /></a></li>
|
<ul class="ticks" role="list">{links.map((l) => <li><a class="link" href={l.href}>{l.t}</a></li>)}</ul>
|
||||||
</ul>
|
<p>Oder rufen Sie uns an: <a class="link" href={salon.phoneHref}>{salon.phoneDisplay}</a></p>
|
||||||
<p>Oder rufen Sie uns an: <a href={salon.phoneHref}>{salon.phoneDisplay}</a></p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
<style>
|
|
||||||
.nf__in { display: grid; gap: var(--s-5); justify-items: start; max-width: 46rem; }
|
|
||||||
.nf__links { list-style: none; padding: 0; }
|
|
||||||
</style>
|
|
||||||
@@ -1,25 +1,20 @@
|
|||||||
---
|
---
|
||||||
import Base from '../../layouts/Base.astro';
|
import Base from '../../layouts/Base.astro';
|
||||||
import Icon from '../../components/Icon.astro';
|
|
||||||
import { salon } from '../../data/site';
|
import { salon } from '../../data/site';
|
||||||
---
|
---
|
||||||
<Base title="Danke für Ihre Anfrage" description="Ihre Anfrage an das Friseur-Team VK ist angekommen. Wir rufen Sie zurück." bar="kontakt">
|
<Base title="Danke für Ihre Anfrage" description="Ihre Anfrage an das Friseur-Team VK ist angekommen. Wir rufen Sie innerhalb von zwei Werktagen zurück.">
|
||||||
<section class="section">
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
<div class="wrap thx">
|
<div class="pad">
|
||||||
<span class="thx__ico" aria-hidden="true"><Icon name="check" /></span>
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ Anfrage gesendet</span></p>
|
||||||
<p class="label">Anfrage gesendet</p>
|
<h1 id="ph-h" class="ph__title is-mid"><span class="ini">D</span>anke,<br /><span class="it">Ihre Anfrage ist da.</span></h1>
|
||||||
<h1>Danke, <em>Ihre Anfrage ist da.</em></h1>
|
</div>
|
||||||
<p class="lead">Wir melden uns telefonisch zu der Zeit, die Sie angegeben haben, und besprechen alles Weitere. Den Termin tragen wir dann für Sie ein.</p>
|
<div class="r3 ph__grid">
|
||||||
<p>Falls Sie noch etwas ergänzen möchten, erreichen Sie uns unter <a href={salon.phoneHref}>{salon.phoneDisplay}</a> oder <a href={salon.whatsappHref} rel="noopener">per WhatsApp</a>.</p>
|
<div class="a"></div><div class="b"></div>
|
||||||
<div class="btn-row">
|
<div class="c gap-5">
|
||||||
<a class="btn" href="/">Zur Startseite<Icon name="arrow" /></a>
|
<p class="lead">Wir rufen Sie innerhalb von zwei Werktagen zu Ihrer Wunschzeit zurück. Absenden ist noch keine Buchung, den Termin machen wir im Gespräch fest.</p>
|
||||||
<a class="btn btn--ghost" href="/kundenkarte">Zur Kundenkarte</a>
|
<p>Möchten Sie noch etwas ergänzen? Dann erreichen Sie uns unter <a class="link" href={salon.phoneHref}>{salon.phoneDisplay}</a> oder <a class="link" href={salon.whatsappHref} rel="noopener">per WhatsApp</a>.</p>
|
||||||
|
<div class="row"><a class="oval" href="/">Zur Startseite</a><a class="go" href="/kundenkarte">Kundenkarte</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
<style>
|
|
||||||
.thx { display: grid; gap: var(--s-5); justify-items: start; max-width: 46rem; }
|
|
||||||
.thx__ico { width: 3.5rem; height: 3.5rem; border-radius: 50%; background: var(--c-petrol); color: var(--c-papier); display: grid; place-items: center; }
|
|
||||||
.thx__ico :global(svg) { width: 1.8rem; height: 1.8rem; }
|
|
||||||
</style>
|
|
||||||
@@ -1,66 +1,110 @@
|
|||||||
---
|
---
|
||||||
import Base from '../../layouts/Base.astro';
|
import Base from '../../layouts/Base.astro';
|
||||||
import Icon from '../../components/Icon.astro';
|
|
||||||
import '../../styles/form.css';
|
import '../../styles/form.css';
|
||||||
import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
import { anliegen, salon, ANFRAGE_ENDPOINT, waits } from '../../data/site';
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Anfrage stellen"
|
title="Anfrage stellen"
|
||||||
description="Anfrage an das Friseur-Team VK in Tostedt für Brautstyling, Balayage, Haarverlängerung und mehr. Mit Fotos, Wunschzeitraum und Rückrufzeit. Wir melden uns telefonisch."
|
description="Anfrage an das Friseur-Team VK in Tostedt für Braut, Great Lengths, Balayage und Farbe. Mit Fotos und Wunschzeitraum, Rückruf innerhalb von zwei Werktagen."
|
||||||
crumbs={[{ name: 'Anfrage', href: '/anfrage' }]}
|
crumbs={[{ name: 'Anfrage', href: '/anfrage' }]}
|
||||||
bar="kontakt"
|
|
||||||
>
|
>
|
||||||
<section class="aq">
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
<div class="wrap aq__grid">
|
<div class="pad">
|
||||||
<div class="aq__side">
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ Anfrage</span></p>
|
||||||
<p class="label">Anfrage</p>
|
<h1 id="ph-h" class="ph__title is-mid"><span class="sr">Erzählen Sie uns, was Sie vorhaben.</span><span aria-hidden="true"><span class="ini">E</span>rzählen Sie uns,<br /><span class="it">was Sie vorhaben.</span></span></h1>
|
||||||
<h1>Erzählen Sie uns, <em>was Sie vorhaben.</em></h1>
|
|
||||||
<p class="lead">Wir vergeben Termine nicht online, sondern im Gespräch. Mit diesen Angaben können wir vorab einschätzen, wie viel Zeit wir einplanen und wer aus dem Team am besten passt.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="aq__main">
|
<section class="sec-b" aria-label="Anfrageformular">
|
||||||
|
<div class="r3 aq">
|
||||||
|
<aside class="ab aq__side">
|
||||||
|
<div class="gap-5 aq__sticky">
|
||||||
|
<p class="lead">Termine vergeben wir im Gespräch, nicht online. Mit diesen Angaben wissen wir vorab, wie viel Zeit wir einplanen und wer aus dem Team am besten passt.</p>
|
||||||
|
<ol class="aq__how" role="list">
|
||||||
|
<li><span>1</span>Sie schicken die Anfrage, gern mit Fotos.</li>
|
||||||
|
<li><span>2</span>Wir rufen innerhalb von zwei Werktagen zurück.</li>
|
||||||
|
<li><span>3</span>Wir tragen den Termin ein und bestätigen ihn.</li>
|
||||||
|
</ol>
|
||||||
|
<div class="note note--wash">
|
||||||
|
<p class="cap">Schnitt, Herren, Kinder</p>
|
||||||
|
<p>Dafür brauchen Sie kein Formular. Rufen Sie an oder schreiben Sie per WhatsApp.</p>
|
||||||
|
<div class="row"><a class="oval oval--sm" href={salon.phoneHref}>{salon.phoneDisplay}</a><a class="go" href={salon.whatsappHref} rel="noopener">WhatsApp</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div class="c aq__main">
|
||||||
<div id="f-summary" class="f__summary" role="alert" tabindex="-1" hidden>
|
<div id="f-summary" class="f__summary" role="alert" tabindex="-1" hidden>
|
||||||
<h2>Bitte prüfen Sie Ihre Angaben</h2>
|
<h2>Bitte prüfen Sie Ihre Angaben</h2>
|
||||||
<ul></ul>
|
<ul></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="anfrage" class="f" action={ANFRAGE_ENDPOINT} method="post" enctype="multipart/form-data" data-endpoint={ANFRAGE_ENDPOINT}>
|
<form id="anfrage" class="f" action={ANFRAGE_ENDPOINT} method="post" enctype="multipart/form-data" data-endpoint={ANFRAGE_ENDPOINT}>
|
||||||
<!-- 1 Anliegen -->
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend><span class="f__n">1</span>Worum geht es?</legend>
|
<legend><span class="f__n">1</span>Worum geht es?</legend>
|
||||||
<div class="choices choices--2" role="radiogroup" aria-describedby="err-anliegen">
|
<div class="choices" role="radiogroup" aria-describedby="err-anliegen">
|
||||||
{anliegen.map((a) => (
|
{anliegen.map((a) => (
|
||||||
<label class="choice">
|
<label class="choice">
|
||||||
<input type="radio" name="anliegen" value={a.value} required data-hint={a.hint} data-label={a.label} />
|
<input type="radio" name="anliegen" value={a.value} required data-hint={a.hint} data-label={a.label} />
|
||||||
<span class="choice__t">{a.label}</span>
|
<span class="choice__t">{a.label}</span>
|
||||||
<span class="choice__h">{a.from !== null ? `ab ${eur(a.from)}` : 'Preis nach Absprache'}</span>
|
<span class="choice__h">{a.sub}</span>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<p class="f__err" id="err-anliegen" data-for="anliegen"></p>
|
<p class="f__err" id="err-anliegen" data-for="anliegen"></p>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- 2 Braut (nur bei Brautstyling) -->
|
<!-- Braut -->
|
||||||
<fieldset class="step-later" data-locked data-only="braut">
|
<fieldset class="step-later" data-locked data-only="braut">
|
||||||
<legend><span class="f__n">2</span>Ihre Hochzeit <span class="f__opt nojs-only">(nur bei Brautstyling)</span></legend>
|
<legend><span class="f__n" data-num>2</span>Ihre Hochzeit</legend>
|
||||||
<div class="f__row3">
|
<div class="f__row2">
|
||||||
<div class="f__field">
|
<div class="f__field"><label class="f__label" for="hochzeitsdatum">Datum der Hochzeit</label><input type="date" id="hochzeitsdatum" name="hochzeitsdatum" /></div>
|
||||||
<label class="f__label" for="hochzeitsdatum">Datum der Hochzeit</label>
|
<div class="f__field"><label class="f__label" for="x_trauung">Uhrzeit der Trauung</label><input type="time" id="x_trauung" name="x_trauung" data-label="Trauung um" /></div>
|
||||||
<input type="date" id="hochzeitsdatum" name="hochzeitsdatum" />
|
|
||||||
</div>
|
</div>
|
||||||
<div class="f__field">
|
<p class="note note--wash small" id="braut-kurz" hidden>Ihre Hochzeit ist in weniger als acht Wochen. Rufen Sie uns bitte direkt an, dann sagen wir Ihnen sofort, ob es noch passt: <a class="link" href={salon.phoneHref}>{salon.phoneDisplay}</a></p>
|
||||||
<label class="f__label" for="ort">Ort oder Location <span class="f__opt">(optional)</span></label>
|
<div class="f__row2">
|
||||||
<input type="text" id="ort" name="ort" autocomplete="off" placeholder="Name der Location oder Adresse" />
|
<div class="f__field"><label class="f__label" for="x_fertig">Alle fertig bis <span class="f__opt">(optional)</span></label><input type="time" id="x_fertig" name="x_fertig" data-label="Fertig bis" /></div>
|
||||||
|
<div class="f__field"><label class="f__label" for="ort">Ort oder Location <span class="f__opt">(optional)</span></label><input type="text" id="ort" name="ort" autocomplete="off" placeholder="z. B. Gutshof in Handeloh" /></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="f__field">
|
<div class="f__row2">
|
||||||
<label class="f__label" for="personen">Personen mit Styling</label>
|
<div class="f__field"><label class="f__label" for="personen">Personen mit Frisur</label><input type="number" id="personen" name="personen" min="1" max="15" inputmode="numeric" placeholder="1" aria-describedby="help-personen" /><p class="f__help" id="help-personen">Braut plus Begleitung</p></div>
|
||||||
<input type="number" id="personen" name="personen" min="1" max="15" inputmode="numeric" placeholder="1" aria-describedby="help-personen" />
|
<div class="f__field"><label class="f__label" for="x_makeup">Personen mit Make-up <span class="f__opt">(optional)</span></label><input type="number" id="x_makeup" name="x_makeup" min="0" max="15" inputmode="numeric" placeholder="0" data-label="Make-up für" /></div>
|
||||||
<p class="f__help" id="help-personen">Braut plus Begleitung, zum Beispiel 3</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<fieldset class="f__group">
|
||||||
|
<legend class="f__sub">Wo soll gestylt werden?</legend>
|
||||||
|
<div class="choices choices--inline">
|
||||||
|
<label class="choice"><input type="radio" name="x_wo" value="im Salon" data-label="Styling" /><span class="choice__t">Im Salon</span></label>
|
||||||
|
<label class="choice"><input type="radio" name="x_wo" value="vor Ort" data-label="Styling" /><span class="choice__t">Vor Ort</span></label>
|
||||||
|
<label class="choice"><input type="radio" name="x_wo" value="noch offen" data-label="Styling" /><span class="choice__t">Noch offen</span></label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<!-- 3 Haar -->
|
<!-- Great Lengths -->
|
||||||
|
<fieldset class="step-later" data-locked data-only="verlaengerung">
|
||||||
|
<legend><span class="f__n" data-num>2</span>Ihr Wunsch</legend>
|
||||||
|
<fieldset class="f__group">
|
||||||
|
<legend class="f__sub">Was möchten Sie?</legend>
|
||||||
|
<div class="choices choices--inline">
|
||||||
|
<label class="choice"><input type="radio" name="x_gl" value="mehr Länge" data-label="Great Lengths" /><span class="choice__t">Mehr Länge</span></label>
|
||||||
|
<label class="choice"><input type="radio" name="x_gl" value="mehr Fülle" data-label="Great Lengths" /><span class="choice__t">Mehr Fülle</span></label>
|
||||||
|
<label class="choice"><input type="radio" name="x_gl" value="beides" data-label="Great Lengths" /><span class="choice__t">Beides</span></label>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
<div class="f__row2">
|
||||||
|
<div class="f__field">
|
||||||
|
<label class="f__label" for="x_ziel">Gewünschte Länge</label>
|
||||||
|
<select id="x_ziel" name="x_ziel" data-label="Ziellänge"><option value="">Bitte wählen</option><option>bis Schulter</option><option>bis Brust</option><option>bis Mitte Rücken</option><option>noch länger</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="f__field">
|
||||||
|
<label class="f__label" for="x_budget">Budgetrahmen</label>
|
||||||
|
<select id="x_budget" name="x_budget" data-label="Budget"><option value="">Bitte wählen</option><option>bis 500 €</option><option>500 bis 900 €</option><option>900 bis 1.400 €</option><option>über 1.400 €</option><option>weiß ich noch nicht</option></select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label class="check"><input type="checkbox" name="x_frueher" value="ja" data-label="Hatte schon Extensions" /><span>Ich hatte schon einmal Extensions.</span></label>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<!-- Haar -->
|
||||||
<fieldset class="step-later" data-locked>
|
<fieldset class="step-later" data-locked>
|
||||||
<legend><span class="f__n" data-num>2</span>Ihr Haar heute</legend>
|
<legend><span class="f__n" data-num>2</span>Ihr Haar heute</legend>
|
||||||
<fieldset class="f__group">
|
<fieldset class="f__group">
|
||||||
@@ -82,21 +126,21 @@ import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="f__field">
|
<div class="f__field">
|
||||||
<label class="f__label" for="wunsch">Ihr Wunsch <span class="f__opt">(optional)</span></label>
|
<label class="f__label" for="wunsch">Ihr Wunsch <span class="f__opt">(optional)</span></label>
|
||||||
<textarea id="wunsch" name="wunsch" rows="4" maxlength="2000" aria-describedby="help-wunsch"></textarea>
|
<textarea id="wunsch" name="wunsch" rows="4" maxlength="1500" aria-describedby="help-wunsch"></textarea>
|
||||||
<p class="f__help" id="help-wunsch">Was soll sich ändern? Was hat Ihnen beim letzten Mal nicht gefallen?</p>
|
<p class="f__help" id="help-wunsch">Was soll sich ändern? Was hat Ihnen beim letzten Mal nicht gefallen?</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="f__field">
|
<div class="f__field">
|
||||||
<span class="f__label" id="lbl-fotos">Fotos <span class="f__opt">(optional, bis zu 3, je höchstens 6 MB)</span></span>
|
<span class="f__label" id="lbl-fotos">Fotos <span class="f__opt">(optional, bis zu 3, je höchstens 6 MB)</span></span>
|
||||||
<div class="drop">
|
<div class="drop">
|
||||||
<input type="file" id="fotos" name="fotos" accept="image/jpeg,image/png,image/webp,image/heic,image/heif" multiple aria-labelledby="lbl-fotos" aria-describedby="help-fotos err-fotos" />
|
<input type="file" id="fotos" name="fotos" accept="image/jpeg,image/png,image/webp,image/heic,image/heif" multiple aria-labelledby="lbl-fotos" aria-describedby="help-fotos err-fotos" />
|
||||||
<p class="f__help" id="help-fotos">Am besten ein Foto vom Ist-Zustand, bei Tageslicht von hinten, und eins von Ihrem Wunsch.</p>
|
<p class="f__help" id="help-fotos">Ein Foto von Ihrem Haar heute, bei Tageslicht von hinten, hilft uns sehr.</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="files" id="files" aria-live="polite"></ul>
|
<ul class="files" id="files" aria-live="polite"></ul>
|
||||||
<p class="f__err" id="err-fotos" data-for="fotos"></p>
|
<p class="f__err" id="err-fotos" data-for="fotos"></p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- 4 Zeit -->
|
<!-- Zeit -->
|
||||||
<fieldset class="step-later" data-locked>
|
<fieldset class="step-later" data-locked>
|
||||||
<legend><span class="f__n" data-num>3</span>Wann passt es Ihnen?</legend>
|
<legend><span class="f__n" data-num>3</span>Wann passt es Ihnen?</legend>
|
||||||
<div class="f__field">
|
<div class="f__field">
|
||||||
@@ -109,11 +153,16 @@ import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
|||||||
<option>In ein bis drei Monaten</option>
|
<option>In ein bis drei Monaten</option>
|
||||||
<option>Später oder fester Termin</option>
|
<option>Später oder fester Termin</option>
|
||||||
</select>
|
</select>
|
||||||
|
<p class="f__help">Aktuell: {waits.items.map((w) => `${w.label} ${w.value.toLowerCase()}`).join(' · ')}</p>
|
||||||
</div>
|
</div>
|
||||||
<label class="check check--box">
|
<label class="check check--box">
|
||||||
<input type="checkbox" name="warteliste" value="on" />
|
<input type="checkbox" name="warteliste" value="on" />
|
||||||
<span><strong>Ich kann auch kurzfristig.</strong><br /><span class="small">Wenn ein Termin frei wird, rufen wir Sie an. So kommen Sie oft schneller dran.</span></span>
|
<span><strong>Ich kann auch kurzfristig.</strong><br /><span class="small muted">Wenn ein Termin frei wird, rufen wir Sie an. So kommen Sie oft schneller dran.</span></span>
|
||||||
</label>
|
</label>
|
||||||
|
<div class="f__field">
|
||||||
|
<label class="f__label" for="x_tage">Passende Tage <span class="f__opt">(optional)</span></label>
|
||||||
|
<input type="text" id="x_tage" name="x_tage" placeholder="z. B. dienstags und freitags ab 14 Uhr" data-label="Passende Tage" />
|
||||||
|
</div>
|
||||||
<fieldset class="f__group">
|
<fieldset class="f__group">
|
||||||
<legend class="f__sub">Waren Sie schon bei uns?</legend>
|
<legend class="f__sub">Waren Sie schon bei uns?</legend>
|
||||||
<div class="choices choices--inline">
|
<div class="choices choices--inline">
|
||||||
@@ -123,13 +172,13 @@ import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- 5 Kontakt -->
|
<!-- Kontakt -->
|
||||||
<fieldset class="step-later" data-locked>
|
<fieldset class="step-later" data-locked>
|
||||||
<legend><span class="f__n" data-num>4</span>Wie erreichen wir Sie?</legend>
|
<legend><span class="f__n" data-num>4</span>Wie erreichen wir Sie?</legend>
|
||||||
<div class="f__row2">
|
<div class="f__row2">
|
||||||
<div class="f__field">
|
<div class="f__field">
|
||||||
<label class="f__label" for="name">Name <span class="f__req" aria-hidden="true">*</span></label>
|
<label class="f__label" for="name">Name <span class="f__req" aria-hidden="true">*</span></label>
|
||||||
<input type="text" id="name" name="name" required autocomplete="name" maxlength="120" aria-describedby="err-name" />
|
<input type="text" id="name" name="name" required autocomplete="name" maxlength="100" aria-describedby="err-name" />
|
||||||
<p class="f__err" id="err-name" data-for="name"></p>
|
<p class="f__err" id="err-name" data-for="name"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="f__field">
|
<div class="f__field">
|
||||||
@@ -154,59 +203,34 @@ import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
|||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<div class="pricebox" aria-live="polite">
|
<div class="pricebox" aria-live="polite">
|
||||||
<p class="label">Preisrahmen</p>
|
<p class="cap">Preisrahmen</p>
|
||||||
<p class="pricebox__v" id="pricebox-v" hidden></p>
|
<p class="pricebox__v" id="pricebox-v" hidden></p>
|
||||||
<ul class="pricebox__all" id="pricebox-all">
|
<ul class="pricebox__all" id="pricebox-all" role="list">{anliegen.map((a) => <li>{a.label}: {a.hint}</li>)}</ul>
|
||||||
{anliegen.map((a) => <li>{a.label}: {a.hint}</li>)}
|
<p class="small">Richtwerte. Den genauen Preis nennen wir nach der Beratung. <a class="link" href="/leistungen-preise">Alle Preise</a></p>
|
||||||
</ul>
|
|
||||||
<p class="small">Richtwerte. Den genauen Preis nennen wir im Gespräch. <a href="/leistungen-preise">Alle Preise</a></p>
|
|
||||||
</div>
|
</div>
|
||||||
<label class="check">
|
<label class="check"><input type="checkbox" name="preis_ok" value="on" /><span>Ich habe den Preisrahmen gesehen und möchte beraten werden.</span></label>
|
||||||
<input type="checkbox" name="preis_ok" value="on" />
|
|
||||||
<span>Ich habe den Preisrahmen gesehen und möchte beraten werden.</span>
|
|
||||||
</label>
|
|
||||||
<label class="check">
|
<label class="check">
|
||||||
<input type="checkbox" name="datenschutz" value="on" required aria-describedby="err-datenschutz" />
|
<input type="checkbox" name="datenschutz" value="on" required aria-describedby="err-datenschutz" />
|
||||||
<span>Ich bin einverstanden, dass der Salon meine Angaben und Fotos speichert, um meine Anfrage zu bearbeiten. Mehr in der <a href="/datenschutz">Datenschutzerklärung</a>. <span class="f__req" aria-hidden="true">*</span></span>
|
<span>Ich bin einverstanden, dass der Salon meine Angaben und Fotos speichert, um meine Anfrage zu bearbeiten. Mehr in der <a class="link" href="/datenschutz">Datenschutzerklärung</a>. <span class="f__req" aria-hidden="true">*</span></span>
|
||||||
</label>
|
</label>
|
||||||
<p class="f__err" id="err-datenschutz" data-for="datenschutz"></p>
|
<p class="f__err" id="err-datenschutz" data-for="datenschutz"></p>
|
||||||
|
<div class="hp" aria-hidden="true"><label for="website">Website (bitte leer lassen)</label><input type="text" id="website" name="website" tabindex="-1" autocomplete="off" /></div>
|
||||||
<div class="hp" aria-hidden="true">
|
|
||||||
<label for="website">Website (bitte leer lassen)</label>
|
|
||||||
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="f__submit">
|
<div class="f__submit">
|
||||||
<button type="submit" class="btn" id="f-btn"><span class="f__spin" aria-hidden="true"></span><span class="f__btn-t">Anfrage senden</span><Icon name="arrow" /></button>
|
<button type="submit" class="oval oval--fill f__btn" id="f-btn"><span class="f__spin" aria-hidden="true"></span><span class="f__btn-t">Anfrage senden</span></button>
|
||||||
<p class="f__help">* Pflichtfeld. Wir melden uns telefonisch, eine Online-Buchung gibt es bewusst nicht.</p>
|
<p class="f__help">* Pflichtfeld. Absenden ist noch keine Buchung, wir rufen Sie zurück.</p>
|
||||||
<p class="f__err" id="f-net" role="alert"></p>
|
<p class="f__err" id="f-net" role="alert"></p>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div id="f-done" class="done" tabindex="-1" hidden>
|
<div id="f-done" class="done" tabindex="-1" hidden>
|
||||||
<span class="done__ico"><Icon name="check" /></span>
|
<span class="step__n" aria-hidden="true">Danke</span>
|
||||||
<h2 id="f-done-h">Danke, Ihre Anfrage ist da.</h2>
|
<h2 id="f-done-h" class="h3">Danke, Ihre Anfrage ist da.</h2>
|
||||||
<p id="f-done-p">Wir rufen Sie zurück, um alles Weitere zu besprechen.</p>
|
<p id="f-done-p">Wir rufen Sie innerhalb von zwei Werktagen zurück.</p>
|
||||||
<p class="small leise" id="f-done-id"></p>
|
<p class="cap" id="f-done-id"></p>
|
||||||
<a class="more" href="/"><span>Zur Startseite</span><Icon name="arrow" /></a>
|
<div class="row"><a class="oval" href="/">Zur Startseite</a><a class="go" href="/kundenkarte">Kundenkarte</a></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<aside class="aq__more" aria-label="So geht es weiter">
|
|
||||||
<ol class="aq__how">
|
|
||||||
<li><span>1</span>Sie schicken die Anfrage, gern mit Fotos.</li>
|
|
||||||
<li><span>2</span>Wir rufen Sie zu Ihrer Wunschzeit zurück.</li>
|
|
||||||
<li><span>3</span>Wir tragen den Termin ein und bestätigen ihn.</li>
|
|
||||||
</ol>
|
|
||||||
<div class="aq__regular">
|
|
||||||
<p class="f__label">Schon Kundin oder Kunde bei uns?</p>
|
|
||||||
<p class="small">Dann geht es am schnellsten direkt:</p>
|
|
||||||
<div class="btn-row">
|
|
||||||
<a class="btn btn--ghost" href={salon.phoneHref}><Icon name="phone" />{salon.phoneDisplay}</a>
|
|
||||||
<a class="btn btn--ghost" href={salon.whatsappHref} rel="noopener"><Icon name="whatsapp" />WhatsApp</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</aside>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
@@ -215,23 +239,3 @@ import { anliegen, salon, ANFRAGE_ENDPOINT, eur } from '../../data/site';
|
|||||||
import { initAnfrage } from '../../scripts/anfrage';
|
import { initAnfrage } from '../../scripts/anfrage';
|
||||||
initAnfrage();
|
initAnfrage();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.aq { padding-block: var(--s-7) var(--section); }
|
|
||||||
.aq__grid { display: grid; gap: var(--s-7); grid-template-areas: 'side' 'main' 'more'; }
|
|
||||||
.aq__side { grid-area: side; display: grid; gap: var(--s-5); align-content: start; }
|
|
||||||
.aq__side h1 { font-size: var(--fs-h2); }
|
|
||||||
.aq__main { grid-area: main; min-width: 0; display: grid; gap: var(--s-5); align-content: start; }
|
|
||||||
.aq__more { grid-area: more; display: grid; gap: var(--s-6); align-content: start; }
|
|
||||||
.aq__how { list-style: none; padding: 0; display: grid; gap: var(--s-3); border-top: var(--hair); padding-top: var(--s-5); }
|
|
||||||
.aq__how li { display: grid; grid-template-columns: 2rem 1fr; align-items: baseline; }
|
|
||||||
.aq__how span { font-family: var(--font-display); font-style: italic; color: var(--c-petrol); font-size: 1.25rem; }
|
|
||||||
.aq__regular { display: grid; gap: var(--s-3); padding: var(--s-5); background: var(--c-nebel-hell); border-radius: var(--r-m); }
|
|
||||||
@media (min-width: 64rem) {
|
|
||||||
.aq__grid { grid-template-columns: 0.85fr 1.15fr; grid-template-areas: 'side main' 'more main'; grid-template-rows: auto 1fr; column-gap: var(--s-9); row-gap: var(--s-6); }
|
|
||||||
}
|
|
||||||
:global(.f__row2), :global(.f__row3) { display: grid; gap: var(--s-5); }
|
|
||||||
@media (min-width: 40rem) { :global(.f__row2) { grid-template-columns: 1fr 1fr; } }
|
|
||||||
@media (min-width: 52rem) { :global(.f__row3) { grid-template-columns: 1fr 1.3fr 1fr; } }
|
|
||||||
:global(.f.is-js .nojs-only) { display: none; }
|
|
||||||
</style>
|
|
||||||
@@ -1,145 +1,100 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import PageHero from '../components/PageHero.astro';
|
import PageHead from '../components/PageHead.astro';
|
||||||
import Shot from '../components/Shot.astro';
|
|
||||||
import Pic from '../components/Pic.astro';
|
import Pic from '../components/Pic.astro';
|
||||||
import Reels from '../components/Reels.astro';
|
import H from '../components/H.astro';
|
||||||
|
import PriceGroup from '../components/PriceGroup.astro';
|
||||||
import Faq from '../components/Faq.astro';
|
import Faq from '../components/Faq.astro';
|
||||||
import PriceList from '../components/PriceList.astro';
|
import Closing from '../components/Closing.astro';
|
||||||
import CtaBand from '../components/CtaBand.astro';
|
import { priceGroups, waits, faqSchema, serviceSchema } from '../data/site';
|
||||||
import BeforeAfter from '../components/BeforeAfter.astro';
|
|
||||||
import Icon from '../components/Icon.astro';
|
|
||||||
import { SITE_URL } from '../data/site';
|
|
||||||
import redken from '../assets/img/produkte-redken.jpg';
|
|
||||||
|
|
||||||
const techniken = [
|
import hero from '../assets/img/arbeiten/balayage-beige-wellen.jpg';
|
||||||
{ t: 'Balayage', d: 'Die Farbe wird frei Hand aufgemalt. Der Verlauf von dunkel zu hell wirkt weich, der Ansatz wächst unauffällig heraus. Gut für alle, die nicht alle sechs Wochen nachfärben möchten.' },
|
import a1 from '../assets/img/arbeiten/balayage-hellblond.jpg';
|
||||||
{ t: 'Foliensträhnen', d: 'Einzelne Partien werden in Folie aufgehellt. Das Ergebnis ist gleichmäßiger und heller als bei einer Balayage und lässt sich gezielt um das Gesicht setzen.' },
|
import a2 from '../assets/img/arbeiten/balayage-honigblond.jpg';
|
||||||
{ t: 'Coloration', d: 'Ansatz oder komplette Farbe, deckend oder als Tönung. Auch graue Haare decken wir ab.' },
|
import a3 from '../assets/img/arbeiten/balayage-sandblond.jpg';
|
||||||
{ t: 'Glossing', d: 'Eine Pflegetönung für Glanz und den letzten Farbton, oft nach einer Aufhellung. Sie frischt auch eine Balayage zwischen zwei Terminen auf.' },
|
import a4 from '../assets/img/arbeiten/balayage-karamell.jpg';
|
||||||
|
import a5 from '../assets/img/arbeiten/farbe-rose-wellen.jpg';
|
||||||
|
import a6 from '../assets/img/arbeiten/farbe-pastell.jpg';
|
||||||
|
import cV from '../assets/img/arbeiten/farbe-vorher-blond.jpg';
|
||||||
|
import cN from '../assets/img/arbeiten/farbe-nachher-blond.jpg';
|
||||||
|
import kV from '../assets/img/arbeiten/farbe-vorher-kupfer.jpg';
|
||||||
|
import kN from '../assets/img/arbeiten/farbe-nachher-kupfer.jpg';
|
||||||
|
|
||||||
|
const group = priceGroups.find((g) => g.id === 'farbe')!;
|
||||||
|
const cases = [
|
||||||
|
{ n: '001', title: 'Von Braun zu Blond', v: cV, nn: cN, altV: 'Langes, mittelbraunes Haar vorher', alt: 'Helles, kühles Blond nachher', facts: [['Ausgangslage', 'mittelbraun, lang, Naturhaar'], ['Wunsch', 'helles Blond, weicher Ansatz'], ['Technik', 'Aufhellung, Glossing, Pflege'], ['Richtwert', 'ab 160 € (lang)']] },
|
||||||
|
{ n: '002', title: 'Von Blond zu Kupfer', v: kV, nn: kN, altV: 'Blondes, schulterlanges Haar vorher', alt: 'Warmes Kupfer nachher', facts: [['Ausgangslage', 'blond, schulterlang'], ['Wunsch', 'warm, kräftig, pflegeleicht'], ['Technik', 'Coloration und Glossing'], ['Richtwert', 'ab 79 € (mittel)']] },
|
||||||
|
];
|
||||||
|
const gallery = [
|
||||||
|
{ src: a1, alt: 'Balayage in Hellblond mit Wellen' },
|
||||||
|
{ src: a2, alt: 'Honigblonde Balayage, von hinten fotografiert' },
|
||||||
|
{ src: a3, alt: 'Sandblonde Balayage mit Wellen' },
|
||||||
|
{ src: a4, alt: 'Balayage in Karamell' },
|
||||||
|
{ src: a5, alt: 'Rosé-Blond mit weichen Wellen' },
|
||||||
|
{ src: a6, alt: 'Platinblond mit Pastell-Strähnen' },
|
||||||
];
|
];
|
||||||
const faq = [
|
const faq = [
|
||||||
{ q: 'Wie lange dauert eine Balayage?', a: 'Das hängt von Haarlänge, Haardichte und Ausgangsfarbe ab. Rechnen Sie mit mehreren Stunden. Die genaue Dauer sagen wir Ihnen bei der Terminabsprache.' },
|
{ q: 'Warum zuerst eine Beratung?', a: 'Weil wir sehen müssen, wie Ihr Haar auf Farbe reagiert und was frühere Färbungen hinterlassen haben. So planen wir die richtige Zeit ein und sagen Ihnen vorher, was es kostet. Die 25 € für die Beratung ziehen wir beim Termin wieder ab.' },
|
||||||
{ q: 'Geht Balayage auch auf gefärbtem oder blondiertem Haar?', a: 'Oft ja, aber nicht immer in einem Termin. Schreiben Sie in der Anfrage, wie Ihr Haar vorbehandelt ist, und schicken Sie ein Foto mit. Dann können wir vorher einschätzen, was möglich ist.' },
|
{ q: 'Wie lange dauert eine Balayage?', a: 'Rechnen Sie mit drei bis fünf Stunden, je nach Länge und Ausgangsfarbe. Getränke gibt es bei uns, Snacks dürfen Sie mitbringen.' },
|
||||||
{ q: 'Mit welchen Produkten arbeiten Sie?', a: 'Mit Farben und Pflege von Redken und Paul Mitchell. Die passende Pflege für zu Hause bekommen Sie bei uns im Salon.' },
|
{ q: 'Wie oft muss ich nachfärben?', a: 'Eine Balayage wächst weich heraus. Die meisten Kundinnen kommen nach drei bis vier Monaten zum Glossing oder Auffrischen.' },
|
||||||
{ q: 'Was kostet eine Balayage?', a: 'Eine Balayage beginnt bei 140 Euro inklusive Glossing, Schnitt und Styling (Richtwert). Der genaue Preis hängt von Länge und Aufwand ab, wir nennen ihn Ihnen vorher.' },
|
{ q: 'Mit welchen Produkten färben Sie?', a: 'Mit Paul Mitchell und Redken. Der Salon ist Paul Mitchell Flagship-Salon.' },
|
||||||
];
|
];
|
||||||
const faqSchema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'FAQPage',
|
|
||||||
mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })),
|
|
||||||
};
|
|
||||||
const service = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Service',
|
|
||||||
name: 'Balayage und Haarfarbe',
|
|
||||||
serviceType: 'Balayage, Foliensträhnen, Coloration, Glossing',
|
|
||||||
provider: { '@id': `${SITE_URL}/#salon` },
|
|
||||||
areaServed: 'Tostedt und Umgebung',
|
|
||||||
};
|
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Balayage und Haarfarbe in Tostedt"
|
title="Balayage und Haarfarbe in Tostedt"
|
||||||
description="Balayage, Foliensträhnen, Coloration und Glossing beim Friseur-Team VK in Tostedt, mit Redken und Paul Mitchell. Mit Fotos anfragen, wir beraten vorher."
|
description="Balayage, Foliensträhnen und Farbwechsel vom Meisterbetrieb in Tostedt, mit Paul Mitchell und Redken. Preise nach Haarlänge, Balayage ab 190 €, Beratung wird verrechnet."
|
||||||
crumbs={[{ name: 'Balayage & Farbe', href: '/balayage-farbe' }]}
|
crumbs={[{ name: 'Balayage & Farbe', href: '/balayage-farbe' }]}
|
||||||
schema={[service, faqSchema]}
|
schema={[faqSchema(faq), serviceSchema('Balayage und Coloration', 'Balayage, Foliensträhnen, Coloration und Glossing mit Paul Mitchell und Redken.', 190)]}
|
||||||
>
|
>
|
||||||
<PageHero
|
<PageHead crumb="Balayage & Farbe" title="Balayage" sub="und Farbe">
|
||||||
label="Balayage und Farbe"
|
<p class="lead" data-rv>Weiche Übergänge, ein Blond, das zu Ihrer Haut passt, und Haar, das danach noch glänzt. Dafür nehmen wir uns vorher Zeit.</p>
|
||||||
title="Farbe, die <em>zu Ihrem Haar passt.</em>"
|
<div class="row" data-rv><a class="oval oval--fill" href="/anfrage?anliegen=balayage">Beratung anfragen</a><a class="go" href="#preise">Preise nach Länge</a></div>
|
||||||
lead="Wir planen jede Farbe nach Ihrem Ausgangshaar: Naturton, Vorbehandlung, Länge. Dafür hilft ein Foto vom Ist-Zustand schon bei der Anfrage."
|
<p class="cap">Neue Kundinnen: {waits.items[0].value}</p>
|
||||||
>
|
<figure class="fig r-4x3 mt-4" data-clip><Pic src={hero} alt="Balayage in Beige mit großen Wellen, von hinten fotografiert" sizes="(min-width: 56rem) 55vw, 100vw" eager pos="top" /></figure>
|
||||||
<div slot="actions" class="btn-row">
|
</PageHead>
|
||||||
<a class="btn" href="/anfrage?anliegen=balayage">Farbe anfragen<Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
<Shot slot="media" arch tone="petrol" title="Balayage, Ergebnis" detail="Rückenansicht, lange Wellen, Tageslicht am Fenster" ratio="4/5" />
|
|
||||||
</PageHero>
|
|
||||||
|
|
||||||
<section class="section section--nebel" aria-labelledby="tech-h">
|
<section class="sec-b" aria-labelledby="w-h">
|
||||||
<div class="wrap">
|
<div class="r3">
|
||||||
<div class="head">
|
<div class="ab"><H text="Wie wir färben" /></div>
|
||||||
<p class="label">Techniken</p>
|
<div class="c copy gap-5" data-rv>
|
||||||
<h2 id="tech-h">Vier Wege <em>zur Farbe.</em></h2>
|
<p>Bei einer Balayage malen wir die Aufhellung frei von Hand ins Haar. Der Ansatz bleibt natürlich, die Längen werden heller, und es wächst ohne harte Kante heraus.</p>
|
||||||
|
<p>Vorher sehen wir uns Ihr Haar an: Wie wurde es bisher gefärbt, wie stark ist es, was wünschen Sie sich wirklich? In einer Bewertung schreibt eine Kundin, dass wir sie vor dem Färben über die Risiken aufgeklärt und nach einer schonenden Lösung gesucht haben. Genau so soll es sein.</p>
|
||||||
|
<p class="muted">Wir arbeiten mit Paul Mitchell und Redken und geben Ihnen die passende Pflege für zu Hause mit.</p>
|
||||||
</div>
|
</div>
|
||||||
<dl class="tech">
|
|
||||||
{techniken.map((x, i) => (
|
|
||||||
<div class="tech__row reveal">
|
|
||||||
<dt><span class="tech__n display" aria-hidden="true">{String(i + 1).padStart(2, '0')}</span>{x.t}</dt>
|
|
||||||
<dd>{x.d}</dd>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="e-h">
|
||||||
|
<div class="pad"><H text="Vorher, nachher" /></div>
|
||||||
|
<div class="cases mt-7">
|
||||||
|
{cases.map((c) => (
|
||||||
|
<article class="case">
|
||||||
|
<span class="cap case__n">{c.n}</span>
|
||||||
|
<div class="case__meta" data-rv>
|
||||||
|
<h3 class="h3">{c.title}</h3>
|
||||||
|
<dl class="facts">{c.facts.map(([k, v]) => <><dt>{k}</dt><dd>{v}</dd></>)}</dl>
|
||||||
|
</div>
|
||||||
|
<div class="case__pics">
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.v} alt={c.altV} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Vorher</span></figure>
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.nn} alt={c.alt} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Nachher</span></figure>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
))}
|
))}
|
||||||
</dl>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section" aria-labelledby="ba-h">
|
<section class="sec-b" aria-label="Galerie Balayage">
|
||||||
<div class="wrap ba-grid">
|
<div class="pad"><div class="gal">
|
||||||
<div class="head">
|
{gallery.map((g, i) => <figure class={`fig r-3x4 d-${(i % 3) + 1}`} data-rv><Pic src={g.src} alt={g.alt} sizes="(min-width: 40rem) 30vw, 50vw" widths={[360, 540, 675]} /></figure>)}
|
||||||
<p class="label">Vorher und nachher</p>
|
</div></div>
|
||||||
<h2 id="ba-h">Der Unterschied <em>auf einen Blick.</em></h2>
|
|
||||||
<p>Ziehen Sie den Regler, um vorher und nachher zu vergleichen. Die Bildpaare entstehen beim Shooting mit echten Kundinnen, die zugestimmt haben.</p>
|
|
||||||
</div>
|
|
||||||
<BeforeAfter before="Dunkelblond, herausgewachsen" after="Balayage mit Glossing" detail="Fotopaar folgt: gleiche Pose, gleiches Licht, vorher und nachher." />
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section--tight brands" aria-labelledby="brands-h">
|
<section class="sec-b" id="preise" aria-label="Preise Farbe">
|
||||||
<div class="wrap brands__grid">
|
<div class="pad"><H text="Preise" /></div>
|
||||||
<figure class="brands__img reveal">
|
<div class="plist mt-7"><PriceGroup group={group} n="001" /></div>
|
||||||
<div class="frame ratio-32">
|
<p class="cap pad from-c mt-4">Kurz: bis Kinn · Mittel: bis Schulter · Lang: über die Schulter. Richtwerte, den genauen Preis nennen wir nach der Beratung.</p>
|
||||||
<Pic src={redken} alt="Produktregal mit Redken-Shampoos und -Pflege im Salon" sizes="(min-width: 60rem) 55vw, 100vw" />
|
|
||||||
</div>
|
|
||||||
</figure>
|
|
||||||
<div class="brands__txt">
|
|
||||||
<p class="label">Produkte</p>
|
|
||||||
<h2 id="brands-h" class="brands__h">Redken und <em>Paul Mitchell.</em></h2>
|
|
||||||
<p>Mit diesen beiden Marken färben und pflegen wir im Salon. Welche Pflege zu Ihrer neuen Farbe passt, besprechen wir am Ende des Termins.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<Reels title="Farbe in Bewegung" items={[
|
|
||||||
{ title: 'Balayage vorher und nachher', detail: 'Drehung im Tageslicht' },
|
|
||||||
{ title: 'Freihand-Technik', detail: 'Pinsel und Farbe, Nahaufnahme' },
|
|
||||||
{ title: 'Glossing', detail: 'Glanz nach dem Föhnen' },
|
|
||||||
{ title: 'Kühles Blond', detail: 'Ergebnis nach Foliensträhnen' },
|
|
||||||
]} />
|
|
||||||
|
|
||||||
<section class="section" aria-labelledby="preise-h">
|
|
||||||
<div class="wrap split">
|
|
||||||
<div class="head">
|
|
||||||
<p class="label">Preise</p>
|
|
||||||
<h2 id="preise-h">Was es <em>kostet.</em></h2>
|
|
||||||
<p>Richtwerte für die häufigsten Farbbehandlungen. Bei langem oder sehr dichtem Haar wird es mehr, das sagen wir Ihnen vorher.</p>
|
|
||||||
</div>
|
|
||||||
<PriceList ids={['farbe']} headingLevel={3} compact />
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Faq items={faq} />
|
<Faq items={faq} />
|
||||||
|
<Closing title="Erst reden, dann färben" text="Schicken Sie uns ein Foto von Ihrem Haar heute und eins von Ihrem Wunsch. Wir rufen zurück und schlagen einen Beratungstermin vor." anliegen="balayage" cta="Beratung anfragen" />
|
||||||
<CtaBand
|
|
||||||
title="Zeigen Sie uns Ihr Haar."
|
|
||||||
text="Ein Foto vom Ist-Zustand und eins von Ihrem Wunsch reichen für eine erste Einschätzung. Wir rufen Sie zurück und besprechen Technik, Dauer und Preis."
|
|
||||||
anliegen="balayage"
|
|
||||||
button="Farbe anfragen"
|
|
||||||
/>
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.tech { margin-top: var(--s-7); border-top: 1.5px solid var(--c-anthrazit); }
|
|
||||||
.tech__row { display: grid; gap: var(--s-2); padding-block: var(--s-5); border-bottom: var(--hair); }
|
|
||||||
.tech__row dt { font-family: var(--font-display); font-size: var(--fs-h3); line-height: 1.15; display: flex; gap: var(--s-4); align-items: baseline; }
|
|
||||||
.tech__n { font-size: 0.6em; font-style: italic; color: var(--c-petrol); min-width: 2ch; }
|
|
||||||
.tech__row dd { max-width: 58ch; }
|
|
||||||
@media (min-width: 52rem) { .tech__row { grid-template-columns: 1fr 1.4fr; gap: var(--s-7); align-items: baseline; } }
|
|
||||||
.ba-grid { display: grid; gap: var(--s-7); }
|
|
||||||
@media (min-width: 60rem) { .ba-grid { grid-template-columns: 1fr 1fr; gap: var(--s-9); align-items: center; } }
|
|
||||||
.brands__grid { display: grid; gap: var(--s-6); }
|
|
||||||
.brands__img { margin: 0; }
|
|
||||||
.brands__txt { display: grid; gap: var(--s-4); max-width: 30rem; align-content: center; }
|
|
||||||
.brands__h { font-size: var(--fs-h3); }
|
|
||||||
@media (min-width: 60rem) { .brands__grid { grid-template-columns: 1.3fr 1fr; gap: var(--s-8); } .brands__img { margin-left: calc(var(--gutter) * -1); } }
|
|
||||||
.split { display: grid; gap: var(--s-7); }
|
|
||||||
@media (min-width: 60rem) { .split { grid-template-columns: 1fr 1.2fr; gap: var(--s-9); align-items: start; } }
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHead from '../components/PageHead.astro';
|
||||||
|
import Pic from '../components/Pic.astro';
|
||||||
|
import H from '../components/H.astro';
|
||||||
|
import PriceGroup from '../components/PriceGroup.astro';
|
||||||
|
import Closing from '../components/Closing.astro';
|
||||||
|
import { priceGroups, salon, serviceSchema } from '../data/site';
|
||||||
|
|
||||||
|
import marco from '../assets/img/team/marco.jpg';
|
||||||
|
import h1 from '../assets/img/arbeiten/barber-bart.jpg';
|
||||||
|
import h2 from '../assets/img/arbeiten/barber-rasur.jpg';
|
||||||
|
import h3 from '../assets/img/arbeiten/barber-konturen.jpg';
|
||||||
|
import h4 from '../assets/img/arbeiten/herren-kurz-1.jpg';
|
||||||
|
import h5 from '../assets/img/arbeiten/herren-kurz-2.jpg';
|
||||||
|
import maenner from '../assets/img/team/team-maenner.jpg';
|
||||||
|
|
||||||
|
const group = priceGroups.find((g) => g.id === 'herren')!;
|
||||||
|
const kinder = priceGroups.find((g) => g.id === 'kinder')!;
|
||||||
|
---
|
||||||
|
<Base
|
||||||
|
title="Herrenfriseur und Barber in Tostedt"
|
||||||
|
description="Herrenhaarschnitt, Bart konturieren und Nassrasur mit heißem Tuch beim Friseur-Team VK in Tostedt. Haarschnitt ab 29 €, Termin per Telefon oder WhatsApp."
|
||||||
|
crumbs={[{ name: 'Barber', href: '/barber' }]}
|
||||||
|
schema={[serviceSchema('Herrenhaarschnitt und Barber', 'Herrenhaarschnitt, Bartpflege und Nassrasur in Tostedt.', 29)]}
|
||||||
|
>
|
||||||
|
<PageHead crumb="Barber" title="Barber" sub="Schnitt, Bart, Rasur">
|
||||||
|
<p class="lead" data-rv>Haarschnitt, Bart in Form und eine Nassrasur mit heißem Tuch. Für Herren geht es ohne Formular: anrufen oder schreiben, fertig.</p>
|
||||||
|
<div class="row" data-rv><a class="oval oval--fill" href={salon.phoneHref}>{salon.phoneDisplay}</a><a class="oval" href={salon.whatsappHref} rel="noopener">WhatsApp</a></div>
|
||||||
|
<figure class="fig r-4x3 mt-4" data-clip><Pic src={h1} alt="Bart wird mit der Maschine konturiert" sizes="(min-width: 56rem) 55vw, 100vw" eager /></figure>
|
||||||
|
</PageHead>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="m-h">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-2x3" data-clip><Pic src={marco} alt="Marco, Friseur und Barber" sizes="(min-width: 56rem) 36vw, 100vw" widths={[400, 600, 800]} /></figure></div>
|
||||||
|
<div class="c gap-5 self-end" data-rv>
|
||||||
|
<span class="cap">Ihr Barber</span>
|
||||||
|
<h2 class="h3" id="m-h">Marco Gollnast-Heise</h2>
|
||||||
|
<p class="copy">Marco schneidet Herren und kümmert sich um Bart und Rasur. In einer Google-Bewertung wird er namentlich für einen Kinderhaarschnitt gelobt.</p>
|
||||||
|
<p class="cap">Wer im Team außerdem Herren schneidet, sagen wir Ihnen gern am Telefon.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-label="Arbeiten">
|
||||||
|
<div class="pad"><div class="gal gal--4">
|
||||||
|
{[h2, h3, h4, h5].map((src, i) => <figure class={`fig r-3x4 d-${i + 1}`} data-rv><Pic src={src} alt={['Nassrasur mit heißem Tuch', 'Bartkontur', 'Kurzer Herrenschnitt, seitlich', 'Herrenschnitt mit Volumen, von hinten'][i]} sizes="(min-width: 64rem) 22vw, 50vw" widths={[360, 540, 675]} /></figure>)}
|
||||||
|
</div></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-label="Preise Herren">
|
||||||
|
<div class="pad"><H text="Preise" /></div>
|
||||||
|
<div class="plist mt-7"><PriceGroup group={group} n="001" /><PriceGroup group={kinder} n="002" /></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-label="Team">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-4x3" data-clip><Pic src={maenner} alt="Die Männer im Team" sizes="(min-width: 56rem) 36vw, 100vw" /></figure></div>
|
||||||
|
<div class="c self-end"><p class="lead" data-rv>Kein Formular, kein Warten auf Rückruf. Für Herren vergeben wir Termine direkt am Telefon, oft noch in derselben Woche.</p></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Closing title="Einfach anrufen" text="Herrenschnitt, Bart oder Rasur: Rufen Sie an oder schreiben Sie per WhatsApp. Wir sagen Ihnen sofort, wann es passt." phoneFirst />
|
||||||
|
</Base>
|
||||||
@@ -1,127 +1,107 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import PageHero from '../components/PageHero.astro';
|
import PageHead from '../components/PageHead.astro';
|
||||||
import Shot from '../components/Shot.astro';
|
import Pic from '../components/Pic.astro';
|
||||||
import Steps from '../components/Steps.astro';
|
import H from '../components/H.astro';
|
||||||
import Reels from '../components/Reels.astro';
|
import PriceGroup from '../components/PriceGroup.astro';
|
||||||
import Faq from '../components/Faq.astro';
|
import Faq from '../components/Faq.astro';
|
||||||
import PriceList from '../components/PriceList.astro';
|
import Closing from '../components/Closing.astro';
|
||||||
import CtaBand from '../components/CtaBand.astro';
|
import { priceGroups, waits, faqSchema, serviceSchema, reviews } from '../data/site';
|
||||||
import Icon from '../components/Icon.astro';
|
|
||||||
import Rating from '../components/Rating.astro';
|
import hero from '../assets/img/arbeiten/braut-hochsteck-haarschmuck.jpg';
|
||||||
import { SITE_URL } from '../data/site';
|
import b1 from '../assets/img/arbeiten/braut-blumen-chignon.jpg';
|
||||||
|
import b2 from '../assets/img/arbeiten/braut-halboffen-spange.jpg';
|
||||||
|
import b3 from '../assets/img/arbeiten/braut-flechtfrisur.jpg';
|
||||||
|
import b4 from '../assets/img/arbeiten/braut-hochsteck-perlen.jpg';
|
||||||
|
import b5 from '../assets/img/arbeiten/braut-wellen-bluetenspangen.jpg';
|
||||||
|
import b6 from '../assets/img/arbeiten/braut-chignon-blueten.jpg';
|
||||||
|
import b7 from '../assets/img/arbeiten/braut-zopf-garten.jpg';
|
||||||
|
import b8 from '../assets/img/arbeiten/braut-wellen-offen.jpg';
|
||||||
|
|
||||||
|
const group = priceGroups.find((g) => g.id === 'braut')!;
|
||||||
|
const gallery = [
|
||||||
|
{ src: b1, alt: 'Tiefer Chignon mit frischen Blüten' },
|
||||||
|
{ src: b2, alt: 'Halboffene Brautfrisur mit Wellen und Spange' },
|
||||||
|
{ src: b3, alt: 'Geflochtene Hochsteckfrisur' },
|
||||||
|
{ src: b4, alt: 'Hochsteckfrisur mit Perlenkette, für die Brautmutter' },
|
||||||
|
{ src: b5, alt: 'Offene Wellen mit Blütenspangen' },
|
||||||
|
{ src: b6, alt: 'Chignon mit Porzellanblüten' },
|
||||||
|
{ src: b7, alt: 'Lockerer Zopf mit Blüte, im Garten fotografiert' },
|
||||||
|
{ src: b8, alt: 'Lange, offene Brautwellen' },
|
||||||
|
];
|
||||||
|
|
||||||
const faq = [
|
const faq = [
|
||||||
{ q: 'Kommen Sie am Hochzeitstag auch zu mir?', a: 'Ja. Das Styling am Hochzeitstag ist im Salon oder bei Ihnen vor Ort möglich, zum Beispiel zu Hause oder in der Location. Die Anfahrt besprechen wir bei der Beratung.' },
|
{ q: 'Wie früh sollte ich anfragen?', a: 'Sechs bis zwölf Monate vor der Hochzeit. Samstage im Mai, Juni und September sind schnell vergeben. Liegt Ihre Hochzeit weniger als acht Wochen entfernt, rufen Sie bitte direkt an.' },
|
||||||
{ q: 'Gibt es einen Probetermin?', a: 'Ja, vor der Hochzeit gibt es einen Probetermin. Dort probieren wir Frisur und auf Wunsch Make-up aus, damit am Hochzeitstag alles sitzt und nichts mehr überraschend ist.' },
|
{ q: 'Muss ich zum Probestyling?', a: 'Wir empfehlen es sehr. Beim Probestyling testen wir Frisur, Haarschmuck und Halt, und Sie wissen am Hochzeitsmorgen genau, was passiert. Der Betrag wird verrechnet, wenn Sie bei uns buchen.' },
|
||||||
{ q: 'Können Begleitpersonen mitgestylt werden?', a: 'Ja. Geben Sie in der Anfrage an, wie viele Personen eine Frisur oder ein Make-up möchten. Dann planen wir die Zeit am Morgen entsprechend.' },
|
{ q: 'Kommen Sie auch zu uns?', a: 'Ja, im Umkreis von etwa 30 Kilometern um Tostedt, zum Beispiel nach Buchholz, Hollenstedt, Handeloh oder auf einen Gutshof in der Heide. Die Anfahrt berechnen wir nach Strecke.' },
|
||||||
{ q: 'Wie läuft die Anfrage ab?', a: 'Sie schicken uns über das Formular Datum, Ort und Ihre Wünsche, gern mit Inspirationsfotos. Wir rufen Sie zurück, klären die Details und tragen Beratung, Probetermin und Hochzeitstag bei uns ein.' },
|
{ q: 'Wie viele Personen können Sie am Morgen stylen?', a: 'Das hängt von der Uhrzeit ab. Rechnen Sie für die Braut mit 90 bis 120 Minuten, für Begleitpersonen mit 45 Minuten. Sagen Sie uns in der Anfrage, bis wann alle fertig sein müssen, dann planen wir rückwärts.' },
|
||||||
{ q: 'Was kostet das Brautstyling?', a: 'Der Probetermin beginnt bei 75 Euro, die Brautfrisur am Hochzeitstag bei 120 Euro (Richtwerte). Den genauen Preis nennen wir Ihnen nach der Beratung, er hängt von Frisur, Haarlänge und Anfahrt ab.' },
|
{ q: 'Wie sichere ich den Termin?', a: 'Mit dem Probestyling. Ab da ist Ihr Datum fest bei uns eingetragen.' },
|
||||||
];
|
];
|
||||||
const faqSchema = {
|
const brautReview = reviews.find((r) => r.topic === 'Braut');
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'FAQPage',
|
|
||||||
mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })),
|
|
||||||
};
|
|
||||||
const service = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Service',
|
|
||||||
name: 'Brautstyling',
|
|
||||||
serviceType: 'Brautfrisur und Braut-Make-up',
|
|
||||||
provider: { '@id': `${SITE_URL}/#salon` },
|
|
||||||
areaServed: 'Tostedt und Umgebung',
|
|
||||||
description: 'Beratung, Probetermin und Styling am Hochzeitstag, im Salon oder vor Ort.',
|
|
||||||
};
|
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Brautservice: Brautfrisur und Make-up in Tostedt"
|
title="Brautfrisur und Brautstyling in Tostedt"
|
||||||
description="Brautstyling beim Friseur-Team VK in Tostedt: Beratung, Probetermin und Styling am Hochzeitstag, auf Wunsch bei Ihnen vor Ort. Jetzt Anfrage mit Datum und Ort stellen."
|
description="Brautfrisur und Braut-Make-up vom Meisterbetrieb in Tostedt, mit Probestyling und auf Wunsch vor Ort im Landkreis Harburg. Brautfrisur ab 150 €, Probestyling ab 85 €."
|
||||||
crumbs={[{ name: 'Brautservice', href: '/brautservice' }]}
|
crumbs={[{ name: 'Braut', href: '/brautservice' }]}
|
||||||
schema={[service, faqSchema]}
|
schema={[faqSchema(faq), serviceSchema('Brautfrisur und Brautstyling', 'Brautfrisur, Braut-Make-up und Styling für Begleitpersonen, im Salon in Tostedt oder vor Ort.', 85)]}
|
||||||
>
|
>
|
||||||
<PageHero
|
<PageHead crumb="Braut" title="Brautfrisur" sub="in Tostedt und vor Ort">
|
||||||
label="Brautservice"
|
<p class="lead" data-rv>Vom ersten Probestyling bis zur letzten Haarnadel am Morgen der Hochzeit. Frisur und Make-up aus einer Hand, im Salon oder bei Ihnen.</p>
|
||||||
title="Ihr Hochzeitstag, <em>in Ruhe vorbereitet.</em>"
|
<div class="row" data-rv><a class="oval oval--fill" href="/anfrage?anliegen=braut">Datum anfragen</a><a class="go" href="#preise">Preise</a></div>
|
||||||
lead="Frisur und Make-up für die Braut und auf Wunsch für die Begleitung. Wir beginnen mit einem Gespräch, proben die Frisur vorab und sind am Hochzeitstag im Salon oder bei Ihnen vor Ort."
|
<p class="cap">{waits.items[2].label}: {waits.items[2].value}</p>
|
||||||
>
|
<figure class="fig r-4x3 mt-4" data-clip><Pic src={hero} alt="Hochsteckfrisur mit Haarschmuck, seitlich fotografiert" sizes="(min-width: 56rem) 55vw, 100vw" eager pos="top" /></figure>
|
||||||
<div slot="actions" class="btn-row">
|
</PageHead>
|
||||||
<a class="btn" href="/anfrage?anliegen=braut">Brautstyling anfragen<Icon name="arrow" /></a>
|
|
||||||
<Rating />
|
|
||||||
</div>
|
|
||||||
<Shot slot="media" arch title="Braut-Hochsteckfrisur" detail="Detail Nacken mit Haarschmuck, weiches Fensterlicht" ratio="4/5" />
|
|
||||||
</PageHero>
|
|
||||||
|
|
||||||
<section class="section section--nebel" aria-labelledby="ablauf-h">
|
<!-- Ablauf -->
|
||||||
<div class="wrap">
|
<section class="sec-b" aria-labelledby="ab-h">
|
||||||
<div class="head">
|
<div class="pad"><H text="So läuft es ab" /></div>
|
||||||
<p class="label">Ablauf</p>
|
<div class="steps mt-7">
|
||||||
<h2 id="ablauf-h">Drei Termine bis <em>zum Ja.</em></h2>
|
<p class="steps__lead">Von der Anfrage<br />bis zum Ja</p>
|
||||||
|
<div class="step" data-rv><span class="step__n" aria-hidden="true">1</span><h3>Anfrage mit Datum</h3><p>Datum, Uhrzeit der Trauung, Ort und wer außer Ihnen gestylt werden soll. Inspirationsfotos helfen sehr.</p></div>
|
||||||
|
<div class="step d-1" data-rv><span class="step__n" aria-hidden="true">2</span><h3>Probestyling</h3><p>Etwa zwei bis drei Monate vorher. Bringen Sie Haarschmuck, ein Foto vom Kleid und gern Ihre Trauzeugin mit.</p></div>
|
||||||
|
<div class="step d-2" data-rv><span class="step__n" aria-hidden="true">3</span><h3>Der Morgen</h3><p>Wir planen rückwärts von Ihrer Uhrzeit, mit Puffer. Sie bekommen vorher einen Zeitplan für alle, die gestylt werden.</p></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="steps-wrap">
|
</section>
|
||||||
<Steps steps={[
|
|
||||||
{ title: 'Beratung', text: 'Wir sprechen über Kleid, Stil und Haar und schauen uns Ihre Inspirationsbilder an. Dabei klären wir auch, ob wir am Hochzeitstag zu Ihnen kommen.' },
|
<!-- Galerie -->
|
||||||
{ title: 'Probetermin', text: 'Wir setzen die Frisur einmal komplett um, auf Wunsch mit Make-up. Was nicht passt, ändern wir an diesem Termin, damit am Morgen der Hochzeit alles sitzt.' },
|
<section class="sec-b" aria-labelledby="gal-h">
|
||||||
{ title: 'Der Hochzeitstag', text: 'Im Salon oder vor Ort: Wir planen die Zeit so, dass Braut und Begleitung fertig sind, bevor es losgeht.' },
|
<div class="r3">
|
||||||
]} />
|
<div class="ab"><h2 class="h3" id="gal-h" data-rv>Bräute aus dem Salon</h2></div>
|
||||||
|
<div class="c"><p class="muted maxw-m" data-rv>Alle Fotos zeigen echte Kundinnen. Mehr finden Sie auf Instagram.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="pad mt-6">
|
||||||
|
<div class="gal gal--4">
|
||||||
|
{gallery.map((g, i) => <figure class={`fig r-3x4 d-${(i % 4) + 1}`} data-rv><Pic src={g.src} alt={g.alt} sizes="(min-width: 64rem) 22vw, (min-width: 40rem) 32vw, 50vw" widths={[360, 540, 675]} /></figure>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section" aria-labelledby="galerie-h">
|
{brautReview && (
|
||||||
<div class="wrap">
|
<section class="sec-b" aria-label="Stimme einer Kundin">
|
||||||
<div class="head">
|
<div class="r3">
|
||||||
<p class="label">Galerie</p>
|
<div class="a"><span class="cap">Google-Bewertung</span></div>
|
||||||
<h2 id="galerie-h">Brautfrisuren aus <em>unserem Salon.</em></h2>
|
<div class="bc"><p class="owner__q" data-rv>„{brautReview.q}“</p></div>
|
||||||
<p class="leise">Die Fotos entstehen beim Shooting mit dem Team. Bis dahin zeigen diese Flächen, welche Motive hier stehen werden.</p>
|
|
||||||
</div>
|
|
||||||
<div class="gal">
|
|
||||||
<Shot class="gal__a" title="Hochsteckfrisur" detail="Rückenansicht mit Schleier" ratio="3/4" arch />
|
|
||||||
<div class="gal__r">
|
|
||||||
<Shot title="Offene Wellen" detail="Halbseitlich, Haarband" ratio="1/1" />
|
|
||||||
<Shot title="Detail" detail="Haarschmuck und Flechtwerk, Makro" ratio="1/1" />
|
|
||||||
<Shot class="gal__c" title="Probetermin" detail="Braut vor dem Spiegel, Team im Hintergrund" ratio="3/2" tone="petrol" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
)}
|
||||||
|
|
||||||
<Reels title="Vom Probetermin bis zum großen Tag" items={[
|
<!-- Preise -->
|
||||||
{ title: 'Eine Brautfrisur entsteht', detail: 'Zeitraffer, 20 Sekunden' },
|
<section class="sec-b" id="preise" aria-label="Preise Brautservice">
|
||||||
{ title: 'Haarschmuck einsetzen', detail: 'Nahaufnahme Hände' },
|
<div class="pad"><H text="Preise" /></div>
|
||||||
{ title: 'Styling vor Ort', detail: 'Morgen der Hochzeit, Location' },
|
<div class="plist mt-7"><PriceGroup group={group} n="001" /></div>
|
||||||
{ title: 'Begleitung', detail: 'Trauzeugin und Brautmutter' },
|
<div class="r3 mt-6">
|
||||||
]} />
|
<div class="ab"></div>
|
||||||
|
<div class="c note note--wash" data-rv>
|
||||||
<section class="section" aria-labelledby="preise-h">
|
<h3>Gut zu wissen</h3>
|
||||||
<div class="wrap split">
|
<ul class="ticks">
|
||||||
<div class="head">
|
<li>Das Probestyling sichert Ihr Datum und wird beim Hochzeitstermin verrechnet.</li>
|
||||||
<p class="label">Preise</p>
|
<li>Haarschmuck bringen Sie mit, auf Wunsch beraten wir beim Kauf.</li>
|
||||||
<h2 id="preise-h">Was es <em>kostet.</em></h2>
|
<li>Absagen bis acht Wochen vorher sind kostenfrei, danach berechnen wir die Hälfte.</li>
|
||||||
<p>Damit Sie vorher wissen, womit Sie rechnen können: Das sind unsere Richtwerte. Den genauen Preis nennen wir nach der Beratung.</p>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<PriceList ids={['braut']} headingLevel={3} compact />
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Faq items={faq} />
|
<Faq items={faq} />
|
||||||
|
<Closing title="Ihr Datum zuerst" text="Schicken Sie uns Datum, Uhrzeit und Ort. Wir melden uns innerhalb von zwei Werktagen und sagen Ihnen, ob es passt." anliegen="braut" cta="Datum anfragen" />
|
||||||
<CtaBand
|
|
||||||
title="Wann und wo wird geheiratet?"
|
|
||||||
text="Schicken Sie uns Datum, Ort und die Zahl der Personen, gern mit Inspirationsfotos. Wir rufen Sie zurück und planen Beratung, Probetermin und Hochzeitstag mit Ihnen."
|
|
||||||
anliegen="braut"
|
|
||||||
button="Brautstyling anfragen"
|
|
||||||
/>
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.steps-wrap { margin-top: var(--s-8); }
|
|
||||||
.gal { margin-top: var(--s-7); display: grid; gap: var(--s-4); }
|
|
||||||
.gal__r { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); }
|
|
||||||
.gal__r :global(.gal__c) { grid-column: 1 / -1; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.gal { grid-template-columns: 5fr 7fr; gap: var(--s-5); align-items: end; }
|
|
||||||
.gal__r { gap: var(--s-5); }
|
|
||||||
}
|
|
||||||
.split { display: grid; gap: var(--s-7); }
|
|
||||||
@media (min-width: 60rem) { .split { grid-template-columns: 1fr 1.2fr; gap: var(--s-9); align-items: start; } }
|
|
||||||
</style>
|
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import DraftNote from '../components/DraftNote.astro';
|
|
||||||
import { salon } from '../data/site';
|
import { salon } from '../data/site';
|
||||||
---
|
---
|
||||||
<Base title="Datenschutz" description="Datenschutzerklärung des Friseur-Team VK in Tostedt: welche Daten die Website und das Anfrageformular verarbeiten." crumbs={[{ name: 'Datenschutz', href: '/datenschutz' }]}>
|
<Base title="Datenschutz" description="Datenschutzerklärung des Friseur-Team VK in Tostedt: welche Daten die Website und das Anfrageformular verarbeiten." crumbs={[{ name: 'Datenschutz', href: '/datenschutz' }]}>
|
||||||
<section class="section legal">
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
<div class="wrap">
|
<div class="pad">
|
||||||
<p class="label">Rechtliches</p>
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ Datenschutz</span></p>
|
||||||
<h1>Datenschutz</h1>
|
<h1 id="ph-h" class="ph__title is-mid"><span class="ini">D</span>atenschutz</h1>
|
||||||
<DraftNote text="Entwurf, der beschreibt, was diese Website technisch tatsächlich tut. Er ersetzt keine Rechtsberatung und muss vor dem Livegang geprüft und mit dem Rechtstexte-Dienst des Salons abgeglichen werden. Dazu gehört auch der Vertrag zur Auftragsverarbeitung mit Heidrich Digital." />
|
</div>
|
||||||
<div class="prose">
|
<div class="r3 ph__grid">
|
||||||
|
<div class="ab"><p class="note small">Entwurf, der beschreibt, was diese Website technisch tatsächlich tut. Er ersetzt keine Rechtsberatung und muss vor dem Livegang geprüft und mit dem Rechtstexte-Dienst des Salons abgeglichen werden. Dazu gehört auch der Vertrag zur Auftragsverarbeitung mit Heidrich Digital.</p></div>
|
||||||
|
<div class="c prose">
|
||||||
<h2>Verantwortlich</h2>
|
<h2>Verantwortlich</h2>
|
||||||
<p>{salon.legalName}, Inhaberin {salon.owner}<br />{salon.street}, {salon.zip} {salon.city}<br />Telefon {salon.phoneIntl}, E-Mail <a href={`mailto:${salon.email}`}>{salon.email}</a></p>
|
<p>{salon.legalName}, Inhaberin {salon.owner}<br />{salon.street}, {salon.zip} {salon.city}<br />Telefon {salon.phoneIntl}, E-Mail <a href={`mailto:${salon.email}`}>{salon.email}</a></p>
|
||||||
|
|
||||||
@@ -37,12 +38,8 @@ import { salon } from '../data/site';
|
|||||||
<h2>Ihre Rechte</h2>
|
<h2>Ihre Rechte</h2>
|
||||||
<p>Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung, Datenübertragbarkeit und Widerspruch. Eine Einwilligung können Sie jederzeit für die Zukunft widerrufen. Schreiben Sie dazu an <a href={`mailto:${salon.email}`}>{salon.email}</a>. Außerdem können Sie sich bei einer Datenschutz-Aufsichtsbehörde beschweren, in Niedersachsen bei der Landesbeauftragten für den Datenschutz Niedersachsen.</p>
|
<p>Sie haben das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung, Datenübertragbarkeit und Widerspruch. Eine Einwilligung können Sie jederzeit für die Zukunft widerrufen. Schreiben Sie dazu an <a href={`mailto:${salon.email}`}>{salon.email}</a>. Außerdem können Sie sich bei einer Datenschutz-Aufsichtsbehörde beschweren, in Niedersachsen bei der Landesbeauftragten für den Datenschutz Niedersachsen.</p>
|
||||||
|
|
||||||
<p class="small leise">Stand: Entwurf Oktober 2026</p>
|
<p class="cap">Stand: Entwurf Oktober 2026</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
<style>
|
|
||||||
.legal .wrap { display: grid; gap: var(--s-6); }
|
|
||||||
.legal h1 { font-size: var(--fs-h2); }
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHead from '../components/PageHead.astro';
|
||||||
|
import Pic from '../components/Pic.astro';
|
||||||
|
import H from '../components/H.astro';
|
||||||
|
import Closing from '../components/Closing.astro';
|
||||||
|
import { salon } from '../data/site';
|
||||||
|
import produkte from '../assets/img/produkte-redken.jpg';
|
||||||
|
|
||||||
|
const ideas = [
|
||||||
|
{ n: '001', t: 'Wertgutschein', d: 'Ab 20 €, frei wählbarer Betrag. Gilt für alle Leistungen und Produkte.' },
|
||||||
|
{ n: '002', t: 'Glossing und Pflege', d: 'Glanz und Pflege für die Längen, eine schöne Kleinigkeit zwischendurch.' },
|
||||||
|
{ n: '003', t: 'Farbberatung', d: 'Für alle, die über eine neue Farbe nachdenken. Wird beim Termin verrechnet.' },
|
||||||
|
{ n: '004', t: 'Braut-Probestyling', d: 'Das Geschenk von der Trauzeugin. Wird bei Buchung der Brautfrisur verrechnet.' },
|
||||||
|
];
|
||||||
|
const wa = `${salon.whatsappHref}?text=${encodeURIComponent('Hallo, ich möchte gern einen Gutschein bestellen.')}`;
|
||||||
|
---
|
||||||
|
<Base
|
||||||
|
title="Gutscheine"
|
||||||
|
description="Gutscheine vom Friseur-Team VK in Tostedt: Wertgutschein ab 20 €, Glossing, Farbberatung oder Braut-Probestyling. Bestellen per Telefon oder WhatsApp, abholen am Empfang."
|
||||||
|
crumbs={[{ name: 'Gutscheine', href: '/gutscheine' }]}
|
||||||
|
>
|
||||||
|
<PageHead crumb="Gutscheine" title="Gutscheine" sub="zum Verschenken">
|
||||||
|
<p class="lead" data-rv>Ein Termin bei uns als Geschenk. Bestellen Sie per Telefon oder WhatsApp, wir legen den Gutschein am Empfang für Sie bereit.</p>
|
||||||
|
<div class="row" data-rv><a class="oval oval--fill" href={wa} rel="noopener">Per WhatsApp bestellen</a><a class="go" href={salon.phoneHref}>{salon.phoneDisplay}</a></div>
|
||||||
|
<figure class="fig r-4x3 mt-4" data-clip><Pic src={produkte} alt="Pflegeprodukte im Regal am Empfang" sizes="(min-width: 56rem) 55vw, 100vw" eager /></figure>
|
||||||
|
</PageHead>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="g-h">
|
||||||
|
<div class="pad"><H text="Ideen" /></div>
|
||||||
|
<div class="rows mt-7">
|
||||||
|
{ideas.map((x) => (
|
||||||
|
<div class="srow" data-rv>
|
||||||
|
<span class="srow__n">{x.n}</span>
|
||||||
|
<h3 class="srow__t">{x.t}</h3>
|
||||||
|
<span class="srow__d"><span>{x.d}</span></span>
|
||||||
|
<span></span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p class="cap pad from-c mt-4">Gutscheine gelten drei Jahre ab Ende des Kaufjahres.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Closing title="Schnell bestellt" text="Sagen Sie uns Betrag oder Leistung und für wen der Gutschein ist. Abholen können Sie ihn zu unseren Öffnungszeiten." phoneFirst />
|
||||||
|
</Base>
|
||||||
@@ -1,151 +1,118 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import PageHero from '../components/PageHero.astro';
|
import PageHead from '../components/PageHead.astro';
|
||||||
import Shot from '../components/Shot.astro';
|
|
||||||
import Pic from '../components/Pic.astro';
|
import Pic from '../components/Pic.astro';
|
||||||
import Steps from '../components/Steps.astro';
|
import H from '../components/H.astro';
|
||||||
import Reels from '../components/Reels.astro';
|
import PriceGroup from '../components/PriceGroup.astro';
|
||||||
import Faq from '../components/Faq.astro';
|
import Faq from '../components/Faq.astro';
|
||||||
import PriceList from '../components/PriceList.astro';
|
import Closing from '../components/Closing.astro';
|
||||||
import CtaBand from '../components/CtaBand.astro';
|
import { priceGroups, waits, salon, faqSchema, serviceSchema } from '../data/site';
|
||||||
import Icon from '../components/Icon.astro';
|
|
||||||
import { SITE_URL } from '../data/site';
|
|
||||||
import greatLengths from '../assets/img/great-lengths.jpg';
|
|
||||||
|
|
||||||
|
import gl from '../assets/img/great-lengths.jpg';
|
||||||
|
import l1V from '../assets/img/arbeiten/gl-laenge-vorher-1.jpg';
|
||||||
|
import l1N from '../assets/img/arbeiten/gl-laenge-nachher-1.jpg';
|
||||||
|
import l2V from '../assets/img/arbeiten/gl-laenge-vorher-2.jpg';
|
||||||
|
import l2N from '../assets/img/arbeiten/gl-laenge-nachher-2.jpg';
|
||||||
|
import f1V from '../assets/img/arbeiten/gl-fuelle-vorher-1.jpg';
|
||||||
|
import f1N from '../assets/img/arbeiten/gl-fuelle-nachher-1.jpg';
|
||||||
|
import f2V from '../assets/img/arbeiten/gl-fuelle-vorher-2.jpg';
|
||||||
|
import f2N from '../assets/img/arbeiten/gl-fuelle-nachher-2.jpg';
|
||||||
|
import f3V from '../assets/img/arbeiten/gl-fuelle-vorher-3.jpg';
|
||||||
|
import f3N from '../assets/img/arbeiten/gl-fuelle-nachher-3.jpg';
|
||||||
|
|
||||||
|
const group = priceGroups.find((g) => g.id === 'verlaengerung')!;
|
||||||
|
const cases = [
|
||||||
|
{ n: '001', title: 'Länge', v: l2V, nn: l2N, altV: 'Schulterlanges dunkles Haar vorher', alt: 'Langes dunkles Haar nach der Verlängerung', facts: [['Ausgangslage', 'schulterlang, dunkel'], ['Ergebnis', 'gut 20 cm mehr Länge'], ['Richtwert', 'ab 650 €']] },
|
||||||
|
{ n: '002', title: 'Fülle', v: f2V, nn: f2N, altV: 'Feines, glattes Haar vorher', alt: 'Volle Wellen nach der Verdichtung', facts: [['Ausgangslage', 'fein, dünne Spitzen'], ['Ergebnis', 'gleiche Länge, deutlich mehr Volumen'], ['Richtwert', 'ab 350 €']] },
|
||||||
|
{ n: '003', title: 'Länge und Fülle', v: l1V, nn: l1N, altV: 'Kinnlanges Haar vorher', alt: 'Langes, glattes Haar nachher', facts: [['Ausgangslage', 'kurz bis kinnlang'], ['Ergebnis', 'lang und gleichmäßig dicht'], ['Richtwert', 'nach Beratung']] },
|
||||||
|
];
|
||||||
|
const more = [
|
||||||
|
{ v: f1V, nn: f1N, alt: 'Verdichtung, dunkles Haar' },
|
||||||
|
{ v: f3V, nn: f3N, alt: 'Verdichtung, glattes dunkles Haar' },
|
||||||
|
];
|
||||||
const faq = [
|
const faq = [
|
||||||
{ q: 'Was ist der Unterschied zwischen Verlängerung und Verdichtung?', a: 'Bei der Verlängerung werden Strähnen so eingesetzt, dass das Haar länger wird. Bei der Verdichtung bleibt die Länge, das Haar wird voller. Beides geht mit Great Lengths und auch kombiniert.' },
|
{ q: 'Was kostet eine Haarverlängerung mit Great Lengths?', a: 'Eine Verdichtung beginnt bei uns ab 350 €, eine Verlängerung ab 650 €. Der Preis hängt von der Menge, der Länge und dem System ab. Nach der kostenlosen Beratung bekommen Sie einen festen Preis, bevor wir das Haar bestellen.' },
|
||||||
{ q: 'Warum gibt es vorher einen Beratungstermin?', a: 'Weil Haarstruktur, Farbton und Pflege zusammenpassen müssen. In der Beratung sehen wir uns Ihr Haar an, wählen Farbe und Menge der Strähnen aus und erklären, wie Sie die Verlängerung zu Hause pflegen.' },
|
{ q: 'Wie lange hält das?', a: 'Je nach System und Pflege drei bis sechs Monate. Danach setzen wir die Strähnen nach. Gute Great-Lengths-Strähnen lassen sich meist mehrfach verwenden.' },
|
||||||
{ q: 'Wie lange hält eine Haarverlängerung?', a: 'Das hängt von Ihrem Haarwachstum und der Pflege ab. Nach einigen Monaten setzen wir die Strähnen nach, den Zeitpunkt besprechen wir bei der Beratung.' },
|
{ q: 'Sieht man die Übergänge?', a: 'Bei richtig gesetzten Strähnen nicht. Wir wählen Farbe und Struktur passend zu Ihrem Haar aus und schneiden die Strähnen nach dem Einsetzen ein.' },
|
||||||
{ q: 'Was kostet das?', a: 'Eine Verdichtung beginnt bei 350 Euro, eine Verlängerung bei 650 Euro (Richtwerte). Der Preis hängt vor allem von der Anzahl der Strähnen ab. Nach der Beratung bekommen Sie einen genauen Preis, bevor etwas bestellt wird.' },
|
{ q: 'Geht das auch bei sehr feinem Haar?', a: 'Oft ja, dann meist als Verdichtung mit kleineren Strähnen oder Tressen. Ob es passt, sehen wir in der Beratung.' },
|
||||||
|
{ q: 'Welche Systeme bieten Sie an?', a: 'Laut Great-Lengths-Partnerverzeichnis arbeiten wir mit GL PreBonded (Bondings), Ultrasonic touch und GL Weft Linea (Tressen). Welches System passt, entscheiden wir gemeinsam in der Beratung.' },
|
||||||
];
|
];
|
||||||
const faqSchema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'FAQPage',
|
|
||||||
mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })),
|
|
||||||
};
|
|
||||||
const service = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'Service',
|
|
||||||
name: 'Haarverlängerung und Haarverdichtung mit Great Lengths',
|
|
||||||
serviceType: 'Haarverlängerung',
|
|
||||||
brand: { '@type': 'Brand', name: 'Great Lengths' },
|
|
||||||
provider: { '@id': `${SITE_URL}/#salon` },
|
|
||||||
areaServed: 'Tostedt und Umgebung',
|
|
||||||
};
|
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Haarverlängerung und Haarverdichtung mit Great Lengths in Tostedt"
|
title="Great Lengths Haarverlängerung und Haarverdichtung in Tostedt"
|
||||||
description="Haarverlängerung und Haarverdichtung mit Great Lengths beim Friseur-Team VK in Tostedt. Mit Beratungstermin, transparenten Richtpreisen und Nachpflege."
|
description="Master of Great Lengths in Tostedt: Haarverlängerung und Haarverdichtung mit Echthaar, kostenlose Beratung. Verdichtung ab 350 €, Verlängerung ab 650 €. Für Tostedt, Buchholz und den Landkreis Harburg."
|
||||||
crumbs={[{ name: 'Haarverlängerung', href: '/haarverlaengerung' }]}
|
crumbs={[{ name: 'Great Lengths', href: '/haarverlaengerung' }]}
|
||||||
schema={[service, faqSchema]}
|
schema={[faqSchema(faq), serviceSchema('Great Lengths Haarverlängerung und Haarverdichtung', 'Haarverlängerung und Haarverdichtung mit Great Lengths Echthaar, durch einen Master of Great Lengths Salon in Tostedt.', 350)]}
|
||||||
>
|
>
|
||||||
<PageHero
|
<PageHead crumb="Great Lengths" title="Great Lengths" sub="Länge und Fülle">
|
||||||
label="Haarverlängerung und -verdichtung"
|
<p class="lead" data-rv>Mehr Länge oder mehr Fülle mit Echthaar von Great Lengths. Wir tragen die Auszeichnung Master of Great Lengths und beraten Sie vorher kostenlos.</p>
|
||||||
title="Mehr Länge, <em>mehr Fülle.</em>"
|
<div class="row" data-rv><a class="oval oval--fill" href="/anfrage?anliegen=verlaengerung">Kostenlose Beratung</a><a class="go" href={salon.greatLengths} rel="noopener">Unser Eintrag bei Great Lengths</a></div>
|
||||||
lead="Wir arbeiten mit Great Lengths. Bevor eine Strähne eingesetzt wird, sehen wir uns Ihr Haar in einem Beratungstermin genau an und besprechen Farbe, Menge und Pflege."
|
<p class="cap">{waits.items[1].value}</p>
|
||||||
>
|
<div class="pair mt-4">
|
||||||
<div slot="actions" class="btn-row">
|
<figure class="fig r-3x4" data-clip><Pic src={l2V} alt="Vorher: schulterlanges dunkles Haar" sizes="(min-width: 56rem) 27vw, 50vw" widths={[360, 540, 675]} eager /><span class="tag">Vorher</span></figure>
|
||||||
<a class="btn" href="/anfrage?anliegen=verlaengerung">Beratung anfragen<Icon name="arrow" /></a>
|
<figure class="fig r-3x4" data-clip><Pic src={l2N} alt="Nachher: langes dunkles Haar nach der Verlängerung" sizes="(min-width: 56rem) 27vw, 50vw" widths={[360, 540, 675]} eager /><span class="tag">Nachher</span></figure>
|
||||||
</div>
|
</div>
|
||||||
<div slot="media" class="frame arch ratio-45">
|
</PageHead>
|
||||||
<Pic src={greatLengths} eager alt="Great Lengths Schriftzug auf glänzendem, blondem Echthaar" sizes="(min-width: 60rem) 38vw, 88vw" widths={[400, 640, 900, 1080]} />
|
|
||||||
</div>
|
|
||||||
</PageHero>
|
|
||||||
|
|
||||||
<section class="section section--nebel" aria-labelledby="zwei-h">
|
<section class="sec-b" aria-labelledby="w-h">
|
||||||
<div class="wrap">
|
<div class="r3">
|
||||||
<div class="head">
|
<div class="ab gap-5">
|
||||||
<p class="label">Zwei Möglichkeiten</p>
|
<H text="Warum Great Lengths" />
|
||||||
<h2 id="zwei-h">Verlängern oder <em>verdichten.</em></h2>
|
<figure class="fig r-4x3" data-clip><Pic src={gl} alt="Great Lengths Echthaarsträhnen" sizes="(min-width: 56rem) 36vw, 100vw" /></figure>
|
||||||
</div>
|
</div>
|
||||||
<div class="two">
|
<div class="c copy gap-5 self-end" data-rv>
|
||||||
<article class="two__item reveal">
|
<p>Great Lengths verarbeitet Echthaar, das sich färben, föhnen und locken lässt wie Ihr eigenes. Die Strähnen werden einzeln gesetzt, so fein, dass man sie beim Tragen nicht sieht.</p>
|
||||||
<Shot title="Verlängerung, Ergebnis" detail="Schulterlang zu rückenlang, Seitenansicht" ratio="3/2" />
|
<p>Als Master of Great Lengths haben wir die Schulungen des Herstellers durchlaufen und setzen Verlängerungen regelmäßig. Im Partnerverzeichnis sind wir unter Tostedt eingetragen.</p>
|
||||||
<h3>Haarverlängerung</h3>
|
<ul class="ticks">
|
||||||
<p>Für mehr Länge, zum Beispiel nach einem zu kurzen Schnitt oder für einen besonderen Anlass. Die Strähnen werden farblich an Ihr Haar angepasst.</p>
|
<li>Beratung mit Haaranalyse, kostenlos und unverbindlich</li>
|
||||||
<p class="num two__p">ab 650 €<sup>*</sup></p>
|
<li>Fester Preis, bevor wir das Haar bestellen</li>
|
||||||
</article>
|
<li>Pflegetermin und Nachsetzen bei uns im Salon</li>
|
||||||
<article class="two__item reveal">
|
|
||||||
<Shot title="Verdichtung, Ergebnis" detail="Feines Haar, vorher und nachher von oben" ratio="3/2" tone="petrol" />
|
|
||||||
<h3>Haarverdichtung</h3>
|
|
||||||
<p>Für mehr Fülle bei gleicher Länge, etwa bei feinem Haar oder lichten Stellen. Oft reichen dafür weniger Strähnen als bei einer Verlängerung.</p>
|
|
||||||
<p class="num two__p">ab 350 €<sup>*</sup></p>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" aria-labelledby="ablauf-h">
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="head">
|
|
||||||
<p class="label">Ablauf</p>
|
|
||||||
<h2 id="ablauf-h">Von der Beratung <em>bis zur Nachpflege.</em></h2>
|
|
||||||
</div>
|
|
||||||
<div class="steps-wrap">
|
|
||||||
<Steps steps={[
|
|
||||||
{ title: 'Beratung', text: 'Wir sehen uns Ihr Haar an, bestimmen den Farbton und die Menge der Strähnen. Danach kennen Sie den Preis.' },
|
|
||||||
{ title: 'Einarbeitung', text: 'Die Strähnen werden bestellt und an einem eigenen Termin eingesetzt. Danach schneiden wir alles in Form.' },
|
|
||||||
{ title: 'Pflege und Nachsetzen', text: 'Sie bekommen eine Pflegeanleitung für zu Hause. Wenn das Haar nachgewachsen ist, setzen wir die Strähnen neu.' },
|
|
||||||
]} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section--tight" aria-labelledby="pflege-h">
|
|
||||||
<div class="wrap pflege">
|
|
||||||
<p class="label">Pflege zu Hause</p>
|
|
||||||
<h2 id="pflege-h" class="pflege__h">Damit die Verlängerung lange schön bleibt.</h2>
|
|
||||||
<ul class="pflege__list">
|
|
||||||
<li>Vor dem Waschen gründlich durchbürsten, am besten mit einer weichen Bürste für Extensions.</li>
|
|
||||||
<li>Die Ansätze nicht mit Spülung oder Öl einarbeiten.</li>
|
|
||||||
<li>Nachts locker zu einem Zopf flechten.</li>
|
|
||||||
<li>Föhnen und Glätten mit Hitzeschutz und nicht direkt an den Verbindungen.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<p class="small leise">Welche Produkte zu Ihrem Haar passen, erklären wir beim Termin.</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Reels title="Great Lengths im Salon" items={[
|
<section class="sec-b" aria-labelledby="e-h">
|
||||||
{ title: 'Verdichtung, vorher und nachher', detail: 'Draufsicht, Tageslicht' },
|
<div class="pad"><H text="Vorher, nachher" /></div>
|
||||||
{ title: 'Farbabgleich der Strähnen', detail: 'Nahaufnahme am Ansatz' },
|
<div class="cases mt-7">
|
||||||
{ title: 'Ergebnis in Bewegung', detail: 'Haare werden ausgeschüttelt' },
|
{cases.slice(1).map((c) => (
|
||||||
]} />
|
<article class="case">
|
||||||
|
<span class="cap case__n">{c.n}</span>
|
||||||
|
<div class="case__meta" data-rv>
|
||||||
|
<h3 class="h3">{c.title}</h3>
|
||||||
|
<dl class="facts">{c.facts.map(([k, v]) => <><dt>{k}</dt><dd>{v}</dd></>)}</dl>
|
||||||
|
</div>
|
||||||
|
<div class="case__pics">
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.v} alt={c.altV} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Vorher</span></figure>
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.nn} alt={c.alt} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Nachher</span></figure>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div class="pad mt-7"><div class="gal gal--4">
|
||||||
|
{more.flatMap((m) => [
|
||||||
|
<figure class="fig r-3x4" data-rv><Pic src={m.v} alt={`Vorher: ${m.alt}`} sizes="(min-width: 64rem) 22vw, 50vw" widths={[360, 540]} /><span class="tag">Vorher</span></figure>,
|
||||||
|
<figure class="fig r-3x4 d-1" data-rv><Pic src={m.nn} alt={`Nachher: ${m.alt}`} sizes="(min-width: 64rem) 22vw, 50vw" widths={[360, 540]} /><span class="tag">Nachher</span></figure>,
|
||||||
|
])}
|
||||||
|
</div></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section class="section" aria-labelledby="preise-h">
|
<section class="sec-b" aria-labelledby="s-h">
|
||||||
<div class="wrap split">
|
<div class="pad"><H text="In drei Schritten" /></div>
|
||||||
<div class="head">
|
<div class="steps mt-7">
|
||||||
<p class="label">Preise</p>
|
<p class="steps__lead">Von der Beratung<br />bis zur Pflege</p>
|
||||||
<h2 id="preise-h">Was es <em>kostet.</em></h2>
|
<div class="step" data-rv><span class="step__n" aria-hidden="true">1</span><h3>Vorab-Check</h3><p>Fotos von hinten und vom Scheitel, Ihr Wunsch und ein Budgetrahmen. So wissen wir vor dem Termin, was möglich ist.</p></div>
|
||||||
<p>Der größte Posten sind die Strähnen selbst. Deshalb nennen wir den genauen Preis erst nach der Beratung, wenn Menge und Länge feststehen.</p>
|
<div class="step d-1" data-rv><span class="step__n" aria-hidden="true">2</span><h3>Beratung im Salon</h3><p>Haaranalyse, Farbabgleich, System und fester Preis. Kostenlos, rund 30 Minuten.</p></div>
|
||||||
</div>
|
<div class="step d-2" data-rv><span class="step__n" aria-hidden="true">3</span><h3>Einsetzen</h3><p>Das Haar wird für Sie bestellt. Beim Termin setzen wir die Strähnen und schneiden alles zusammen.</p></div>
|
||||||
<PriceList ids={['verlaengerung']} headingLevel={3} compact />
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="cap pad from-c mt-4">Danach kommen Sie alle drei bis sechs Monate zum Nachsetzen. Mit der Bestellung des Haares wird eine Anzahlung fällig, die verrechnet wird.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" id="preise" aria-label="Preise Great Lengths">
|
||||||
|
<div class="pad"><H text="Preise" /></div>
|
||||||
|
<div class="plist mt-7"><PriceGroup group={group} n="001" /></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Faq items={faq} />
|
<Faq items={faq} />
|
||||||
|
<Closing title="Erst beraten, dann bestellen" text="Schicken Sie uns zwei Fotos (von hinten und vom Scheitel) und Ihren Wunsch. Wir melden uns und schlagen einen Termin für die kostenlose Beratung vor." anliegen="verlaengerung" cta="Beratung anfragen" />
|
||||||
<CtaBand
|
|
||||||
title="Beratungstermin anfragen."
|
|
||||||
text="Schicken Sie uns ein Foto Ihrer Haare von hinten und schreiben Sie, ob Sie mehr Länge oder mehr Fülle möchten. Wir rufen Sie zurück und vereinbaren die Beratung."
|
|
||||||
anliegen="verlaengerung"
|
|
||||||
button="Beratung anfragen"
|
|
||||||
/>
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.two { margin-top: var(--s-7); display: grid; gap: var(--s-8); }
|
|
||||||
@media (min-width: 52rem) { .two { grid-template-columns: 1fr 1fr; gap: var(--s-7); } .two__item:nth-child(2) { margin-top: var(--s-9); } }
|
|
||||||
.two__item { display: grid; gap: var(--s-3); align-content: start; }
|
|
||||||
.two__item h3 { margin-top: var(--s-4); }
|
|
||||||
.two__item p { max-width: 44ch; }
|
|
||||||
.two__p { font-weight: 600; }
|
|
||||||
sup { font-size: 0.75em; color: var(--c-text-leise); }
|
|
||||||
.steps-wrap { margin-top: var(--s-8); }
|
|
||||||
.pflege { max-width: 52rem; }
|
|
||||||
.pflege__h { font-size: var(--fs-h3); margin-top: var(--s-3); }
|
|
||||||
.pflege__list { margin-block: var(--s-5); padding: 0; list-style: none; counter-reset: p; }
|
|
||||||
.pflege__list li { counter-increment: p; display: grid; grid-template-columns: 2.5rem 1fr; padding-block: var(--s-3); border-bottom: var(--hair); }
|
|
||||||
.pflege__list li::before { content: counter(p); font-family: var(--font-display); font-style: italic; color: var(--c-petrol); font-size: 1.25rem; line-height: 1.3; }
|
|
||||||
.split { display: grid; gap: var(--s-7); }
|
|
||||||
@media (min-width: 60rem) { .split { grid-template-columns: 1fr 1.2fr; gap: var(--s-9); align-items: start; } }
|
|
||||||
</style>
|
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import DraftNote from '../components/DraftNote.astro';
|
|
||||||
import { salon } from '../data/site';
|
import { salon } from '../data/site';
|
||||||
---
|
---
|
||||||
<Base title="Impressum" description="Impressum des Friseur-Team VK Veronika Sprangel, Kastanienallee 1, 21255 Tostedt." crumbs={[{ name: 'Impressum', href: '/impressum' }]}>
|
<Base title="Impressum" description="Impressum des Friseur-Team VK Veronika Sprangel, Kastanienallee 1, 21255 Tostedt." crumbs={[{ name: 'Impressum', href: '/impressum' }]}>
|
||||||
<section class="section legal">
|
<section class="ph" aria-labelledby="ph-h">
|
||||||
<div class="wrap">
|
<div class="pad">
|
||||||
<p class="label">Rechtliches</p>
|
<p class="ph__crumb"><a href="/">Start</a> <span>/ Impressum</span></p>
|
||||||
<h1>Impressum</h1>
|
<h1 id="ph-h" class="ph__title is-mid"><span class="ini">I</span>mpressum</h1>
|
||||||
<DraftNote text="Dieser Text ist ein ehrlicher Entwurf auf Basis der bisherigen Website. Er muss vor dem Livegang vom Salon geprüft werden, idealerweise über einen Rechtstexte-Dienst. Offene Punkte sind im Text benannt." />
|
</div>
|
||||||
<div class="prose">
|
<div class="r3 ph__grid">
|
||||||
|
<div class="ab"><p class="note small">Dieser Text ist ein ehrlicher Entwurf auf Basis der bisherigen Website. Er muss vor dem Livegang vom Salon geprüft werden, idealerweise über einen Rechtstexte-Dienst. Offene Punkte sind im Text benannt.</p></div>
|
||||||
|
<div class="c prose">
|
||||||
<h2>Angaben gemäß § 5 DDG</h2>
|
<h2>Angaben gemäß § 5 DDG</h2>
|
||||||
<p>{salon.legalName}<br />Inhaberin: {salon.owner}<br />{salon.street}<br />{salon.zip} {salon.city}</p>
|
<p>{salon.legalName}<br />Inhaberin: {salon.owner}<br />{salon.street}<br />{salon.zip} {salon.city}</p>
|
||||||
|
|
||||||
@@ -32,7 +33,7 @@ import { salon } from '../data/site';
|
|||||||
<p>Wir sind nicht bereit und nicht verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.</p>
|
<p>Wir sind nicht bereit und nicht verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.</p>
|
||||||
|
|
||||||
<h2>Bildnachweis</h2>
|
<h2>Bildnachweis</h2>
|
||||||
<p>Fotos vom Salon und vom Team: Friseur-Team VK (Fotograf oder Fotografin bitte ergänzen). Bild mit Great-Lengths-Schriftzug: Great Lengths, Nutzungsrecht bitte bestätigen.</p>
|
<p>Fotos vom Salon, vom Team und von Arbeiten: Friseur-Team VK (Fotograf oder Fotografin bitte ergänzen). Bild mit Great-Lengths-Schriftzug: Great Lengths, Nutzungsrecht bitte bestätigen.</p>
|
||||||
|
|
||||||
<h2>Umsetzung</h2>
|
<h2>Umsetzung</h2>
|
||||||
<p>Website: Heidrich Digital, Till Heidrich</p>
|
<p>Website: Heidrich Digital, Till Heidrich</p>
|
||||||
@@ -40,7 +41,3 @@ import { salon } from '../data/site';
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
<style>
|
|
||||||
.legal .wrap { display: grid; gap: var(--s-6); }
|
|
||||||
.legal h1 { font-size: var(--fs-h2); }
|
|
||||||
</style>
|
|
||||||
@@ -1,309 +1,294 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import Pic from '../components/Pic.astro';
|
import Pic from '../components/Pic.astro';
|
||||||
import Shot from '../components/Shot.astro';
|
import H from '../components/H.astro';
|
||||||
import Icon from '../components/Icon.astro';
|
import { salon, waits, reviews, priceGroups, fromPrice } from '../data/site';
|
||||||
import Rating from '../components/Rating.astro';
|
|
||||||
import Reels from '../components/Reels.astro';
|
|
||||||
import Hours from '../components/Hours.astro';
|
|
||||||
import CtaBand from '../components/CtaBand.astro';
|
|
||||||
import LoyaltyCard from '../components/LoyaltyCard.astro';
|
|
||||||
import { salon, eur } from '../data/site';
|
|
||||||
import salonInnen1 from '../assets/img/salon-innen-1.jpg';
|
import salonInnen1 from '../assets/img/salon-innen-1.jpg';
|
||||||
import salonInnen4 from '../assets/img/salon-innen-4.jpg';
|
import salonInnen4 from '../assets/img/salon-innen-4.jpg';
|
||||||
import salonEmpfang from '../assets/img/salon-empfang.jpg';
|
import salonEmpfang from '../assets/img/salon-empfang.jpg';
|
||||||
|
import salonWarte from '../assets/img/salon-wartebereich.jpg';
|
||||||
import teamFoto from '../assets/img/team-gruppenfoto.jpg';
|
import teamFoto from '../assets/img/team-gruppenfoto.jpg';
|
||||||
import greatLengths from '../assets/img/great-lengths.jpg';
|
import vero from '../assets/img/team/veronika.jpg';
|
||||||
|
import marco from '../assets/img/team/marco.jpg';
|
||||||
|
import brautHero from '../assets/img/arbeiten/braut-wellen-bluetenspangen.jpg';
|
||||||
|
import braut1 from '../assets/img/arbeiten/braut-chignon-blueten.jpg';
|
||||||
|
import braut2 from '../assets/img/arbeiten/braut-halboffen-spange.jpg';
|
||||||
|
import bal1 from '../assets/img/arbeiten/balayage-beige-wellen.jpg';
|
||||||
|
import bal2 from '../assets/img/arbeiten/balayage-honigblond.jpg';
|
||||||
|
import bal3 from '../assets/img/arbeiten/balayage-karamell.jpg';
|
||||||
|
import glN from '../assets/img/arbeiten/gl-laenge-nachher-2.jpg';
|
||||||
|
import glV from '../assets/img/arbeiten/gl-laenge-vorher-2.jpg';
|
||||||
|
import fV from '../assets/img/arbeiten/gl-fuelle-vorher-2.jpg';
|
||||||
|
import fN from '../assets/img/arbeiten/gl-fuelle-nachher-2.jpg';
|
||||||
|
import cV from '../assets/img/arbeiten/farbe-vorher-blond.jpg';
|
||||||
|
import cN from '../assets/img/arbeiten/farbe-nachher-blond.jpg';
|
||||||
|
import damen1 from '../assets/img/arbeiten/schnitt-lob-wellen.jpg';
|
||||||
|
import damen2 from '../assets/img/arbeiten/schnitt-bob.jpg';
|
||||||
|
import herren1 from '../assets/img/arbeiten/barber-bart.jpg';
|
||||||
|
import herren2 from '../assets/img/arbeiten/herren-kurz-2.jpg';
|
||||||
|
|
||||||
const alltag = [
|
const g = Object.fromEntries(priceGroups.map((x) => [x.id, x]));
|
||||||
{ t: 'Damen', d: 'Schneiden, Föhnen, Hochsteckfrisuren, Dauerwelle', from: 45, href: '/leistungen-preise#preise-damen' },
|
const first = (id: string) => fromPrice(g[id].items.find((i) => i.from || i.tiers) ?? g[id].items[0]);
|
||||||
{ t: 'Herren und Barbershop', d: 'Haarschnitt, Bart trimmen, Nassrasur', from: 26, href: '/leistungen-preise#preise-herren' },
|
|
||||||
{ t: 'Kinder', d: 'Haarschnitt bis 12 Jahre', from: 18, href: '/leistungen-preise#preise-kinder' },
|
const services = [
|
||||||
{ t: 'Augenbrauen und Wimpern', d: 'Zupfen und Färben', from: 10, href: '/leistungen-preise#preise-augen' },
|
{ n: '001', title: 'Braut', em: 'Frisur und Make-up', text: 'Vom Probestyling bis zum Morgen der Hochzeit, im Salon oder bei Ihnen vor Ort.', price: `Brautfrisur ${fromPrice(g.braut.items[1])}`, href: '/brautservice', th: [braut1, braut2] },
|
||||||
|
{ n: '002', title: 'Balayage & Farbe', em: 'weich und schonend', text: 'Balayage, Foliensträhnen und Farbwechsel. Mit Paul Mitchell und Redken, Preise nach Haarlänge.', price: `Balayage ${fromPrice(g.farbe.items[1])}`, href: '/balayage-farbe', th: [bal2, bal3] },
|
||||||
|
{ n: '003', title: 'Great Lengths', em: 'Länge und Fülle', text: 'Haarverlängerung und Haarverdichtung als Master of Great Lengths. Die Beratung ist kostenlos.', price: `Verdichtung ${fromPrice(g.verlaengerung.items[1])}`, href: '/haarverlaengerung', th: [glN, fN] },
|
||||||
|
{ n: '004', title: 'Damen', em: 'Schnitt und Styling', text: 'Schneiden, Föhnen, Hochstecken, Dauerwelle. Der Termin zwischendurch.', price: first('damen'), href: '/leistungen-preise#damen', th: [damen1, damen2] },
|
||||||
|
{ n: '005', title: 'Herren & Barber', em: 'Schnitt, Bart, Rasur', text: 'Haarschnitt, Bart konturieren und Nassrasur mit heißem Tuch.', price: first('herren'), href: '/barber', th: [herren1, herren2] },
|
||||||
|
{ n: '006', title: 'Kinder, Augenbrauen & Wimpern', em: '', text: 'Kinderhaarschnitt bis 12 Jahre, Augenbrauen zupfen und färben, Wimpern färben.', price: first('kinder'), href: '/leistungen-preise#kinder', th: [] },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const cases = [
|
||||||
|
{ n: '001', title: 'Mehr Länge', where: 'Great Lengths', v: glV, nn: glN, alt: 'Langes, dunkles Haar nach der Verlängerung', altV: 'Schulterlanges Haar vor der Verlängerung', facts: [['Ausgangslage', 'schulterlang, dunkel, Naturhaar'], ['Wunsch', 'deutlich länger, natürlicher Fall'], ['Technik', 'Great Lengths Echthaar'], ['Richtwert', 'ab 650 €']] },
|
||||||
|
{ n: '002', title: 'Mehr Fülle', where: 'Great Lengths', v: fV, nn: fN, alt: 'Volle, wellige Längen nach der Verdichtung', altV: 'Feines, glattes Haar vor der Verdichtung', facts: [['Ausgangslage', 'fein, wenig Volumen in den Längen'], ['Wunsch', 'mehr Fülle, gleiche Länge'], ['Technik', 'Haarverdichtung mit Great Lengths'], ['Richtwert', 'ab 350 €']] },
|
||||||
|
{ n: '003', title: 'Neues Blond', where: 'Balayage & Farbe', v: cV, nn: cN, alt: 'Helles, kühles Blond nach der Behandlung', altV: 'Mittelbraunes Haar vor der Behandlung', facts: [['Ausgangslage', 'mittelbraun, lang'], ['Wunsch', 'helles Blond mit weichem Übergang'], ['Technik', 'Aufhellung und Glossing'], ['Richtwert', 'ab 160 € (lang)']] },
|
||||||
|
];
|
||||||
|
|
||||||
|
const salonPics = [
|
||||||
|
{ img: salonInnen4, alt: 'Bedienplätze vor großen Fenstern', cap: 'Helle Plätze vor großen Fenstern, an der Wand der VK-Schriftzug.' },
|
||||||
|
{ img: salonInnen1, alt: 'Salon mit blauen Wandpaneelen', cap: 'Der Salon in der Kastanienallee mit den blauen Wandpaneelen.' },
|
||||||
|
{ img: salonEmpfang, alt: 'Empfangstresen mit Pflegeprodukten', cap: 'Am Empfang: Paul Mitchell, Redken und Ihr nächster Termin.' },
|
||||||
|
{ img: salonWarte, alt: 'Wartebereich mit Sesseln', cap: 'Ein ruhiger Platz zum Ankommen.' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const faq = {
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'FAQPage',
|
||||||
|
mainEntity: [
|
||||||
|
{ q: 'Wer macht Great Lengths in Tostedt?', a: 'Das Friseur-Team VK in der Kastanienallee 1 ist offizieller Great-Lengths-Partner mit der Auszeichnung Master of Great Lengths. Die Beratung ist kostenlos.' },
|
||||||
|
{ q: 'Kann ich beim Friseur-Team VK online buchen?', a: 'Nein. Für Braut, Farbe und Great Lengths stellen Sie eine Anfrage, der Salon ruft zurück und plant den Termin. Schnitt und Herren gehen direkt per Telefon oder WhatsApp.' },
|
||||||
|
].map((x) => ({ '@type': 'Question', name: x.q, acceptedAnswer: { '@type': 'Answer', text: x.a } })),
|
||||||
|
};
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Friseur-Team VK · Friseur in Tostedt für Braut, Balayage und Great Lengths"
|
title="Friseur-Team VK · Friseur in Tostedt für Braut, Balayage und Great Lengths"
|
||||||
description="Meisterbetrieb in der Kastanienallee 1 in Tostedt, seit 2017. Schwerpunkte: Brautstyling, Balayage und Haarverlängerung mit Great Lengths. 4,9 Sterne bei 193 Google-Bewertungen."
|
description="Meisterbetrieb in Tostedt seit 2017: Brautstyling, Balayage und Great Lengths Haarverlängerung, dazu Damen, Herren und Barber. 4,9 Sterne aus 194 Google-Bewertungen."
|
||||||
|
schema={[faq]}
|
||||||
>
|
>
|
||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<section class="hero">
|
<section class="hero" aria-labelledby="hero-h">
|
||||||
<div class="wrap hero__grid">
|
<div class="pad">
|
||||||
<div class="hero__txt">
|
<p class="hero__crumb">Start <span>/ Tostedt</span></p>
|
||||||
<p class="label">Friseur in Tostedt · Meisterbetrieb seit 2017</p>
|
<h1 class="hero__h" id="hero-h">
|
||||||
<h1>Schön von außen, <em>glücklich von innen.</em></h1>
|
<span class="sr">Schön von außen, glücklich von innen. Friseur-Team VK in Tostedt.</span>
|
||||||
<p class="lead">Wir sind das Friseur-Team VK aus der Kastanienallee 1. Unsere Schwerpunkte sind Brautstyling, Balayage und Haarverlängerung mit Great Lengths. Für Schnitt und Farbe im Alltag sind wir genauso da.</p>
|
<span class="l" aria-hidden="true"><span><span class="ini">S</span>chön von außen,</span></span>
|
||||||
<div class="btn-row hero__btns">
|
<span class="l l2" aria-hidden="true"><span>glücklich <span class="paren">(</span>von innen<span class="paren">)</span></span></span>
|
||||||
<a class="btn" href="/anfrage">Anfrage stellen<Icon name="arrow" /></a>
|
</h1>
|
||||||
<a class="btn btn--ghost" href={salon.phoneHref}><Icon name="phone" />{salon.phoneDisplay}</a>
|
|
||||||
</div>
|
|
||||||
<Rating />
|
|
||||||
</div>
|
|
||||||
<figure class="hero__media">
|
|
||||||
<div class="hero__panel" aria-hidden="true"></div>
|
|
||||||
<div class="frame arch hero__img">
|
|
||||||
<Pic src={salonInnen1} eager alt="Der helle Salon in der Kastanienallee mit Bedienplätzen und blauen Wandpaneelen" sizes="(min-width: 60rem) 42vw, 92vw" widths={[420, 640, 860, 1100, 1400]} />
|
|
||||||
</div>
|
|
||||||
<figcaption>Der Salon in der Kastanienallee 1</figcaption>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Schwerpunkte -->
|
|
||||||
<section class="section focus" aria-labelledby="focus-h">
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="head">
|
|
||||||
<p class="label">Schwerpunkte</p>
|
|
||||||
<h2 id="focus-h">Braut, Farbe, <em>Länge.</em></h2>
|
|
||||||
<p class="lead">Drei Leistungen, bei denen wir vor dem ersten Handgriff in Ruhe beraten.</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="focus__list">
|
<div class="r3 hero__grid">
|
||||||
<li class="frow reveal">
|
<div class="a"></div>
|
||||||
<div class="frow__media">
|
<div class="b hero__text">
|
||||||
<Shot arch title="Braut-Hochsteckfrisur" detail="Detail Nacken, weiches Fensterlicht" ratio="4/5" />
|
<p>Willkommen beim Friseur-Team VK in der Kastanienallee 1. Seit {salon.sinceYear} ein Meisterbetrieb in Tostedt.</p>
|
||||||
</div>
|
<p>Unsere Schwerpunkte sind <a class="link" href="/brautservice">Brautstyling</a>, <a class="link" href="/balayage-farbe">Balayage</a> und <a class="link" href="/haarverlaengerung">Haarverlängerung mit Great Lengths</a>. Für Schnitt und Farbe im Alltag sind wir genauso da.</p>
|
||||||
<div class="frow__txt">
|
<ul class="hero__proof" role="list">
|
||||||
<span class="frow__n display" aria-hidden="true">01</span>
|
<li><a href={salon.google.url} rel="noopener">{salon.google.rating} Sterne aus {salon.google.count} Google-Bewertungen</a></li>
|
||||||
<h3>Brautservice</h3>
|
<li>Master of Great Lengths</li>
|
||||||
<p>Vom ersten Gespräch über den Probetermin bis zum Styling am Hochzeitstag, Frisur und Make-up aus einer Hand. Auf Wunsch kommen wir am großen Tag zu Ihnen.</p>
|
<li>Paul Mitchell Flagship-Salon</li>
|
||||||
<p class="frow__price num">Probetermin ab {eur(75)}<sup>*</sup></p>
|
|
||||||
<a class="more" href="/brautservice"><span>Zum Brautservice</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="frow frow--alt reveal">
|
|
||||||
<div class="frow__media">
|
|
||||||
<Shot arch tone="petrol" title="Balayage, Ergebnis" detail="Rückenansicht, lange Wellen, Tageslicht" ratio="4/5" />
|
|
||||||
</div>
|
|
||||||
<div class="frow__txt">
|
|
||||||
<span class="frow__n display" aria-hidden="true">02</span>
|
|
||||||
<h3>Balayage und Farbe</h3>
|
|
||||||
<p>Balayage, Foliensträhnen, Coloration und Glossing. Wir planen die Farbe nach Ihrem Ausgangshaar und arbeiten mit Produkten von Redken und Paul Mitchell.</p>
|
|
||||||
<p class="frow__price num">Balayage ab {eur(140)}<sup>*</sup></p>
|
|
||||||
<a class="more" href="/balayage-farbe"><span>Zu Balayage und Farbe</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
<li class="frow reveal">
|
|
||||||
<div class="frow__media">
|
|
||||||
<div class="frame arch ratio-45">
|
|
||||||
<Pic src={greatLengths} alt="Great Lengths Schriftzug auf glänzendem, blondem Echthaar" sizes="(min-width: 60rem) 34vw, 88vw" widths={[400, 640, 900, 1080]} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="frow__txt">
|
|
||||||
<span class="frow__n display" aria-hidden="true">03</span>
|
|
||||||
<h3>Haarverlängerung und -verdichtung</h3>
|
|
||||||
<p>Mehr Länge oder mehr Fülle mit Great Lengths. Vor jeder Verlängerung steht ein Beratungstermin, in dem wir Haarstruktur, Farbton und Pflege besprechen.</p>
|
|
||||||
<p class="frow__price num">Verdichtung ab {eur(350)}<sup>*</sup></p>
|
|
||||||
<a class="more" href="/haarverlaengerung"><span>Zur Haarverlängerung</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
<p class="small leise focus__foot">* Richtwerte. Preise werden mit dem Salon abgestimmt.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<Reels
|
|
||||||
title="Arbeiten in Bewegung"
|
|
||||||
items={[
|
|
||||||
{ title: 'Balayage vorher und nachher', detail: '15 Sekunden, Drehung im Tageslicht' },
|
|
||||||
{ title: 'Eine Brautfrisur entsteht', detail: 'Zeitraffer vom Probetermin' },
|
|
||||||
{ title: 'Great Lengths', detail: 'Ergebnis nach der Verdichtung' },
|
|
||||||
{ title: 'Herrenschnitt mit Fade', detail: 'Barbershop, Nahaufnahme' },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Über uns -->
|
|
||||||
<section class="section about" aria-labelledby="about-h">
|
|
||||||
<div class="wrap about__grid">
|
|
||||||
<figure class="about__img reveal">
|
|
||||||
<div class="frame ratio-32">
|
|
||||||
<Pic src={teamFoto} alt="Das Team des Friseur-Team VK in schwarzer Kleidung im Salon" sizes="(min-width: 60rem) 58vw, 100vw" />
|
|
||||||
</div>
|
|
||||||
<figcaption>Das Team im Salon in der Kastanienallee</figcaption>
|
|
||||||
</figure>
|
|
||||||
<div class="about__txt">
|
|
||||||
<p class="label">Über uns</p>
|
|
||||||
<h2 id="about-h">Ein Meisterbetrieb, <em>seit 2017.</em></h2>
|
|
||||||
<p>Veronika Sprangel ist Friseurmeisterin und hat den Salon im November 2017 eröffnet. Heute arbeiten wir zu acht: Friseurinnen und Friseure, ein Barber, ein Auszubildender und Dennis Sprangel, der sich um die Organisation kümmert.</p>
|
|
||||||
<p>Für Farbe und Pflege nutzen wir Redken und Paul Mitchell, für Haarverlängerungen Great Lengths.</p>
|
|
||||||
<a class="more" href="/team"><span>Das Team kennenlernen</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Alltag -->
|
|
||||||
<section class="section section--nebel daily" aria-labelledby="daily-h">
|
|
||||||
<div class="wrap daily__grid">
|
|
||||||
<div>
|
|
||||||
<div class="head">
|
|
||||||
<p class="label">Für jeden Tag</p>
|
|
||||||
<h2 id="daily-h">Der Termin zwischendurch.</h2>
|
|
||||||
</div>
|
|
||||||
<ul class="daily__list">
|
|
||||||
{alltag.map((a) => (
|
|
||||||
<li>
|
|
||||||
<a href={a.href} class="daily__row">
|
|
||||||
<span class="daily__t">{a.t}</span>
|
|
||||||
<span class="daily__d">{a.d}</span>
|
|
||||||
<span class="daily__p num">ab {eur(a.from)}<sup>*</sup></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
</ul>
|
||||||
<a class="more" href="/leistungen-preise"><span>Alle Leistungen und Preise</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
</div>
|
||||||
<figure class="daily__img reveal">
|
<div class="c">
|
||||||
<div class="frame arch ratio-34">
|
<div class="fig hero__img" data-clip>
|
||||||
<Pic src={salonInnen4} alt="Bedienplätze vor großen Fenstern, an der Wand der grüne VK-Schriftzug" sizes="(min-width: 60rem) 36vw, 92vw" widths={[400, 640, 803]} />
|
<Pic src={brautHero} alt="Brautfrisur mit offenen Wellen und Blütenspangen, gestylt im Salon" sizes="(min-width: 56rem) 55vw, 100vw" eager pos="top" />
|
||||||
</div>
|
</div>
|
||||||
</figure>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="r3 hero__foot">
|
||||||
|
<div class="a">
|
||||||
|
<div class="fig hero__small" data-clip>
|
||||||
|
<Pic src={salonInnen1} alt="Der helle Salon mit blauen Wandpaneelen" sizes="(min-width: 56rem) 16vw, 40vw" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="b hero__scroll" aria-hidden="true"><span>↓</span><span>(scrollen)</span></div>
|
||||||
|
<div class="c hero__ctas">
|
||||||
|
<a class="oval" href="/neue-kundinnen">Neu bei uns?</a>
|
||||||
|
<a class="oval oval--fill" href="/anfrage">Termin anfragen</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Wartezeiten -->
|
||||||
|
<section class="status" aria-label="Aktuelle Wartezeiten für neue Kundinnen">
|
||||||
|
<div class="status__in">
|
||||||
|
<p class="status__lbl pad"><span class="status__dot" aria-hidden="true"></span><span>Neue Kundinnen<br />Stand {waits.asOf}</span></p>
|
||||||
|
<ul role="list">
|
||||||
|
{waits.items.map((w) => <li class="pad"><b>{w.label}</b><span>{w.value}</span></li>)}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Zahlen + Meisterbetrieb -->
|
||||||
|
<section class="stats" id="team" aria-labelledby="meister-h">
|
||||||
|
<div class="stats__tiles">
|
||||||
|
<div class="tile"><div class="tile__top"><span class="cap">001</span><span class="tile__l">als Meisterbetrieb eröffnet</span></div><span class="tile__v">{salon.sinceYear}</span></div>
|
||||||
|
<div class="tile"><div class="tile__top"><span class="cap">002</span><span class="tile__l">Menschen im Team</span></div><span class="tile__v">{salon.teamSize}</span></div>
|
||||||
|
<div class="tile"><div class="tile__top"><span class="cap">003</span><span class="tile__l">Google-Bewertungen</span></div><span class="tile__v">{salon.google.count}</span></div>
|
||||||
|
<div class="tile"><div class="tile__top"><span class="cap">004</span><span class="tile__l">Sterne im Schnitt</span></div><span class="tile__v">{salon.google.rating}</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="stats__text">
|
||||||
|
<H text="Ein Meisterbetrieb, zu acht" />
|
||||||
|
<div class="copy gap-5" data-rv>
|
||||||
|
<p>Veronika Sprangel ist Friseurmeisterin und hat den Salon im {salon.since} eröffnet. Heute arbeiten Friseurinnen und Friseure, ein Barber, ein Auszubildender und Dennis Sprangel in der Organisation zusammen.</p>
|
||||||
|
<p class="muted">Seit 2022 ist der Salon Paul Mitchell Flagship-Salon. Für Haarverlängerung und Haarverdichtung tragen wir die Auszeichnung Master of Great Lengths.</p>
|
||||||
|
<div class="row"><a class="oval oval--sm" href="/team">Team kennenlernen</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Leistungen -->
|
||||||
|
<section class="sec" id="leistungen" aria-labelledby="l-h">
|
||||||
|
<div class="pad"><H text="Leistungen" /></div>
|
||||||
|
<div class="rows mt-7">
|
||||||
|
{services.map((s, i) => (
|
||||||
|
<a class="srow" href={s.href} data-rv>
|
||||||
|
<span class="srow__n">{s.n}</span>
|
||||||
|
<span class="srow__t">{s.title}{s.em && <><br /><em>{s.em}</em></>}</span>
|
||||||
|
<span class="srow__d"><span>{s.text}</span><span class="price">{s.price}*</span></span>
|
||||||
|
<span class="srow__th" aria-hidden="true">
|
||||||
|
{s.th.map((t) => <span class="fig"><Pic src={t} alt="" sizes="72px" widths={[150, 300]} /></span>)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<p class="cap mt-4 pad from-c">* Richtwerte. Den genauen Preis besprechen wir in der Beratung. <a class="link" href="/leistungen-preise">Alle Preise</a></p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Ergebnisse -->
|
||||||
|
<section class="sec-b" aria-labelledby="e-h">
|
||||||
|
<div class="pad"><H text="Ergebnisse" /></div>
|
||||||
|
<p class="lead pad from-c mt-5" data-rv>Echte Kundinnen aus dem Salon, vorher und nachher. Damit Sie vorab sehen, was bei Haaren wie Ihren möglich ist.</p>
|
||||||
|
<div class="cases mt-8">
|
||||||
|
{cases.map((c) => (
|
||||||
|
<article class="case">
|
||||||
|
<span class="cap case__n">{c.n} · {c.where}</span>
|
||||||
|
<div class="case__meta" data-rv>
|
||||||
|
<h3 class="h3">{c.title}</h3>
|
||||||
|
<dl class="facts">
|
||||||
|
{c.facts.map(([k, v]) => <><dt>{k}</dt><dd>{v}</dd></>)}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="case__pics">
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.v} alt={c.altV} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Vorher</span></figure>
|
||||||
|
<figure class="fig r-3x4" data-clip><Pic src={c.nn} alt={c.alt} sizes="(min-width: 56rem) 28vw, 50vw" widths={[360, 540, 675]} /><span class="tag">Nachher</span></figure>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Bewertungen -->
|
<!-- Bewertungen -->
|
||||||
<section class="section rating" aria-labelledby="rating-h">
|
<section class="sec-b" aria-labelledby="b-h">
|
||||||
<div class="wrap">
|
<div class="pad"><H text="Was Kundinnen sagen" /></div>
|
||||||
<h2 id="rating-h" class="label">Google-Bewertungen</h2>
|
<div class="revs mt-7">
|
||||||
<div class="rating__body"><Rating variant="block" /></div>
|
<p class="revs__lead cap">{salon.google.rating} von 5 Sternen<br />{salon.google.count} Bewertungen<br />bei Google</p>
|
||||||
</div>
|
{reviews.map((r, i) => (
|
||||||
</section>
|
<figure class={`rev d-${i + 1}`} data-rv>
|
||||||
|
<blockquote><q>{r.q}</q></blockquote>
|
||||||
<!-- Kundenkarte -->
|
<figcaption class="rev__src"><span class="stars" aria-label="5 von 5 Sternen">★★★★★</span><span>Google-Bewertung · {r.topic}</span></figcaption>
|
||||||
<section class="section--tight card-teaser" aria-labelledby="card-h">
|
|
||||||
<div class="wrap card-teaser__grid">
|
|
||||||
<div class="card-teaser__visual reveal"><LoyaltyCard stamps={4} /></div>
|
|
||||||
<div class="card-teaser__txt">
|
|
||||||
<p class="label">Kundenkarte</p>
|
|
||||||
<h2 id="card-h">Ihre Stempelkarte, jetzt auch <em>im Handy.</em></h2>
|
|
||||||
<p>Für jeden Besuch gibt es einen Stempel, beim sechsten ein Pflege-Upgrade gratis. Die Karte liegt in Apple Wallet oder Google Wallet, als Web-Karte im Browser oder weiterhin gedruckt im Portemonnaie. Ihre bisherige Papierkarte gilt weiter, wir übertragen die Stempel.</p>
|
|
||||||
<a class="more" href="/kundenkarte"><span>So funktioniert die Kundenkarte</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- Besuch -->
|
|
||||||
<section class="section visit" aria-labelledby="visit-h">
|
|
||||||
<div class="wrap visit__grid">
|
|
||||||
<figure class="visit__img reveal">
|
|
||||||
<div class="frame ratio-32">
|
|
||||||
<Pic src={salonEmpfang} alt="Empfangstresen des Salons mit Produktregal und Eingangstür" sizes="(min-width: 60rem) 50vw, 100vw" />
|
|
||||||
</div>
|
|
||||||
</figure>
|
</figure>
|
||||||
<div class="visit__txt">
|
))}
|
||||||
<p class="label">Besuch</p>
|
</div>
|
||||||
<h2 id="visit-h">Kastanienallee 1, <em>Tostedt.</em></h2>
|
<p class="revs__note">Ausgewählte Bewertungen aus unserem <a class="link" href={salon.google.url} rel="noopener">Google-Profil</a> ({salon.google.rating} Sterne aus {salon.google.count} Bewertungen, Stand {salon.google.asOf}). Wir haben nicht geprüft, ob die Verfasserinnen bei uns Kundin waren; alle Bewertungen finden Sie bei Google.</p>
|
||||||
<address>
|
</section>
|
||||||
{salon.legalName}<br />{salon.street}<br />{salon.zip} {salon.city}
|
|
||||||
</address>
|
<!-- Neu bei uns -->
|
||||||
<Hours />
|
<section class="sec-b" aria-labelledby="n-h">
|
||||||
<div class="btn-row">
|
<div class="r3">
|
||||||
<a class="btn btn--ghost" href={salon.routeUrl} rel="noopener"><Icon name="pin" />Route planen</a>
|
<div class="ab"><H text="Neu bei uns?" /></div>
|
||||||
<a class="btn btn--ghost" href={salon.whatsappHref} rel="noopener"><Icon name="whatsapp" />WhatsApp</a>
|
<div class="c self-end"><p class="lead" data-rv>Wir sind oft ausgebucht. Damit Ihr Termin trotzdem gut wird, beginnen große Termine bei uns mit einem Gespräch.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="steps mt-7">
|
||||||
|
<p class="steps__lead">So kommen Sie<br />zu Ihrem Termin</p>
|
||||||
|
<div class="step d-1" data-rv><span class="step__n" aria-hidden="true">1</span><h3>Anfrage schicken</h3><p>Sagen Sie uns, was Sie vorhaben, gern mit einem Foto von hinten bei Tageslicht. Das dauert zwei Minuten.</p></div>
|
||||||
|
<div class="step d-2" data-rv><span class="step__n" aria-hidden="true">2</span><h3>Rückruf in zwei Werktagen</h3><p>Wir rufen Sie zur Wunschzeit an, klären Fragen und den Preisrahmen. Bei Farbe und Great Lengths folgt eine kurze Beratung im Salon.</p></div>
|
||||||
|
<div class="step d-3" data-rv><span class="step__n" aria-hidden="true">3</span><h3>Termin mit Zeit</h3><p>Wir planen so viel Zeit ein, wie Ihr Haar braucht. Wer kurzfristig kann, kommt über die Warteliste oft schneller dran.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="pad from-c mt-6 row">
|
||||||
|
<a class="oval oval--fill" href="/anfrage">Anfrage stellen</a>
|
||||||
|
<a class="go" href="/neue-kundinnen">Alles für neue Kundinnen</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Inhaberin -->
|
||||||
|
<section class="sec-b rule" aria-labelledby="o-h">
|
||||||
|
<div class="r3 owner sec-t">
|
||||||
|
<div class="ab">
|
||||||
|
<figure class="fig r-2x3" data-clip><Pic src={vero} alt="Veronika Sprangel, Friseurmeisterin und Inhaberin" sizes="(min-width: 56rem) 36vw, 100vw" widths={[400, 600, 800]} /></figure>
|
||||||
|
<figcaption class="figcap">Veronika Sprangel, Friseurmeisterin, Inhaberin seit 2017</figcaption>
|
||||||
|
</div>
|
||||||
|
<div class="c gap-6 self-end">
|
||||||
|
<p class="cap">Die Inhaberin</p>
|
||||||
|
<p class="owner__q" data-rv>Erst das Gespräch, dann die Farbe.</p>
|
||||||
|
<div class="copy" data-rv>
|
||||||
|
<p>In den Bewertungen heißt sie oft einfach „Vero“. Veronika Sprangel berät bei Braut, Farbe und Great Lengths selbst. Sie sorgt dafür, dass jede neue Kundin bei der Stylistin landet, die zu ihrem Wunsch passt, und dass vor einer großen Farbe niemand rät.</p>
|
||||||
</div>
|
</div>
|
||||||
<p class="small leise">Öffnungszeiten werden vor dem Livegang mit dem Salon bestätigt.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<CtaBand />
|
<!-- Salon -->
|
||||||
|
<section class="salon" id="salon" aria-labelledby="s-h" data-salon>
|
||||||
|
<div class="salon__side">
|
||||||
|
<H text="Ein Salon" />
|
||||||
|
<div class="salon__thumbs" role="group" aria-label="Bilder vom Salon wählen">
|
||||||
|
{salonPics.map((p, i) => (
|
||||||
|
<button type="button" data-pick aria-pressed={i === 0 ? 'true' : 'false'} data-caption={p.cap} aria-label={`Bild ${i + 1}: ${p.alt}`}>
|
||||||
|
<span class="cap">{String(i + 1).padStart(3, '0')}</span>
|
||||||
|
<span class="fig"><Pic src={p.img} alt="" sizes="100px" widths={[200]} /></span>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div class="salon__cap">
|
||||||
|
<span class="cap" data-num>001</span>
|
||||||
|
<span>{salon.street}, {salon.zip} {salon.city}</span>
|
||||||
|
<span class="muted" data-cap aria-live="polite">{salonPics[0].cap}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="salon__big">
|
||||||
|
{salonPics.map((p, i) => (
|
||||||
|
<div class:list={['fig', { 'is-on': i === 0 }]} data-big><Pic src={p.img} alt={i === 0 ? p.alt : ''} sizes="(min-width: 56rem) 55vw, 100vw" /></div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Barber · Karriere · Kundin -->
|
||||||
|
<section class="trio" aria-label="Weitere Bereiche">
|
||||||
|
<div class="trio__i" data-rv>
|
||||||
|
<div class="fig"><Pic src={marco} alt="Marco, Friseur und Barber" sizes="(min-width: 56rem) 30vw, 100vw" widths={[400, 600]} pos="top" /></div>
|
||||||
|
<span class="cap">Herren</span>
|
||||||
|
<h3>Barber</h3>
|
||||||
|
<p>Haarschnitt, Bart und Nassrasur mit heißem Tuch. Ohne Formular, einfach anrufen oder schreiben.</p>
|
||||||
|
<a class="go" href="/barber">Zum Barber</a>
|
||||||
|
</div>
|
||||||
|
<div class="trio__i d-1" data-rv>
|
||||||
|
<div class="fig"><Pic src={teamFoto} alt="Das Team vom Friseur-Team VK" sizes="(min-width: 56rem) 30vw, 100vw" widths={[400, 600, 900]} /></div>
|
||||||
|
<span class="cap">Karriere</span>
|
||||||
|
<h3>Wir stellen ein</h3>
|
||||||
|
<p>Friseurin oder Friseur mit Lust auf Braut, Farbe oder Extensions. Gehalt steht dabei, Bewerbung geht per WhatsApp.</p>
|
||||||
|
<a class="go" href="/karriere">Zu den Stellen</a>
|
||||||
|
</div>
|
||||||
|
<div class="trio__i d-2" data-rv>
|
||||||
|
<div class="fig"><Pic src={salonEmpfang} alt="Empfang im Salon" sizes="(min-width: 56rem) 30vw, 100vw" widths={[400, 600, 900]} /></div>
|
||||||
|
<span class="cap">Schon Kundin?</span>
|
||||||
|
<h3>Kundenkarte & Gutscheine</h3>
|
||||||
|
<p>Die Stempelkarte gibt es jetzt auch im Handy, die alte Papierkarte gilt weiter. Gutscheine bekommen Sie am Empfang.</p>
|
||||||
|
<div class="row"><a class="go" href="/kundenkarte">Kundenkarte</a><a class="go" href="/gutscheine">Gutscheine</a></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Bis bald -->
|
||||||
|
<section class="sec bye" id="kontakt" aria-labelledby="bye-h">
|
||||||
|
<div class="pad"><H text="Bis bald im Salon" class="bye__h" /></div>
|
||||||
|
<div class="r3 bye__grid">
|
||||||
|
<div class="a"><div class="fig r-1x1 bye__pic" data-clip><Pic src={teamFoto} alt="" sizes="20vw" widths={[300, 500]} pos="left" /></div></div>
|
||||||
|
<div class="b bye__phone"><a class="oval" href={salon.phoneHref}>{salon.phoneDisplay}</a></div>
|
||||||
|
<div class="c gap-5" data-rv>
|
||||||
|
<p class="maxw-s">Für Braut, Balayage und Great Lengths starten wir mit einer kurzen Anfrage. Wir rufen zurück, beraten und tragen den Termin für Sie ein. Schnitt und Herren gehen direkt am Telefon.</p>
|
||||||
|
<div class="row"><a class="oval oval--fill" href="/anfrage">Anfrage stellen</a><a class="go" href={salon.routeUrl} rel="noopener">Route planen</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
/* Hero */
|
|
||||||
.hero { padding-block: var(--s-6) var(--section); overflow: hidden; }
|
|
||||||
.hero__grid { display: grid; gap: var(--s-7); }
|
|
||||||
.hero__txt { display: grid; gap: var(--s-5); align-content: center; justify-items: start; }
|
|
||||||
.hero__txt h1 { font-size: var(--fs-h1); max-width: 11ch; }
|
|
||||||
.hero__txt h1 em { display: inline; }
|
|
||||||
.hero__btns { margin-top: var(--s-2); }
|
|
||||||
.hero__media { position: relative; margin: 0; padding: 0 0 var(--s-5) var(--s-5); }
|
|
||||||
.hero__panel { position: absolute; left: 0; bottom: var(--s-7); top: 22%; width: 52%; background: var(--c-petrol); }
|
|
||||||
.hero__img { position: relative; aspect-ratio: 4 / 5; }
|
|
||||||
.hero__img :global(img) { object-position: 38% 50%; }
|
|
||||||
.hero__media figcaption { position: relative; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.hero { padding-top: var(--s-8); }
|
|
||||||
.hero__grid { grid-template-columns: 1.08fr 0.92fr; gap: var(--s-8); align-items: center; }
|
|
||||||
.hero__img { aspect-ratio: 5 / 6; }
|
|
||||||
}
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.hero__txt > * { animation: h-rise var(--t-slow) var(--ease-out) both; }
|
|
||||||
.hero__txt > h1 { animation-name: h-rise-t; animation-delay: 60ms; }
|
|
||||||
.hero__txt > :nth-child(3) { animation-delay: 140ms; }
|
|
||||||
.hero__txt > :nth-child(4) { animation-delay: 220ms; }
|
|
||||||
.hero__txt > :nth-child(5) { animation-delay: 300ms; }
|
|
||||||
.hero__panel { animation: h-grow 1.2s var(--ease-out) both 150ms; transform-origin: bottom; }
|
|
||||||
@keyframes h-rise { from { opacity: 0; transform: translateY(18px); } }
|
|
||||||
@keyframes h-rise-t { from { transform: translateY(16px); } }
|
|
||||||
@keyframes h-grow { from { transform: scaleY(0); } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Schwerpunkte */
|
|
||||||
.focus { padding-top: 0; }
|
|
||||||
.focus__list { list-style: none; padding: 0; margin-top: var(--s-8); display: grid; gap: var(--s-9); }
|
|
||||||
.frow { display: grid; gap: var(--s-6); align-items: center; }
|
|
||||||
.frow__media { max-width: 30rem; width: 100%; }
|
|
||||||
.frow__txt { display: grid; gap: var(--s-3); justify-items: start; max-width: 34rem; }
|
|
||||||
.frow__n { font-size: var(--fs-h2); font-style: italic; color: var(--c-petrol); line-height: 1; }
|
|
||||||
.frow h3 { font-size: var(--fs-h2); line-height: 1.05; }
|
|
||||||
.frow__price { font-weight: 600; margin-top: var(--s-2); }
|
|
||||||
sup { font-size: 0.75em; color: var(--c-text-leise); margin-left: 1px; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.frow { grid-template-columns: 5fr 6fr; gap: var(--s-9); }
|
|
||||||
.frow--alt .frow__media { order: 2; justify-self: end; }
|
|
||||||
.frow--alt { grid-template-columns: 6fr 5fr; }
|
|
||||||
.frow--alt .frow__txt { justify-self: end; }
|
|
||||||
}
|
|
||||||
.focus__foot { margin-top: var(--s-8); }
|
|
||||||
|
|
||||||
/* Über uns */
|
|
||||||
.about__grid { display: grid; gap: var(--s-6); }
|
|
||||||
.about__img { margin: 0; }
|
|
||||||
.about__txt { display: grid; gap: var(--s-4); justify-items: start; max-width: 32rem; }
|
|
||||||
.about__txt h2 { margin-bottom: var(--s-2); }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.about__grid { grid-template-columns: 7fr 5fr; gap: var(--s-8); align-items: end; }
|
|
||||||
.about__img { margin-left: calc(var(--gutter) * -1); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Alltag */
|
|
||||||
.daily__grid { display: grid; gap: var(--s-7); }
|
|
||||||
.daily__list { list-style: none; padding: 0; margin-block: var(--s-6) var(--s-4); border-top: 1.5px solid var(--c-anthrazit); }
|
|
||||||
.daily__row {
|
|
||||||
display: grid; grid-template-columns: 1fr auto; gap: 0.15rem var(--s-4);
|
|
||||||
padding-block: var(--s-4); border-bottom: var(--hair);
|
|
||||||
color: inherit; text-decoration: none;
|
|
||||||
}
|
|
||||||
.daily__row:hover .daily__t { color: var(--c-petrol); }
|
|
||||||
.daily__t { font-family: var(--font-display); font-size: var(--fs-h4); line-height: 1.25; transition: color var(--t-fast); }
|
|
||||||
.daily__d { grid-column: 1; color: var(--c-text-leise); font-size: var(--fs-small); }
|
|
||||||
.daily__p { grid-column: 2; grid-row: 1 / span 2; align-self: center; font-weight: 600; white-space: nowrap; }
|
|
||||||
.daily__img { margin: 0; max-width: 26rem; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.daily__grid { grid-template-columns: 1.3fr 1fr; gap: var(--s-9); align-items: center; }
|
|
||||||
.daily__img { justify-self: end; width: 100%; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bewertungen */
|
|
||||||
.rating__body { margin-top: var(--s-5); }
|
|
||||||
|
|
||||||
/* Kundenkarte */
|
|
||||||
.card-teaser { padding-block: var(--section); background: linear-gradient(180deg, var(--c-papier) 0, var(--c-papier) 30%, var(--c-petrol-hell) 30%); }
|
|
||||||
.card-teaser__grid { display: grid; gap: var(--s-7); align-items: center; }
|
|
||||||
.card-teaser__visual { display: grid; justify-items: center; }
|
|
||||||
.card-teaser__txt { display: grid; gap: var(--s-4); justify-items: start; max-width: 34rem; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.card-teaser { background: linear-gradient(90deg, var(--c-petrol-hell) 0, var(--c-petrol-hell) 46%, var(--c-papier) 46%); }
|
|
||||||
.card-teaser__grid { grid-template-columns: 1fr 1.1fr; gap: var(--s-9); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Besuch */
|
|
||||||
.visit__grid { display: grid; gap: var(--s-7); }
|
|
||||||
.visit__img { margin: 0; }
|
|
||||||
.visit__txt { display: grid; gap: var(--s-5); justify-items: start; }
|
|
||||||
.visit__txt address { font-style: normal; }
|
|
||||||
@media (min-width: 60rem) {
|
|
||||||
.visit__grid { grid-template-columns: 1.1fr 1fr; gap: var(--s-9); align-items: start; }
|
|
||||||
.visit__img { margin-left: calc(var(--gutter) * -1); }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHead from '../components/PageHead.astro';
|
||||||
|
import Pic from '../components/Pic.astro';
|
||||||
|
import H from '../components/H.astro';
|
||||||
|
import Closing from '../components/Closing.astro';
|
||||||
|
import { salon, SITE_URL } from '../data/site';
|
||||||
|
import gruppe from '../assets/img/team-gruppenfoto.jpg';
|
||||||
|
import fabian from '../assets/img/team/fabian.jpg';
|
||||||
|
import innen from '../assets/img/salon-innen-4.jpg';
|
||||||
|
|
||||||
|
/** PRÜFEN: Stellen, Gehaltsspanne und Stunden vom Salon. Werte sind Vorschläge. */
|
||||||
|
const jobs = [
|
||||||
|
{ n: '001', title: 'Friseurin oder Friseur', type: 'Vollzeit oder Teilzeit', pay: '2.700 bis 3.300 € brutto bei Vollzeit, je nach Erfahrung', text: 'Sie schneiden und färben sicher und möchten sich auf Braut, Farbe oder Great Lengths spezialisieren. Die Schulungen dafür zahlen wir.' },
|
||||||
|
{ n: '002', title: 'Ausbildung 2027', type: 'Start 1. August 2027', pay: 'Ausbildungsvergütung nach Tarif, ab 724 € im ersten Jahr', text: 'Drei Jahre im Meisterbetrieb, mit echten Kundinnen ab dem ersten Jahr und Unterstützung bei Berufsschule und Prüfung.' },
|
||||||
|
];
|
||||||
|
const jobSchema = jobs.slice(0, 1).map((j) => ({
|
||||||
|
'@context': 'https://schema.org',
|
||||||
|
'@type': 'JobPosting',
|
||||||
|
title: j.title,
|
||||||
|
description: j.text,
|
||||||
|
datePosted: '2026-10-01',
|
||||||
|
employmentType: ['FULL_TIME', 'PART_TIME'],
|
||||||
|
hiringOrganization: { '@type': 'Organization', name: salon.name, sameAs: SITE_URL },
|
||||||
|
jobLocation: { '@type': 'Place', address: { '@type': 'PostalAddress', streetAddress: salon.street, postalCode: salon.zip, addressLocality: salon.city, addressRegion: salon.region, addressCountry: salon.country } },
|
||||||
|
baseSalary: { '@type': 'MonetaryAmount', currency: 'EUR', value: { '@type': 'QuantitativeValue', minValue: 2700, maxValue: 3300, unitText: 'MONTH' } },
|
||||||
|
}));
|
||||||
|
const wa = `${salon.whatsappHref}?text=${encodeURIComponent('Hallo, ich interessiere mich für eine Stelle bei euch.')}`;
|
||||||
|
---
|
||||||
|
<Base
|
||||||
|
title="Karriere: Friseurin und Ausbildung in Tostedt"
|
||||||
|
description="Jobs beim Friseur-Team VK in Tostedt: Friseurin oder Friseur in Voll- oder Teilzeit mit Gehaltsangabe, Ausbildung 2027. Bewerbung in zwei Minuten per WhatsApp."
|
||||||
|
crumbs={[{ name: 'Karriere', href: '/karriere' }]}
|
||||||
|
schema={jobSchema}
|
||||||
|
>
|
||||||
|
<PageHead crumb="Karriere" title="Karriere" sub="bei VK in Tostedt">
|
||||||
|
<p class="lead" data-rv>Wir sind ein Meisterbetrieb mit vollem Terminbuch und suchen Kolleginnen und Kollegen, die mitwachsen wollen. Das Gehalt steht dabei, die Bewerbung dauert zwei Minuten.</p>
|
||||||
|
<div class="row" data-rv><a class="oval oval--fill" href={wa} rel="noopener">Per WhatsApp bewerben</a><a class="go" href={`mailto:${salon.email}?subject=Bewerbung`}>Per E-Mail</a></div>
|
||||||
|
<figure class="fig r-16x9 mt-4" data-clip><Pic src={gruppe} alt="Das Team vom Friseur-Team VK" sizes="(min-width: 56rem) 55vw, 100vw" eager /></figure>
|
||||||
|
</PageHead>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="j-h">
|
||||||
|
<div class="pad"><H text="Offene Stellen" /></div>
|
||||||
|
<div class="rows mt-7">
|
||||||
|
{jobs.map((j) => (
|
||||||
|
<article class="srow" data-rv>
|
||||||
|
<span class="srow__n">{j.n}</span>
|
||||||
|
<h3 class="srow__t">{j.title}<br /><em>{j.type}</em></h3>
|
||||||
|
<span class="srow__d"><span>{j.text}</span><span class="price">{j.pay}</span></span>
|
||||||
|
<span class="srow__th"><a class="oval oval--sm" href={wa} rel="noopener">Bewerben</a></span>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="b-h">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-4x3" data-clip><Pic src={innen} alt="Arbeitsplätze im Salon" sizes="(min-width: 56rem) 36vw, 100vw" /></figure></div>
|
||||||
|
<div class="c gap-5 self-end" data-rv>
|
||||||
|
<H text="Was Sie bei uns haben" class="h3" />
|
||||||
|
<ul class="ticks">
|
||||||
|
<li>Montags frei, feste Schichten, keine Sonntage</li>
|
||||||
|
<li>Schulungen bei Great Lengths und Paul Mitchell, bezahlt</li>
|
||||||
|
<li>Ein Schwerpunkt, der zu Ihnen passt: Braut, Farbe, Extensions oder Barber</li>
|
||||||
|
<li>Ein heller Salon in Tostedt, gut erreichbar mit Auto und Bahn</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="a-h">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-2x3" data-clip><Pic src={fabian} alt="Fabian, Auszubildender" sizes="(min-width: 56rem) 36vw, 100vw" widths={[400, 600, 800]} /></figure></div>
|
||||||
|
<div class="c gap-5 self-end" data-rv>
|
||||||
|
<span class="cap">Ausbildung</span>
|
||||||
|
<h2 class="h3" id="a-h">Lernen im Meisterbetrieb</h2>
|
||||||
|
<p class="copy">Fabian macht bei uns seine Ausbildung. Wer nachkommen will, schickt uns eine kurze Nachricht, ein Praktikum vorher ist jederzeit möglich.</p>
|
||||||
|
<div class="row"><a class="oval" href={wa} rel="noopener">Praktikum anfragen</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Closing title="Kurz melden reicht" text="Name, Telefonnummer und zwei Sätze zu Ihnen. Wir rufen zurück und laden Sie zum Probetag ein." phoneFirst />
|
||||||
|
</Base>
|
||||||
@@ -1,97 +1,64 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHead from '../components/PageHead.astro';
|
||||||
import Pic from '../components/Pic.astro';
|
import Pic from '../components/Pic.astro';
|
||||||
import Hours from '../components/Hours.astro';
|
import H from '../components/H.astro';
|
||||||
import Icon from '../components/Icon.astro';
|
import { salon, hours, fmtHours } from '../data/site';
|
||||||
import Rating from '../components/Rating.astro';
|
import aussen from '../assets/img/salon-empfang.jpg';
|
||||||
import { salon } from '../data/site';
|
import warte from '../assets/img/salon-wartebereich.jpg';
|
||||||
import salonInnen3 from '../assets/img/salon-innen-3.jpg';
|
|
||||||
|
const anfahrt = [
|
||||||
|
{ n: '001', t: 'Mit dem Auto', d: 'Aus Buchholz über die B75 in rund 20 Minuten, aus Hollenstedt und Sittensen über die A1. Parkmöglichkeiten nennen wir Ihnen gern am Telefon.' },
|
||||||
|
{ n: '002', t: 'Mit der Bahn', d: 'Der Bahnhof Tostedt liegt an der Strecke Hamburg–Bremen (RE4/RB41). Von dort kommen Sie zu Fuß oder mit dem Bus zu uns.' },
|
||||||
|
{ n: '003', t: 'Für Bräute vor Ort', d: 'Wir kommen im Umkreis von etwa 30 Kilometern zu Ihnen, etwa nach Buchholz, Handeloh, Hollenstedt oder Schneverdingen.' },
|
||||||
|
];
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Kontakt und Anfahrt"
|
title="Kontakt und Anfahrt"
|
||||||
description="Friseur-Team VK, Kastanienallee 1, 21255 Tostedt. Telefon 04182 2889535, WhatsApp und E-Mail. Öffnungszeiten Dienstag bis Samstag, Route planen."
|
description={`Friseur-Team VK, ${salon.street}, ${salon.zip} ${salon.city}. Telefon ${salon.phoneDisplay}, WhatsApp, Öffnungszeiten und Anfahrt aus Buchholz, Hollenstedt und dem Landkreis Harburg.`}
|
||||||
crumbs={[{ name: 'Kontakt', href: '/kontakt' }]}
|
crumbs={[{ name: 'Kontakt', href: '/kontakt' }]}
|
||||||
bar="kontakt"
|
|
||||||
>
|
>
|
||||||
<section class="kt">
|
<PageHead crumb="Kontakt" title="Kontakt" sub="und Anfahrt">
|
||||||
<div class="wrap kt__grid">
|
<p class="lead" data-rv>{salon.street}, {salon.zip} {salon.city}.<br />Am schnellsten erreichen Sie uns am Telefon, für Braut, Farbe und Great Lengths über die Anfrage.</p>
|
||||||
<div class="kt__txt">
|
<div class="row" data-rv>
|
||||||
<p class="label">Kontakt und Anfahrt</p>
|
<a class="oval oval--fill" href={salon.phoneHref}>{salon.phoneDisplay}</a>
|
||||||
<h1>So erreichen <em>Sie uns.</em></h1>
|
<a class="oval" href={salon.whatsappHref} rel="noopener">WhatsApp</a>
|
||||||
<ul class="kt__ways">
|
<a class="oval" href="/anfrage">Anfrage</a>
|
||||||
<li>
|
|
||||||
<a href={salon.phoneHref} class="way">
|
|
||||||
<Icon name="phone" /><span><span class="way__k">Telefon</span><span class="way__v">{salon.phoneDisplay}</span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href={salon.whatsappHref} rel="noopener" class="way">
|
|
||||||
<Icon name="whatsapp" /><span><span class="way__k">WhatsApp</span><span class="way__v">Nachricht schreiben</span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href={`mailto:${salon.email}`} class="way">
|
|
||||||
<Icon name="mail" /><span><span class="way__k">E-Mail</span><span class="way__v">{salon.email}</span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="/anfrage" class="way">
|
|
||||||
<Icon name="arrow" /><span><span class="way__k">Für Braut, Farbe, Verlängerung</span><span class="way__v">Anfrage stellen</span></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p class="small leise">Termine vergeben wir am Telefon. Wenn wir gerade alle Hände voll zu tun haben, klappt das nicht immer beim ersten Klingeln. Dann schreiben Sie uns gern per WhatsApp.</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="kt__info">
|
<div class="row" data-rv><a class="go" href={salon.routeUrl} rel="noopener">Route mit Google Maps</a><a class="go" href={salon.appleMapsUrl} rel="noopener">Route mit Apple Karten</a><a class="go" href={`mailto:${salon.email}`}>{salon.email}</a></div>
|
||||||
<div class="kt__block">
|
</PageHead>
|
||||||
<h2 class="label">Adresse</h2>
|
|
||||||
<address class="kt__addr">{salon.legalName}<br />{salon.street}<br />{salon.zip} {salon.city}</address>
|
<section class="sec-b" aria-labelledby="h-h">
|
||||||
<div class="btn-row">
|
<div class="r3">
|
||||||
<a class="btn btn--ghost" href={salon.routeUrl} rel="noopener"><Icon name="pin" />Google Maps</a>
|
<div class="ab gap-5">
|
||||||
<a class="btn btn--ghost" href={salon.appleMapsUrl} rel="noopener"><Icon name="pin" />Apple Karten</a>
|
<H text="Öffnungszeiten" class="h3" />
|
||||||
</div>
|
<div class="ft__hours" data-rv>
|
||||||
<p class="small leise">Die Knöpfe öffnen die Route in Ihrer Karten-App. Eine eingebettete Karte gibt es hier bewusst nicht, damit keine Daten an Dritte gehen.</p>
|
{hours.filter((h) => h.day !== 'Sonntag').map((h) => <div class={h.open ? undefined : 'is-closed'}><span>{h.day}</span><span>{fmtHours(h)}</span></div>)}
|
||||||
</div>
|
|
||||||
<div class="kt__block">
|
|
||||||
<h2 class="label">Öffnungszeiten</h2>
|
|
||||||
<Hours />
|
|
||||||
<p class="small leise">Stand März 2026, wird vor dem Livegang bestätigt.</p>
|
|
||||||
</div>
|
|
||||||
<div class="kt__block">
|
|
||||||
<h2 class="label">Bewertungen</h2>
|
|
||||||
<Rating />
|
|
||||||
</div>
|
</div>
|
||||||
|
<p class="cap">Termine nach Vereinbarung.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="c"><figure class="fig r-16x9" data-clip><Pic src={aussen} alt="Empfang im Salon" sizes="(min-width: 56rem) 55vw, 100vw" /></figure></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section--tight">
|
<section class="sec-b" aria-labelledby="a-h">
|
||||||
<div class="wrap">
|
<div class="pad"><H text="Anfahrt" /></div>
|
||||||
<figure class="kt__img">
|
<div class="rows mt-7">
|
||||||
<div class="frame ratio-169">
|
{anfahrt.map((x) => (
|
||||||
<Pic src={salonInnen3} alt="Blick durch den Salon mit Bedienplätzen und dem Aufsteller mit dem Salon-Logo" sizes="(min-width: 84rem) 80rem, 100vw" />
|
<div class="srow" data-rv>
|
||||||
|
<span class="srow__n">{x.n}</span>
|
||||||
|
<h3 class="srow__t">{x.t}</h3>
|
||||||
|
<span class="srow__d"><span>{x.d}</span></span>
|
||||||
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
<figcaption>Der Salon von innen</figcaption>
|
))}
|
||||||
</figure>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-label="Wartebereich">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-4x3" data-clip><Pic src={warte} alt="Wartebereich mit Sesseln" sizes="(min-width: 56rem) 36vw, 100vw" /></figure></div>
|
||||||
|
<div class="c self-end"><p class="lead" data-rv>Wir bitten Sie, ein paar Minuten vor Ihrem Termin da zu sein. Ein Getränk steht bereit.</p></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.kt { padding-block: var(--s-7) var(--s-8); }
|
|
||||||
.kt__grid { display: grid; gap: var(--s-8); }
|
|
||||||
.kt__txt { display: grid; gap: var(--s-6); align-content: start; }
|
|
||||||
.kt__ways { list-style: none; padding: 0; border-top: 1.5px solid var(--c-anthrazit); }
|
|
||||||
.kt__ways li { border-bottom: var(--hair); }
|
|
||||||
.way { display: flex; gap: var(--s-4); align-items: center; min-height: 4.5rem; padding-block: var(--s-3); color: var(--c-anthrazit); text-decoration: none; }
|
|
||||||
.way > :global(svg) { width: 1.5rem; height: 1.5rem; color: var(--c-petrol); flex: none; }
|
|
||||||
.way > span { display: grid; }
|
|
||||||
.way__k { font-size: var(--fs-small); color: var(--c-text-leise); }
|
|
||||||
.way__v { font-family: var(--font-display); font-size: var(--fs-h4); line-height: 1.25; overflow-wrap: anywhere; }
|
|
||||||
.way:hover .way__v { color: var(--c-petrol); }
|
|
||||||
.kt__info { display: grid; gap: var(--s-7); align-content: start; }
|
|
||||||
.kt__block { display: grid; gap: var(--s-4); justify-items: start; }
|
|
||||||
.kt__addr { font-style: normal; font-size: var(--fs-lead); line-height: 1.5; }
|
|
||||||
.kt__img { margin: 0; }
|
|
||||||
@media (min-width: 60rem) { .kt__grid { grid-template-columns: 1.2fr 1fr; gap: var(--s-9); } .kt__info { padding-top: var(--s-6); } }
|
|
||||||
</style>
|
|
||||||
@@ -1,142 +1,61 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import LoyaltyCard from '../components/LoyaltyCard.astro';
|
import PageHead from '../components/PageHead.astro';
|
||||||
|
import H from '../components/H.astro';
|
||||||
import Faq from '../components/Faq.astro';
|
import Faq from '../components/Faq.astro';
|
||||||
import Icon from '../components/Icon.astro';
|
import { KARTE_START, KARTE_DEMO, faqSchema } from '../data/site';
|
||||||
import { KARTE_START, KARTE_DEMO, salon } from '../data/site';
|
|
||||||
|
|
||||||
const wege = [
|
const wege = [
|
||||||
{ t: 'Apple Wallet', d: 'Auf dem iPhone landet die Karte in der Wallet-App, ohne zusätzliche App. Der Stempelstand aktualisiert sich nach jedem Besuch von selbst.' },
|
{ n: '001', t: 'Apple Wallet', d: 'Auf dem iPhone landet die Karte in der Wallet-App, ohne zusätzliche App. Der Stempelstand aktualisiert sich nach jedem Besuch von selbst.' },
|
||||||
{ t: 'Google Wallet', d: 'Auf Android-Handys genauso: Karte einmal hinzufügen, an der Kasse zeigen, fertig.' },
|
{ n: '002', t: 'Google Wallet', d: 'Auf Android genauso: Karte einmal hinzufügen, an der Kasse zeigen, fertig.' },
|
||||||
{ t: 'Web-Karte', d: 'Ein persönlicher Link, der im Browser funktioniert. Auf Wunsch legen Sie ihn als Symbol auf den Startbildschirm.' },
|
{ n: '003', t: 'Web-Karte', d: 'Ein persönlicher Link, der im Browser funktioniert. Auf Wunsch als Symbol auf dem Startbildschirm.' },
|
||||||
{ t: 'Gedruckte Karte', d: 'Im Visitenkartenformat mit Ihrem persönlichen QR-Code, für alle, die lieber etwas in der Hand haben.' },
|
{ n: '004', t: 'Gedruckte Karte', d: 'Im Visitenkartenformat mit Ihrem QR-Code, für alle, die lieber etwas in der Hand haben.' },
|
||||||
];
|
];
|
||||||
const faq = [
|
const faq = [
|
||||||
{ q: 'Was passiert mit meiner Papier-Stempelkarte?', a: 'Sie gilt weiter. Bringen Sie sie beim nächsten Besuch mit, dann übertragen wir Ihre Stempel auf das neue Konto. Verloren geht nichts.' },
|
{ q: 'Was passiert mit meiner Papier-Stempelkarte?', a: 'Sie gilt weiter. Bringen Sie sie zum nächsten Besuch mit, wir übertragen Ihre Stempel auf das neue Konto. Verloren geht nichts.' },
|
||||||
{ q: 'Brauche ich dafür eine App?', a: 'Nein. Apple Wallet und Google Wallet sind auf den meisten Handys schon installiert. Ohne Wallet nutzen Sie die Web-Karte oder die gedruckte Karte.' },
|
{ q: 'Brauche ich dafür eine App?', a: 'Nein. Apple Wallet und Google Wallet sind auf den meisten Handys schon da. Ohne Wallet nutzen Sie die Web-Karte oder die gedruckte Karte.' },
|
||||||
{ q: 'Kann ich mehrere Wege gleichzeitig nutzen?', a: 'Ja. Wallet, Web-Karte und gedruckte Karte gehören zu einem Konto. Egal, was Sie an der Kasse zeigen, der Stempel landet am selben Ort.' },
|
{ q: 'Welche Daten werden gespeichert?', a: 'Ihr Name und Ihre Stempel. Telefonnummer oder E-Mail sind freiwillig. Nachrichten von uns bekommen Sie nur, wenn Sie das ausdrücklich erlauben, und Sie können die Karte jederzeit löschen lassen.' },
|
||||||
{ q: 'Welche Daten werden gespeichert?', a: 'Für die Karte brauchen wir Ihren Namen und Ihre Stempel. Telefonnummer oder E-Mail sind freiwillig. Nachrichten von uns bekommen Sie nur, wenn Sie das ausdrücklich erlauben, und Sie können die Karte jederzeit löschen lassen.' },
|
{ q: 'Was bekomme ich für sechs Stempel?', a: 'Beim sechsten Stempel gibt es ein Pflege-Upgrade gratis, zum Beispiel eine intensive Pflegebehandlung beim nächsten Termin.' },
|
||||||
{ q: 'Was bekomme ich für sechs Stempel?', a: 'Beim sechsten Stempel gibt es ein Pflege-Upgrade gratis, zum Beispiel eine intensivere Pflegebehandlung zu Ihrem nächsten Termin.' },
|
|
||||||
];
|
];
|
||||||
const faqSchema = {
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'FAQPage',
|
|
||||||
mainEntity: faq.map((f) => ({ '@type': 'Question', name: f.q, acceptedAnswer: { '@type': 'Answer', text: f.a } })),
|
|
||||||
};
|
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Kundenkarte: die digitale Stempelkarte"
|
title="Kundenkarte: die digitale Stempelkarte"
|
||||||
description="Die Kundenkarte vom Friseur-Team VK: Stempel sammeln in Apple Wallet, Google Wallet, als Web-Karte oder gedruckt. Sechs Stempel, ein Pflege-Upgrade gratis. Papierkarten gelten weiter."
|
description="Die Kundenkarte vom Friseur-Team VK: Stempel sammeln in Apple Wallet, Google Wallet, als Web-Karte oder gedruckt. Sechs Stempel, ein Pflege-Upgrade gratis. Papierkarten gelten weiter."
|
||||||
crumbs={[{ name: 'Kundenkarte', href: '/kundenkarte' }]}
|
crumbs={[{ name: 'Kundenkarte', href: '/kundenkarte' }]}
|
||||||
schema={[faqSchema]}
|
schema={[faqSchema(faq)]}
|
||||||
>
|
>
|
||||||
<section class="khero">
|
<PageHead crumb="Kundenkarte" title="Kundenkarte" sub="jetzt im Handy">
|
||||||
<div class="wrap khero__grid">
|
<p class="lead" data-rv>Pro Besuch ein Stempel, beim sechsten gibt es ein Pflege-Upgrade gratis. In Apple Wallet, Google Wallet, im Browser oder weiter auf Papier.</p>
|
||||||
<div class="khero__txt">
|
<div class="row" data-rv><a class="oval oval--fill" href={KARTE_START} rel="noopener">Karte holen</a><a class="go" href={KARTE_DEMO} rel="noopener">So sieht es aus</a></div>
|
||||||
<p class="label">Kundenkarte</p>
|
<div class="wcard mt-4" aria-hidden="true" data-rv>
|
||||||
<h1>Sechs Stempel, <em>ein Pflege-Upgrade.</em></h1>
|
<div class="wcard__top"><span class="wcard__brand">Friseur-Team VK</span><span class="cap">Kundenkarte</span></div>
|
||||||
<p class="lead">Unsere Stempelkarte gibt es jetzt auch digital. Bei jedem Besuch scannen wir Ihre Karte an der Kasse, beim sechsten Stempel ist ein Pflege-Upgrade gratis.</p>
|
<div class="wcard__stamps">{[1, 2, 3, 4, 5, 6].map((i) => <span class:list={['wcard__s', { 'is-on': i <= 4 }]}></span>)}</div>
|
||||||
<div class="btn-row">
|
<div class="wcard__foot"><span><span class="cap">Stempel</span><br />4 von 6</span><span><span class="cap">Prämie</span><br />Pflege-Upgrade</span></div>
|
||||||
<a class="btn" href={KARTE_START} rel="noopener">Karte holen<Icon name="arrow" /></a>
|
|
||||||
<a class="btn btn--ghost" href={KARTE_DEMO} rel="noopener">So sieht’s aus</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</PageHead>
|
||||||
<div class="khero__card">
|
|
||||||
<div class="khero__panel" aria-hidden="true"></div>
|
|
||||||
<LoyaltyCard stamps={4} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" aria-labelledby="wege-h">
|
<section class="sec-b" aria-labelledby="w-h">
|
||||||
<div class="wrap">
|
<div class="pad"><H text="Ein Konto, vier Wege" /></div>
|
||||||
<div class="head">
|
<div class="rows mt-7">
|
||||||
<p class="label">Ein Konto, vier Wege</p>
|
{wege.map((w) => (
|
||||||
<h2 id="wege-h">Sie entscheiden, <em>wo die Karte liegt.</em></h2>
|
<div class="srow" data-rv>
|
||||||
|
<span class="srow__n">{w.n}</span>
|
||||||
|
<h3 class="srow__t">{w.t}</h3>
|
||||||
|
<span class="srow__d"><span>{w.d}</span></span>
|
||||||
|
<span></span>
|
||||||
</div>
|
</div>
|
||||||
<ol class="wege">
|
|
||||||
{wege.map((w, i) => (
|
|
||||||
<li class="wege__item reveal">
|
|
||||||
<span class="wege__n display" aria-hidden="true">{String(i + 1).padStart(2, '0')}</span>
|
|
||||||
<h3>{w.t}</h3>
|
|
||||||
<p>{w.d}</p>
|
|
||||||
</li>
|
|
||||||
))}
|
))}
|
||||||
</ol>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section section--nebel" aria-labelledby="so-h">
|
<section class="sec-b" aria-labelledby="p-h">
|
||||||
<div class="wrap so">
|
<div class="r3">
|
||||||
<div class="head">
|
<div class="ab"><H text="Die Papierkarte gilt weiter" class="h3" /></div>
|
||||||
<p class="label">So geht’s</p>
|
<div class="c copy" data-rv>
|
||||||
<h2 id="so-h">Einmal anmelden, <em>dann nur noch zeigen.</em></h2>
|
<p>Bringen Sie Ihre alte Stempelkarte zum nächsten Termin mit. Wir übertragen die Stempel auf Ihr neues Konto und Sie sammeln dort weiter. Wer kein Smartphone hat, bekommt eine gedruckte Karte mit QR-Code, die genauso funktioniert.</p>
|
||||||
</div>
|
</div>
|
||||||
<ol class="so__list">
|
|
||||||
<li><p><strong>Anmelden.</strong> Am Tresen liegt ein QR-Code. Scannen, Namen eingeben, Weg auswählen. Das geht auch vorab über den Knopf „Karte holen“.</p></li>
|
|
||||||
<li><p><strong>Stempeln.</strong> Zeigen Sie die Karte an der Kasse. Wir scannen sie, der Stempel ist sofort da.</p></li>
|
|
||||||
<li><p><strong>Einlösen.</strong> Beim sechsten Stempel bekommen Sie Ihr Pflege-Upgrade. Danach beginnt die Karte von vorn.</p></li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section" aria-labelledby="papier-h">
|
|
||||||
<div class="wrap papier">
|
|
||||||
<div>
|
|
||||||
<p class="label">Papierkarte</p>
|
|
||||||
<h2 id="papier-h" class="papier__h">Ihre alte Karte <em>gilt weiter.</em></h2>
|
|
||||||
</div>
|
|
||||||
<p class="lead">Bringen Sie Ihre Papier-Stempelkarte zum nächsten Termin mit. Wir übertragen die Stempel auf Ihr neues Konto, und Sie sammeln dort weiter.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section--tight" aria-labelledby="ds-h">
|
|
||||||
<div class="wrap ds">
|
|
||||||
<h2 id="ds-h" class="label">Datenschutz</h2>
|
|
||||||
<p>Für die Karte speichern wir nur, was dafür nötig ist: Ihren Namen und Ihre Stempel. Telefonnummer und E-Mail sind freiwillig. Werbung oder Erinnerungen schicken wir nur mit Ihrer ausdrücklichen, getrennten Einwilligung, die Sie jederzeit widerrufen können. Auf Wunsch löschen wir Ihr Konto vollständig. Fragen dazu beantworten wir unter <a href={`mailto:${salon.email}`}>{salon.email}</a>; Details stehen in der <a href="/datenschutz">Datenschutzerklärung</a>.</p>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<Faq items={faq} />
|
<Faq items={faq} />
|
||||||
|
|
||||||
<section class="section--tight kend">
|
|
||||||
<div class="wrap kend__in">
|
|
||||||
<h2 class="kend__h">Bereit für den ersten Stempel?</h2>
|
|
||||||
<div class="btn-row">
|
|
||||||
<a class="btn" href={KARTE_START} rel="noopener">Karte holen<Icon name="arrow" /></a>
|
|
||||||
<a class="btn btn--ghost" href={KARTE_DEMO} rel="noopener">So sieht’s aus</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.khero { padding-block: var(--s-7) var(--section); overflow: hidden; }
|
|
||||||
.khero__grid { display: grid; gap: var(--s-8); align-items: center; }
|
|
||||||
.khero__txt { display: grid; gap: var(--s-5); justify-items: start; }
|
|
||||||
.khero__txt h1 { max-width: 12ch; }
|
|
||||||
.khero__card { position: relative; display: grid; justify-items: center; padding-block: var(--s-7); }
|
|
||||||
.khero__panel { position: absolute; inset: 0 10% 0 10%; background: var(--c-petrol); border-radius: var(--arch); }
|
|
||||||
.khero__card :global(.lcard) { position: relative; }
|
|
||||||
@media (min-width: 60rem) { .khero__grid { grid-template-columns: 1.1fr 0.9fr; gap: var(--s-9); } }
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
.khero__card :global(.lcard) { animation: float-in 1s var(--ease-out) both 120ms; }
|
|
||||||
@keyframes float-in { from { opacity: 0; transform: translateY(28px) rotate(-2deg); } }
|
|
||||||
}
|
|
||||||
.wege { list-style: none; padding: 0; margin-top: var(--s-7); display: grid; gap: var(--s-6); }
|
|
||||||
@media (min-width: 52rem) { .wege { grid-template-columns: repeat(2, 1fr); gap: var(--s-7) var(--s-8); } }
|
|
||||||
.wege__item { border-top: 1.5px solid var(--c-anthrazit); padding-top: var(--s-4); display: grid; gap: var(--s-2); }
|
|
||||||
.wege__n { font-style: italic; color: var(--c-petrol); font-size: var(--fs-h4); }
|
|
||||||
.wege__item p { max-width: 44ch; }
|
|
||||||
.so { display: grid; gap: var(--s-7); }
|
|
||||||
@media (min-width: 60rem) { .so { grid-template-columns: 1fr 1.2fr; gap: var(--s-9); } }
|
|
||||||
.so__list { padding: 0; list-style: none; counter-reset: s; display: grid; gap: var(--s-5); }
|
|
||||||
.so__list li { counter-increment: s; display: grid; grid-template-columns: 3rem 1fr; }
|
|
||||||
.so__list li::before { content: counter(s); font-family: var(--font-display); font-size: 2rem; line-height: 1; color: var(--c-petrol); font-style: italic; }
|
|
||||||
.papier { display: grid; gap: var(--s-5); }
|
|
||||||
.papier__h { font-size: var(--fs-h2); margin-top: var(--s-3); }
|
|
||||||
@media (min-width: 60rem) { .papier { grid-template-columns: 1fr 1fr; gap: var(--s-9); align-items: end; } }
|
|
||||||
.ds { max-width: 50rem; display: grid; gap: var(--s-4); }
|
|
||||||
.kend__in { display: grid; gap: var(--s-5); justify-items: start; padding-bottom: var(--s-7); }
|
|
||||||
.kend__h { font-size: var(--fs-h3); }
|
|
||||||
</style>
|
|
||||||
@@ -1,92 +1,33 @@
|
|||||||
---
|
---
|
||||||
import Base from '../layouts/Base.astro';
|
import Base from '../layouts/Base.astro';
|
||||||
import PriceList from '../components/PriceList.astro';
|
import PageHead from '../components/PageHead.astro';
|
||||||
import CtaBand from '../components/CtaBand.astro';
|
import PriceGroup from '../components/PriceGroup.astro';
|
||||||
import Pic from '../components/Pic.astro';
|
import Closing from '../components/Closing.astro';
|
||||||
import Icon from '../components/Icon.astro';
|
|
||||||
import { priceGroups, salon } from '../data/site';
|
import { priceGroups, salon } from '../data/site';
|
||||||
import herren from '../assets/img/produkte-herren.jpg';
|
|
||||||
|
|
||||||
const premium = priceGroups.filter((g) => ['braut', 'farbe', 'verlaengerung'].includes(g.id));
|
|
||||||
const links: Record<string, string> = { braut: '/brautservice', farbe: '/balayage-farbe', verlaengerung: '/haarverlaengerung' };
|
|
||||||
---
|
---
|
||||||
<Base
|
<Base
|
||||||
title="Leistungen und Preise"
|
title="Leistungen und Preise"
|
||||||
description="Leistungen und Richtpreise beim Friseur-Team VK in Tostedt: Brautservice, Balayage und Farbe, Haarverlängerung mit Great Lengths, Damen, Herren und Barbershop, Kinder, Augenbrauen und Wimpern."
|
description="Alle Leistungen und Richtpreise vom Friseur-Team VK in Tostedt: Braut, Balayage und Farbe nach Haarlänge, Great Lengths, Damen, Herren, Kinder, Augenbrauen und Wimpern."
|
||||||
crumbs={[{ name: 'Leistungen & Preise', href: '/leistungen-preise' }]}
|
crumbs={[{ name: 'Preise', href: '/leistungen-preise' }]}
|
||||||
>
|
>
|
||||||
<section class="intro">
|
<PageHead crumb="Leistungen & Preise" title="Preise" sub="ehrlich vorab">
|
||||||
<div class="wrap intro__grid">
|
<p class="lead" data-rv>Alle Preise sind Richtwerte ab. Bei Farbe richten sie sich nach der Haarlänge, bei Braut und Great Lengths nennen wir Ihnen nach der Beratung einen festen Preis.</p>
|
||||||
<div>
|
<ul class="ticks small" data-rv>
|
||||||
<p class="label">Leistungen und Preise</p>
|
<li>Kurz: bis Kinn · Mittel: bis Schulter · Lang: über die Schulter</li>
|
||||||
<h1>Was wir machen, <em>und was es kostet.</em></h1>
|
<li>Farbberatung 25 €, wird beim Termin verrechnet</li>
|
||||||
</div>
|
<li>Great-Lengths-Beratung kostenlos</li>
|
||||||
<div class="intro__side">
|
</ul>
|
||||||
<p class="lead">Alle Preise sind „ab“-Werte. Wie viel es am Ende wird, hängt von Haarlänge, Dichte und Aufwand ab. Den genauen Preis nennen wir Ihnen vor der Behandlung.</p>
|
<nav aria-label="Zu den Bereichen" class="row" data-rv>
|
||||||
<nav aria-label="Leistungsbereiche" class="jump">
|
{priceGroups.map((g) => <a class="go" href={`#${g.id}`}>{g.title}</a>)}
|
||||||
{priceGroups.map((g) => <a href={`#preise-${g.id}`}>{g.title}</a>)}
|
|
||||||
</nav>
|
</nav>
|
||||||
|
</PageHead>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-label="Preisliste">
|
||||||
|
<div class="plist">
|
||||||
|
{priceGroups.map((g, i) => <PriceGroup group={g} n={String(i + 1).padStart(3, '0')} showLink />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<p class="cap pad from-c mt-4">Stand Oktober 2026. Absagen bitte bis 24 Stunden vorher, bei Braut und Great Lengths gelten eigene Fristen.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="section premium" aria-labelledby="prem-h">
|
<Closing title="Fragen zum Preis?" text={`Rufen Sie uns an, ${salon.phoneDisplay}. Bei Farbe, Braut und Great Lengths ist die Anfrage mit Foto der schnellste Weg zu einem festen Preis.`} />
|
||||||
<div class="wrap">
|
|
||||||
<h2 id="prem-h" class="label">Schwerpunkte</h2>
|
|
||||||
<div class="premium__grid">
|
|
||||||
{premium.map((g) => (
|
|
||||||
<div class="premium__col">
|
|
||||||
<PriceList ids={[g.id]} headingLevel={3} />
|
|
||||||
<a class="more" href={links[g.id]}><span>Mehr zu {g.title}</span><Icon name="arrow" /></a>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section section--nebel" aria-labelledby="alltag-h">
|
|
||||||
<div class="wrap alltag">
|
|
||||||
<div class="alltag__side">
|
|
||||||
<h2 id="alltag-h" class="label">Für jeden Tag</h2>
|
|
||||||
<figure class="alltag__img">
|
|
||||||
<div class="frame ratio-32">
|
|
||||||
<Pic src={herren} alt="Regal mit Pflege- und Stylingprodukten für Herren auf einer blauen Felge" sizes="(min-width: 60rem) 30vw, 100vw" widths={[400, 640, 954]} />
|
|
||||||
</div>
|
|
||||||
<figcaption>Barbershop-Ecke im Salon</figcaption>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
<PriceList ids={['damen', 'herren', 'kinder', 'augen']} headingLevel={3} />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="section--tight">
|
|
||||||
<div class="wrap note">
|
|
||||||
<p>Sie finden Ihre Leistung nicht? Rufen Sie uns an unter <a href={salon.phoneHref}>{salon.phoneDisplay}</a> oder fragen Sie über das <a href="/anfrage?anliegen=sonstiges">Anfrageformular</a>.</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<CtaBand />
|
|
||||||
</Base>
|
</Base>
|
||||||
|
|
||||||
<style>
|
|
||||||
.intro { padding-block: var(--s-7) 0; }
|
|
||||||
.intro__grid { display: grid; gap: var(--s-6); }
|
|
||||||
.intro h1 { margin-top: var(--s-4); max-width: 13ch; }
|
|
||||||
.intro__side { display: grid; gap: var(--s-5); align-content: end; }
|
|
||||||
@media (min-width: 60rem) { .intro__grid { grid-template-columns: 1.2fr 1fr; gap: var(--s-9); align-items: end; } }
|
|
||||||
.jump { display: flex; flex-wrap: wrap; gap: var(--s-2); }
|
|
||||||
.jump a {
|
|
||||||
display: inline-flex; align-items: center; min-height: var(--tap); padding: 0 1rem;
|
|
||||||
border: 1px solid var(--c-linie); border-radius: var(--r-pill);
|
|
||||||
color: var(--c-anthrazit); text-decoration: none; font-size: var(--fs-small); font-stretch: 90%;
|
|
||||||
}
|
|
||||||
.jump a:hover { border-color: var(--c-petrol); color: var(--c-petrol); }
|
|
||||||
.premium__grid { margin-top: var(--s-6); display: grid; gap: var(--s-8); }
|
|
||||||
@media (min-width: 64rem) { .premium__grid { grid-template-columns: repeat(3, 1fr); gap: var(--s-7); } }
|
|
||||||
.premium__col { display: grid; gap: var(--s-4); align-content: start; }
|
|
||||||
.premium__col :global(.plist__foot) { display: none; }
|
|
||||||
.alltag { display: grid; gap: var(--s-7); }
|
|
||||||
.alltag__img { margin: var(--s-5) 0 0; max-width: 28rem; }
|
|
||||||
@media (min-width: 60rem) { .alltag { grid-template-columns: 1fr 1.4fr; gap: var(--s-9); } .alltag__side { position: sticky; top: 7rem; align-self: start; } }
|
|
||||||
.note { max-width: 50rem; font-size: var(--fs-lead); }
|
|
||||||
</style>
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
import PageHead from '../components/PageHead.astro';
|
||||||
|
import Pic from '../components/Pic.astro';
|
||||||
|
import H from '../components/H.astro';
|
||||||
|
import Faq from '../components/Faq.astro';
|
||||||
|
import Closing from '../components/Closing.astro';
|
||||||
|
import { waits, salon, faqSchema } from '../data/site';
|
||||||
|
import empfang from '../assets/img/salon-empfang.jpg';
|
||||||
|
|
||||||
|
const faq = [
|
||||||
|
{ q: 'Warum kann ich nicht online buchen?', a: 'Weil wir bei Farbe, Braut und Great Lengths vorher wissen müssen, wie viel Zeit Ihr Haar braucht. Ein falsch geplanter Termin ärgert Sie und uns. Schnitt und Herren vergeben wir direkt am Telefon.' },
|
||||||
|
{ q: 'Was passiert nach meiner Anfrage?', a: 'Wir rufen Sie innerhalb von zwei Werktagen zu Ihrer Wunschzeit an. Absenden ist noch keine Buchung, den Termin machen wir im Gespräch fest.' },
|
||||||
|
{ q: 'Was kostet die Beratung?', a: 'Die Farbberatung kostet 25 € und wird beim Termin verrechnet. Die Beratung für Great Lengths und das Vorgespräch für Braut sind kostenlos, das Probestyling ist kostenpflichtig.' },
|
||||||
|
{ q: 'Und wenn ich absagen muss?', a: 'Bitte so früh wie möglich, spätestens 24 Stunden vorher. Für Braut und Great Lengths gelten längere Fristen, weil wir Zeit blocken oder Haar bestellen. Die nennen wir Ihnen vorher.' },
|
||||||
|
];
|
||||||
|
---
|
||||||
|
<Base
|
||||||
|
title="Neu bei uns"
|
||||||
|
description="So kommen neue Kundinnen zum Friseur-Team VK in Tostedt: Anfrage mit Foto, Rückruf in zwei Werktagen, Beratung bei Farbe und Great Lengths, Warteliste für kurzfristige Termine."
|
||||||
|
crumbs={[{ name: 'Neu bei uns', href: '/neue-kundinnen' }]}
|
||||||
|
schema={[faqSchema(faq)]}
|
||||||
|
>
|
||||||
|
<PageHead crumb="Neu bei uns" title="Neu bei uns" sub="so kommen Sie dran">
|
||||||
|
<p class="lead" data-rv>Wir sind oft ausgebucht. Das ist schön, aber für neue Kundinnen manchmal ärgerlich. Deshalb sagen wir hier offen, wie Sie trotzdem gut zu uns kommen.</p>
|
||||||
|
<div class="note" data-rv>
|
||||||
|
<p class="cap">Aktuelle Wartezeiten, Stand {waits.asOf}</p>
|
||||||
|
<ul class="ticks">{waits.items.map((w) => <li><b>{w.label}:</b> {w.value}</li>)}</ul>
|
||||||
|
</div>
|
||||||
|
</PageHead>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="r-h">
|
||||||
|
<div class="pad"><H text="Drei Wege" /></div>
|
||||||
|
<div class="steps mt-7">
|
||||||
|
<p class="steps__lead">Je nach<br />Wunsch</p>
|
||||||
|
<div class="step" data-rv><span class="step__n" aria-hidden="true">1</span><h3>Schnitt, Herren, Kinder</h3><p>Ohne Formular. Rufen Sie an oder schreiben Sie per WhatsApp, wir sagen Ihnen sofort, wann es passt.</p><a class="go" href={salon.phoneHref}>{salon.phoneDisplay}</a></div>
|
||||||
|
<div class="step d-1" data-rv><span class="step__n" aria-hidden="true">2</span><h3>Farbe und Great Lengths</h3><p>Anfrage mit Foto, danach eine kurze Beratung im Salon. Farbberatung 25 € (verrechnet), Great-Lengths-Beratung kostenlos.</p><a class="go" href="/anfrage?anliegen=balayage">Anfrage Farbe</a></div>
|
||||||
|
<div class="step d-2" data-rv><span class="step__n" aria-hidden="true">3</span><h3>Braut</h3><p>Anfrage mit Datum, Uhrzeit und Ort. Das Probestyling sichert Ihren Termin.</p><a class="go" href="/anfrage?anliegen=braut">Anfrage Braut</a></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="sec-b" aria-labelledby="w-h">
|
||||||
|
<div class="r3">
|
||||||
|
<div class="ab"><figure class="fig r-4x3" data-clip><Pic src={empfang} alt="Empfangstresen im Salon" sizes="(min-width: 56rem) 36vw, 100vw" /></figure></div>
|
||||||
|
<div class="c gap-5 self-end" data-rv>
|
||||||
|
<H text="Die Warteliste" class="h3" />
|
||||||
|
<p class="copy">Fast jede Woche sagt jemand ab. Wenn Sie in der Anfrage „Ich kann auch kurzfristig“ ankreuzen und Ihre Tage angeben, rufen wir Sie an, sobald etwas frei wird. So kommen viele neue Kundinnen schneller dran, als die Wartezeit oben vermuten lässt.</p>
|
||||||
|
<div class="row"><a class="oval oval--fill" href="/anfrage">Anfrage stellen</a></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Faq items={faq} />
|
||||||
|
<Closing />
|
||||||
|
</Base>
|
||||||