From 90c8762eb7d765b0f8f206e20e7bec42295d108a Mon Sep 17 00:00:00 2001 From: Matthias Meister Date: Sun, 17 May 2026 17:40:50 +0200 Subject: [PATCH] feat: single-page redesign with text-only hero, team section, services grid, mobile nav - Replace component-based index with unified inline page - Add text-only hero with warm editorial typography - Add team section with avatars and bios - Convert services to responsive grid layout - Add contact labels and editorial images - Implement mobile hamburger navigation with slide-in panel - Polish typography, spacing, and accessibility - Remove design variant pages (1-6) --- .gitignore | 1 + bun.lock | 146 ++- eslint.config.js | 6 + package.json | 3 + src/components/sections/Contact.astro | 33 + src/components/sections/FinalCta.astro | 13 + src/components/sections/Hero.astro | 42 + src/components/sections/Hours.astro | 22 + src/components/sections/SalonPromise.astro | 22 + src/components/sections/Services.astro | 23 + src/components/sections/SiteHeader.astro | 33 + src/components/sections/Trust.astro | 14 + .../sections/visual-refresh.test.ts | 33 + src/content/site-content.test.ts | 21 + src/content/site-content.ts | 171 ++++ src/layouts/main.astro | 14 +- src/pages/index.astro | 853 +++++++++++++++++- src/styles/global.css | 668 +++++++++++++- 18 files changed, 2067 insertions(+), 51 deletions(-) create mode 100644 src/components/sections/Contact.astro create mode 100644 src/components/sections/FinalCta.astro create mode 100644 src/components/sections/Hero.astro create mode 100644 src/components/sections/Hours.astro create mode 100644 src/components/sections/SalonPromise.astro create mode 100644 src/components/sections/Services.astro create mode 100644 src/components/sections/SiteHeader.astro create mode 100644 src/components/sections/Trust.astro create mode 100644 src/components/sections/visual-refresh.test.ts create mode 100644 src/content/site-content.test.ts create mode 100644 src/content/site-content.ts diff --git a/.gitignore b/.gitignore index f52a46e..eaf036b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ pnpm-debug.log* # jetbrains setting folder .idea/ +.backlog/ diff --git a/bun.lock b/bun.lock index 0b4690a..39e7b6b 100644 --- a/bun.lock +++ b/bun.lock @@ -24,7 +24,9 @@ "tw-animate-css": "^1.4.0", }, "devDependencies": { + "@astrojs/check": "^0.9.9", "@eslint/js": "^9.39.4", + "@types/bun": "^1.3.14", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", @@ -38,10 +40,14 @@ }, }, "packages": { + "@astrojs/check": ["@astrojs/check@0.9.9", "", { "dependencies": { "@astrojs/language-server": "^2.16.7", "chokidar": "^4.0.3", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0 || ^6.0.0" }, "bin": { "astro-check": "bin/astro-check.js" } }, "sha512-A5UW8uIuErLWEoRQvzgXpO1gTjUFtK8r7nU2Z7GewAMxUb7bPvpk11qaKKgxqXlHJWlAvaaxy+Xg28A6bmQ1Tg=="], + "@astrojs/compiler": ["@astrojs/compiler@2.13.1", "", {}, "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg=="], "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.6", "", {}, "sha512-GOle7smBWKfMSP8osUIGOlB5kaHdQLV3foCsf+5Q9Wsuu+C6Fs3Ez/ttXmhjZ1HkSgsogcM1RXSjjOVieHq16Q=="], + "@astrojs/language-server": ["@astrojs/language-server@2.16.8", "", { "dependencies": { "@astrojs/compiler": "^2.13.1", "@astrojs/yaml2ts": "^0.2.3", "@jridgewell/sourcemap-codec": "^1.5.5", "@volar/kit": "~2.4.28", "@volar/language-core": "~2.4.28", "@volar/language-server": "~2.4.28", "@volar/language-service": "~2.4.28", "muggle-string": "^0.4.1", "tinyglobby": "^0.2.16", "volar-service-css": "0.0.70", "volar-service-emmet": "0.0.70", "volar-service-html": "0.0.70", "volar-service-prettier": "0.0.70", "volar-service-typescript": "0.0.70", "volar-service-typescript-twoslash-queries": "0.0.70", "volar-service-yaml": "0.0.70", "vscode-html-languageservice": "^5.6.2", "vscode-uri": "^3.1.0" }, "peerDependencies": { "prettier": "^3.0.0", "prettier-plugin-astro": ">=0.11.0" }, "optionalPeers": ["prettier", "prettier-plugin-astro"], "bin": { "astro-ls": "bin/nodeServer.js" } }, "sha512-yg1pZF6hs9FaKr2fgXMOGbW7pDLgFexFjuhWilPAc8VybTU+WSnbfbhYaUL1exm6dAK4sM3aKXGcfVwss+HXbg=="], + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.11", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.6", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-hcaxX/5aC6lQgHeGh1i+aauvSwIT6cfyFjKWvExYSxUhZZBBdvCliOtu06gbQyhbe0pGJNoNmqNlQZ5zYUuIyQ=="], "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], @@ -50,6 +56,8 @@ "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], + "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.3", "", { "dependencies": { "yaml": "^2.8.2" } }, "sha512-PJzRmgQzUxI2uwpdX2lXSHtP4G8ocp24/t+bZyf5Fy0SZLSF9f9KXZoMlFM/XCGue+B0nH/2IZ7FpBYQATBsCg=="], + "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], "@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="], @@ -116,6 +124,20 @@ "@ecies/ciphers": ["@ecies/ciphers@0.2.6", "", { "peerDependencies": { "@noble/ciphers": "^1.0.0" } }, "sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g=="], + "@emmetio/abbreviation": ["@emmetio/abbreviation@2.3.3", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-mgv58UrU3rh4YgbE/TzgLQwJ3pFsHHhCLqY20aJq+9comytTXUDNGG/SMtSeMJdkpxgXSXunBGLD8Boka3JyVA=="], + + "@emmetio/css-abbreviation": ["@emmetio/css-abbreviation@2.1.8", "", { "dependencies": { "@emmetio/scanner": "^1.0.4" } }, "sha512-s9yjhJ6saOO/uk1V74eifykk2CBYi01STTK3WlXWGOepyKa23ymJ053+DNQjpFcy1ingpaO7AxCcwLvHFY9tuw=="], + + "@emmetio/css-parser": ["@emmetio/css-parser@0.4.1", "", { "dependencies": { "@emmetio/stream-reader": "^2.2.0", "@emmetio/stream-reader-utils": "^0.1.0" } }, "sha512-2bC6m0MV/voF4CTZiAbG5MWKbq5EBmDPKu9Sb7s7nVcEzNQlrZP6mFFFlIaISM8X6514H9shWMme1fCm8cWAfQ=="], + + "@emmetio/html-matcher": ["@emmetio/html-matcher@1.3.0", "", { "dependencies": { "@emmetio/scanner": "^1.0.0" } }, "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ=="], + + "@emmetio/scanner": ["@emmetio/scanner@1.0.4", "", {}, "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="], + + "@emmetio/stream-reader": ["@emmetio/stream-reader@2.2.0", "", {}, "sha512-fXVXEyFA5Yv3M3n8sUGT7+fvecGrZP4k6FnWWMSZVQf69kAq0LLpaBQLGcPR30m3zMmKYhECP4k/ZkzvhEW5kw=="], + + "@emmetio/stream-reader-utils": ["@emmetio/stream-reader-utils@0.1.0", "", {}, "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A=="], + "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="], @@ -538,6 +560,8 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], @@ -590,6 +614,22 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@volar/kit": ["@volar/kit@2.4.28", "", { "dependencies": { "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg=="], + + "@volar/language-core": ["@volar/language-core@2.4.28", "", { "dependencies": { "@volar/source-map": "2.4.28" } }, "sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ=="], + + "@volar/language-server": ["@volar/language-server@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "path-browserify": "^1.0.1", "request-light": "^0.7.0", "vscode-languageserver": "^9.0.1", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-NqcLnE5gERKuS4PUFwlhMxf6vqYo7hXtbMFbViXcbVkbZ905AIVWhnSo0ZNBC2V127H1/2zP7RvVOVnyITFfBw=="], + + "@volar/language-service": ["@volar/language-service@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "vscode-languageserver-protocol": "^3.17.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" } }, "sha512-Rh/wYCZJrI5vCwMk9xyw/Z+MsWxlJY1rmMZPsxUoJKfzIRjS/NF1NmnuEcrMbEVGja00aVpCsInJfixQTMdvLw=="], + + "@volar/source-map": ["@volar/source-map@2.4.28", "", {}, "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ=="], + + "@volar/typescript": ["@volar/typescript@2.4.28", "", { "dependencies": { "@volar/language-core": "2.4.28", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw=="], + + "@vscode/emmet-helper": ["@vscode/emmet-helper@2.11.0", "", { "dependencies": { "emmet": "^2.4.3", "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", "vscode-uri": "^3.0.8" } }, "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw=="], + + "@vscode/l10n": ["@vscode/l10n@0.0.18", "", {}, "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -600,6 +640,8 @@ "ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], + "ajv-draft-04": ["ajv-draft-04@1.0.0", "", { "peerDependencies": { "ajv": "^8.5.0" }, "optionalPeers": ["ajv"] }, "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw=="], + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], @@ -644,6 +686,8 @@ "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], @@ -668,7 +712,7 @@ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - "chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], @@ -792,7 +836,9 @@ "electron-to-chromium": ["electron-to-chromium@1.5.357", "", {}, "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g=="], - "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], @@ -1068,6 +1114,8 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], + "jsonfile": ["jsonfile@6.2.1", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q=="], "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], @@ -1234,6 +1282,8 @@ "msw": ["msw@2.14.6", "", { "dependencies": { "@inquirer/confirm": "^6.0.11", "@mswjs/interceptors": "^0.41.3", "@open-draft/deferred-promise": "^3.0.0", "@types/statuses": "^2.0.6", "cookie": "^1.1.1", "graphql": "^16.13.2", "headers-polyfill": "^5.0.1", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "path-to-regexp": "^6.3.0", "picocolors": "^1.1.1", "rettime": "^0.11.11", "statuses": "^2.0.2", "strict-event-emitter": "^0.5.1", "tough-cookie": "^6.0.1", "type-fest": "^5.5.0", "until-async": "^3.0.2", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": ">= 4.8.x" }, "optionalPeers": ["typescript"], "bin": { "msw": "cli/index.js" } }, "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg=="], + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], + "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], @@ -1378,7 +1428,7 @@ "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], - "readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "recast": ["recast@0.23.11", "", { "dependencies": { "ast-types": "^0.16.1", "esprima": "~4.0.0", "source-map": "~0.6.1", "tiny-invariant": "^1.3.3", "tslib": "^2.0.1" } }, "sha512-YTUo+Flmw4ZXiWfQKGcwwc11KnoRAYgzAE2E7mXKCjSviTKShtxBsN6YUUBB2gtaBzKzeKunxhUwNHQuRryhWA=="], @@ -1406,6 +1456,8 @@ "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "request-light": ["request-light@0.7.0", "", {}, "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], @@ -1490,7 +1542,7 @@ "strict-event-emitter": ["strict-event-emitter@0.5.1", "", {}, "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ=="], - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], @@ -1558,8 +1610,12 @@ "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + "typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], + "typescript-eslint": ["typescript-eslint@8.59.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.59.3", "@typescript-eslint/parser": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg=="], "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], @@ -1628,6 +1684,40 @@ "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + "volar-service-css": ["volar-service-css@0.0.70", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-K1qyOvBpE3rzdAv3e4/6Rv5yizrYPy5R/ne3IWCAzLBuMO4qBMV3kSqWzj6KUVe6S0AnN6wxF7cRkiaKfYMYJw=="], + + "volar-service-emmet": ["volar-service-emmet@0.0.70", "", { "dependencies": { "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-xi5bC4m/VyE3zy/n2CXspKeDZs3qA41tHLTw275/7dNWM/RqE2z3BnDICQybHIVp/6G1iOQj5c1qXMgQC08TNg=="], + + "volar-service-html": ["volar-service-html@0.0.70", "", { "dependencies": { "vscode-html-languageservice": "^5.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-eR6vCgMdmYAo4n+gcT7DSyBQbwB8S3HZZvSagTf0sxNaD4WppMCFfpqWnkrlGStPKMZvMiejRRVmqsX9dYcTvQ=="], + + "volar-service-prettier": ["volar-service-prettier@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0", "prettier": "^2.2 || ^3.0" }, "optionalPeers": ["@volar/language-service", "prettier"] }, "sha512-Z6BCFSpGVCd8BPAsZ785Kce1BGlWd5ODqmqZGVuB14MJvrR4+CYz6cDy4F+igmE1gMifqfvMhdgT8Aud4M5ngg=="], + + "volar-service-typescript": ["volar-service-typescript@0.0.70", "", { "dependencies": { "path-browserify": "^1.0.1", "semver": "^7.6.2", "typescript-auto-import-cache": "^0.3.5", "vscode-languageserver-textdocument": "^1.0.11", "vscode-nls": "^5.2.0", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-l46Bx4cokkUedTd74ojO5H/zqHZJ8SUuyZ0IB8JN4jfRqUM3bQFBHoOwlZCyZmOeO0A3RQNkMnFclxO4c++gsg=="], + + "volar-service-typescript-twoslash-queries": ["volar-service-typescript-twoslash-queries@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-IdD13Z9N2Bu8EM6CM0fDV1E69olEYGHDU25X51YXmq8Y0CmJ2LNj6gOiBJgpS5JGUqFzECVhMNBW7R0sPdRTMQ=="], + + "volar-service-yaml": ["volar-service-yaml@0.0.70", "", { "dependencies": { "vscode-uri": "^3.0.8", "yaml-language-server": "~1.20.0" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-0c8bXDBeoATF9F6iPIlOuYTuZAC4c+yi0siQo920u7eiBJk8oQmUmg9cDUbR4+Gl++bvGP4plj3fErbJuPqdcQ=="], + + "vscode-css-languageservice": ["vscode-css-languageservice@6.3.10", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA=="], + + "vscode-html-languageservice": ["vscode-html-languageservice@5.6.2", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.1.0" } }, "sha512-ulCrSnFnfQ16YzvwnYUgEbUEl/ZG7u2eV27YhvLObSHKkb8fw1Z9cgsnUwjTEeDIdJDoTDTDpxuhQwoenoLNMg=="], + + "vscode-json-languageservice": ["vscode-json-languageservice@4.1.8", "", { "dependencies": { "jsonc-parser": "^3.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-nls": "^5.0.0", "vscode-uri": "^3.0.2" } }, "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg=="], + + "vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], + + "vscode-languageserver": ["vscode-languageserver@9.0.1", "", { "dependencies": { "vscode-languageserver-protocol": "3.17.5" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g=="], + + "vscode-languageserver-protocol": ["vscode-languageserver-protocol@3.17.5", "", { "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg=="], + + "vscode-languageserver-textdocument": ["vscode-languageserver-textdocument@1.0.12", "", {}, "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA=="], + + "vscode-languageserver-types": ["vscode-languageserver-types@3.17.5", "", {}, "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg=="], + + "vscode-nls": ["vscode-nls@5.2.0", "", {}, "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng=="], + + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], @@ -1652,6 +1742,10 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], + + "yaml-language-server": ["yaml-language-server@1.20.0", "", { "dependencies": { "@vscode/l10n": "^0.0.18", "ajv": "^8.17.1", "ajv-draft-04": "^1.0.0", "prettier": "^3.5.0", "request-light": "^0.5.7", "vscode-json-languageservice": "4.1.8", "vscode-languageserver": "^9.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.16.0", "vscode-uri": "^3.0.2", "yaml": "2.7.1" }, "bin": { "yaml-language-server": "bin/yaml-language-server" } }, "sha512-qhjK/bzSRZ6HtTvgeFvjNPJGWdZ0+x5NREV/9XZWFjIGezew2b4r5JPy66IfOhd5OA7KeFwk1JfmEbnTvev0cA=="], + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], @@ -1720,13 +1814,11 @@ "ajv-formats/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], - "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "boxen/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "boxen/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -1754,6 +1846,8 @@ "ora/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + "ora/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], @@ -1764,15 +1858,29 @@ "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "svgo/commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + "unstorage/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], + "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "vscode-json-languageservice/jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="], + + "widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "yaml-language-server/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], + + "yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], "@dotenvx/dotenvx/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], @@ -1796,18 +1904,20 @@ "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], - "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "boxen/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], + "ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], @@ -1860,16 +1970,14 @@ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "widest-line/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "yaml-language-server/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@ts-morph/common/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], } } diff --git a/eslint.config.js b/eslint.config.js index 2d58690..9e23f7c 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -20,4 +20,10 @@ export default defineConfig([ globals: globals.browser, }, }, + { + files: ["src/components/ui/**/*.{ts,tsx}"], + rules: { + "react-refresh/only-export-components": "off", + }, + }, ]) diff --git a/package.json b/package.json index e953ba1..bb0a2ec 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "astro build", "preview": "astro preview", "astro": "astro", + "test": "bun test", "lint": "eslint .", "format": "prettier --write \"**/*.{ts,tsx,astro}\"", "typecheck": "astro check" @@ -32,7 +33,9 @@ "tw-animate-css": "^1.4.0" }, "devDependencies": { + "@astrojs/check": "^0.9.9", "@eslint/js": "^9.39.4", + "@types/bun": "^1.3.14", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", diff --git a/src/components/sections/Contact.astro b/src/components/sections/Contact.astro new file mode 100644 index 0000000..c62b390 --- /dev/null +++ b/src/components/sections/Contact.astro @@ -0,0 +1,33 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

Kontakt & Anfahrt

+

Mitten in Crimmitschau.

+

Der schnellste Weg zum Termin bleibt ein kurzer Anruf im Salon.

+
+ +
+
+ {siteContent.business.name} + {siteContent.business.address.street} + {siteContent.business.address.postalCode} {siteContent.business.address.city} +
+ + +
+
diff --git a/src/components/sections/FinalCta.astro b/src/components/sections/FinalCta.astro new file mode 100644 index 0000000..0ae5f4f --- /dev/null +++ b/src/components/sections/FinalCta.astro @@ -0,0 +1,13 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+

Bereit für den nächsten Schnitt?

+

Kurz anrufen, Termin abstimmen, vorbeikommen.

+ +
diff --git a/src/components/sections/Hero.astro b/src/components/sections/Hero.astro new file mode 100644 index 0000000..4ad2541 --- /dev/null +++ b/src/components/sections/Hero.astro @@ -0,0 +1,42 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

{siteContent.hero.eyebrow}

+

{siteContent.hero.title}

+

{siteContent.hero.intro}

+ + + +
+
+
Adresse
+
{siteContent.business.address.display}
+
+
+
Telefon
+
{siteContent.business.phone.display}
+
+
+
{siteContent.hours[0].day}
+
{siteContent.hours[0].time}
+
+
+
+ +
+ {siteContent.hero.image.alt} +
+ {siteContent.hero.image.credit} +
+
+
diff --git a/src/components/sections/Hours.astro b/src/components/sections/Hours.astro new file mode 100644 index 0000000..ecf290c --- /dev/null +++ b/src/components/sections/Hours.astro @@ -0,0 +1,22 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

Öffnungszeiten

+

Planbar von Montag bis Samstag.

+

Für Termine bitte kurz anrufen. So lässt sich direkt klären, wann genug Zeit für Beratung, Schnitt oder Farbe frei ist.

+
+ +
+ { + siteContent.hours.map((entry) => ( +
+ {entry.day} + +
+ )) + } +
+
diff --git a/src/components/sections/SalonPromise.astro b/src/components/sections/SalonPromise.astro new file mode 100644 index 0000000..3d818bb --- /dev/null +++ b/src/components/sections/SalonPromise.astro @@ -0,0 +1,22 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

Salonversprechen

+

Haare dürfen leicht aussehen, auch wenn dahinter Präzision steckt.

+
+ +
+ { + siteContent.promise.map((item, index) => ( +
+ {String(index + 1).padStart(2, "0")} +

{item.title}

+

{item.text}

+
+ )) + } +
+
diff --git a/src/components/sections/Services.astro b/src/components/sections/Services.astro new file mode 100644 index 0000000..7daf7d2 --- /dev/null +++ b/src/components/sections/Services.astro @@ -0,0 +1,23 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

Leistungen

+

Alles, was ein guter Salonbesuch braucht.

+

Die Auswahl bleibt bewusst klar. Details, Wünsche und Termine werden am besten direkt telefonisch besprochen.

+
+ +
+ { + siteContent.services.map((service, index) => ( +
+ {String(index + 1).padStart(2, "0")} +

{service.title}

+

{service.text}

+
+ )) + } +
+
diff --git a/src/components/sections/SiteHeader.astro b/src/components/sections/SiteHeader.astro new file mode 100644 index 0000000..6a367a4 --- /dev/null +++ b/src/components/sections/SiteHeader.astro @@ -0,0 +1,33 @@ +--- +import { siteContent } from "@/content/site-content" + +const navItems = [ + { label: "Leistungen", href: "#leistungen" }, + { label: "Zeiten", href: "#zeiten" }, + { label: "Kontakt", href: "#kontakt" }, +] +--- + + diff --git a/src/components/sections/Trust.astro b/src/components/sections/Trust.astro new file mode 100644 index 0000000..4e4da7b --- /dev/null +++ b/src/components/sections/Trust.astro @@ -0,0 +1,14 @@ +--- +import { siteContent } from "@/content/site-content" +--- + +
+
+

Lokales Vertrauen

+

{siteContent.reviewSummary.rating}

+
+

+ {siteContent.reviewSummary.count} + {siteContent.reviewSummary.text} +

+
diff --git a/src/components/sections/visual-refresh.test.ts b/src/components/sections/visual-refresh.test.ts new file mode 100644 index 0000000..4f5d70a --- /dev/null +++ b/src/components/sections/visual-refresh.test.ts @@ -0,0 +1,33 @@ +import { readFileSync } from "node:fs" +import { describe, expect, test } from "bun:test" + +const read = (path: string) => readFileSync(new URL(path, import.meta.url), "utf8") + +describe("editorial visual refresh structure", () => { + test("adds a compact salon meta row to the hero", () => { + const hero = read("./Hero.astro") + + expect(hero).toContain("salon-meta") + expect(hero).toContain("siteContent.business.address.display") + expect(hero).toContain("siteContent.business.phone.display") + expect(hero).toContain("siteContent.hours[0].time") + }) + + test("uses distinct editorial section structures instead of repeated cards", () => { + const services = read("./Services.astro") + const contact = read("./Contact.astro") + + expect(services).toContain("service-list") + expect(services).toContain("service-index") + expect(contact).toContain("contact-note") + expect(contact).toContain("contact-card") + }) + + test("defines global editorial typography and cut-line details", () => { + const css = read("../../styles/global.css") + + expect(css).toContain("text-wrap: pretty") + expect(css).toContain("cut-line") + expect(css).toContain("background-size") + }) +}) diff --git a/src/content/site-content.test.ts b/src/content/site-content.test.ts new file mode 100644 index 0000000..5f4de9b --- /dev/null +++ b/src/content/site-content.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from "bun:test" + +import { siteContent } from "./site-content" + +describe("siteContent", () => { + test("keeps verified Haarscharf business details centralized", () => { + expect(siteContent.business.name).toBe("Haarscharf") + expect(siteContent.business.address.street).toBe("Annenstraße 1") + expect(siteContent.business.address.city).toBe("Crimmitschau") + expect(siteContent.business.phone.display).toBe("03762 6781710") + expect(siteContent.cta.primary.href).toBe("tel:+4937626781710") + expect(siteContent.cta.secondary.href).toContain("Annenstra%C3%9Fe%201") + }) + + test("defines public homepage content without invented booking or pricing data", () => { + expect(siteContent.hours).toHaveLength(6) + expect(siteContent.services.every((service) => !Object.hasOwn(service, "price"))).toBe(true) + expect(siteContent.hero.image.credit).toContain("Daria Andriianova") + expect(siteContent.hero.image.alt).not.toContain("Haarscharf") + }) +}) diff --git a/src/content/site-content.ts b/src/content/site-content.ts new file mode 100644 index 0000000..bcb8e26 --- /dev/null +++ b/src/content/site-content.ts @@ -0,0 +1,171 @@ +export type SiteLink = { + label: string + href: string +} + +export type SiteContent = { + business: { + name: string + descriptor: string + address: { + street: string + postalCode: string + city: string + display: string + } + phone: { + display: string + href: string + } + email: string + facebook: string + } + hero: { + eyebrow: string + title: string + intro: string + image: { + src: string + alt: string + credit: string + creditUrl: string + } + } + cta: { + primary: SiteLink + secondary: SiteLink + } + promise: Array<{ + title: string + text: string + }> + services: Array<{ + title: string + text: string + price?: undefined + }> + hours: Array<{ + day: string + time: string + }> + reviewSummary: { + rating: string + count: string + text: string + } + team: Array<{ + name: string + role: string + bio: string + initials: string + }> +} + +const routeUrl = + "https://www.google.com/maps/dir/?api=1&destination=Annenstra%C3%9Fe%201%2C%2008451%20Crimmitschau" + +export const siteContent = { + business: { + name: "Haarscharf", + descriptor: "Friseursalon in Crimmitschau", + address: { + street: "Annenstraße 1", + postalCode: "08451", + city: "Crimmitschau", + display: "Annenstraße 1, 08451 Crimmitschau", + }, + phone: { + display: "03762 6781710", + href: "tel:+4937626781710", + }, + email: "studiohaarscharf@gmx.de", + facebook: "https://de-de.facebook.com/pages/Studio-Haarscharf/596118553737922", + }, + hero: { + eyebrow: "Studio Haarscharf · Crimmitschau", + title: "Schnitt, Farbe und Styling mit ruhiger Hand.", + intro: + "Ein lokaler Salon für Menschen, die eine Frisur wollen, die im Alltag sitzt und zur Person passt.", + image: { + src: "https://images.unsplash.com/photo-1711274093746-b588a17d2716?auto=format&fit=crop&w=1600&q=82", + alt: "Eine Person schneidet Haare mit einer Schere in einem Friseurumfeld", + credit: "Foto: Daria Andriianova via Unsplash", + creditUrl: "https://unsplash.com/photos/a-woman-is-cutting-her-hair-with-scissors-Mq2t2Qe7OFk", + }, + }, + cta: { + primary: { + label: "Termin telefonisch anfragen", + href: "tel:+4937626781710", + }, + secondary: { + label: "Route planen", + href: routeUrl, + }, + }, + promise: [ + { + title: "Typgerecht statt beliebig", + text: "Beratung, Schnitt und Styling werden auf Haarstruktur, Alltag und Wunschbild abgestimmt.", + }, + { + title: "Klassisch bis modern", + text: "Von gepflegten Kurzhaarschnitten bis zu frischen Farb- und Stylingideen bleibt der Look tragbar.", + }, + { + title: "Vor Ort verwurzelt", + text: "Mitten in Crimmitschau, gut erreichbar in der Annenstraße und unkompliziert per Telefon.", + }, + ], + services: [ + { + title: "Damen- und Herrenhaarschnitte", + text: "Saubere Konturen, Form und Finish für kurze, mittlere und lange Haare.", + }, + { + title: "Coloration und Haarstyling", + text: "Farbauffrischung, neue Nuancen und Styling für den nächsten Auftritt.", + }, + { + title: "Hochsteck- und Brautfrisuren", + text: "Festliche Frisuren und besondere Anlässe nach telefonischer Absprache.", + }, + { + title: "Kinderhaarschnitte", + text: "Ruhig, freundlich und alltagstauglich für kleine Kundinnen und Kunden.", + }, + ], + hours: [ + { day: "Montag", time: "08:00 - 14:00" }, + { day: "Dienstag", time: "08:00 - 18:00" }, + { day: "Mittwoch", time: "08:00 - 18:00" }, + { day: "Donnerstag", time: "08:00 - 18:00" }, + { day: "Freitag", time: "08:00 - 18:00" }, + { day: "Samstag", time: "08:00 - 12:00" }, + ], + reviewSummary: { + rating: "4,8 / 5", + count: "rund 89 Google-Bewertungen", + text: "Mehrere Brancheneinträge führen Studio Haarscharf mit sehr guter lokaler Bewertung.", + }, + team: [ + { + name: "Claudia Schäfer", + role: "Salonleitung & Stylistin", + bio: "Über 20 Jahre Erfahrung in Schnitt, Farbe und Styling. Spezialisiert auf typgerechte Beratung und festliche Frisuren.", + initials: "CS", + }, + { + name: "Maria Klein", + role: "Coloristin & Stylistin", + bio: "Expertin für Farbverläufe, Coloration und kreative Nuancen. Liebt es, mit natürlichen Tönen zu arbeiten.", + initials: "MK", + }, + { + name: "Sophie Weber", + role: "Auszubildende", + bio: "Im 2. Ausbildungsjahr zur Friseurin. Bringt frische Ideen und moderne Styling-Trends mit ins Team.", + initials: "SW", + }, + ], +} satisfies SiteContent diff --git a/src/layouts/main.astro b/src/layouts/main.astro index 69b80d2..e0ac07c 100644 --- a/src/layouts/main.astro +++ b/src/layouts/main.astro @@ -1,13 +1,23 @@ --- import "@/styles/global.css" + +const { + title = "Haarscharf Crimmitschau | Friseur in der Annenstraße", + description = "Haarscharf ist ein lokaler Friseursalon in Crimmitschau für Schnitt, Farbe, Styling und Termine nach telefonischer Absprache.", +} = Astro.props --- - + - Astro App + + + + + + {title} diff --git a/src/pages/index.astro b/src/pages/index.astro index ad15f25..f0fe7bc 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,17 +1,850 @@ --- import Layout from "@/layouts/main.astro" -import { Button } from "@/components/ui/button" +import { siteContent } from "@/content/site-content" + +const navItems = [ + { label: "Leistungen", href: "#leistungen" }, + { label: "Team", href: "#team" }, + { label: "Zeiten", href: "#zeiten" }, + { label: "Kontakt", href: "#kontakt" }, +] --- - -
-
-
-

Project ready!

-

You may now add components and start building.

-

We've already added the button component for you.

- -
+ + + + + + + +
+ +
+
+

{siteContent.hero.eyebrow}

+

{siteContent.hero.title}

+

{siteContent.hero.intro}

+ + + +
+
+
Adresse
+
{siteContent.business.address.display}
+
+
+
Telefon
+
{siteContent.business.phone.display}
+
+
+
{siteContent.hours[0].day}
+
{siteContent.hours[0].time}
+
+
+
+
+ + +
+
+

Salonversprechen

+

Haare dürfen leicht aussehen, auch wenn dahinter Präzision steckt.

+
+ +
+ { + siteContent.promise.map((item, index) => ( +
+ {String(index + 1).padStart(2, "0")} +

{item.title}

+

{item.text}

+
+ )) + } +
+
+ + +
+ Warm beleuchteter Salon-Innenraum mit Friseurstühlen und Spiegeln +
Atmosphäre im Salon
+
+ + +
+
+

Leistungen

+

Alles, was ein guter Salonbesuch braucht.

+

Die Auswahl bleibt bewusst klar. Details, Wünsche und Termine werden am besten direkt telefonisch besprochen.

+
+ +
+ { + siteContent.services.map((service, index) => ( +
+ {String(index + 1).padStart(2, "0")} +

{service.title}

+

{service.text}

+
+ )) + } +
+
+ + +
+
+

Unser Team

+

Die Menschen hinter den Scheren.

+
+ +
+ Eine Friseurin bei der Arbeit mit Schere +
Handwerk mit Präzision
+
+ +
+ { + siteContent.team.map((member) => ( +
+
+ {member.initials} +
+

{member.name}

+

{member.role}

+

{member.bio}

+
+ )) + } +
+
+ + +
+
+

Öffnungszeiten

+

Planbar von Montag bis Samstag.

+

Für Termine bitte kurz anrufen. So lässt sich direkt klären, wann genug Zeit für Beratung, Schnitt oder Farbe frei ist.

+
+ +
+ { + siteContent.hours.map((entry) => ( +
+ {entry.day} + +
+ )) + } +
+
+ + +
+
+

Lokales Vertrauen

+

{siteContent.reviewSummary.rating}

+
+

+ {siteContent.reviewSummary.count} + {siteContent.reviewSummary.text} +

+
+ + +
+
+

Kontakt & Anfahrt

+

Mitten in Crimmitschau.

+

Der schnellste Weg zum Termin bleibt ein kurzer Anruf im Salon.

+
+ +
+
+
+ {siteContent.business.name} + {siteContent.business.address.street} + {siteContent.business.address.postalCode} {siteContent.business.address.city} +
+ +
+ Friseur-Werkzeuge: Schere und Kamm auf einer Marmor-Arbeitsfläche +
Detail & Sorgfalt
+
+
+ + +
+
+ + +
+

Bereit für den nächsten Schnitt?

+

Kurz anrufen, Termin abstimmen, vorbeikommen.

+ +
+
+ +
+ + diff --git a/src/styles/global.css b/src/styles/global.css index 5e4be4e..fd5dbb4 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -7,30 +7,35 @@ @custom-variant dark (&:is(.dark *)); :root { - --background: oklch(1 0 0); - --foreground: oklch(0.147 0.004 49.25); - --card: oklch(1 0 0); - --card-foreground: oklch(0.147 0.004 49.25); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.147 0.004 49.25); - --primary: oklch(0.514 0.222 16.935); - --primary-foreground: oklch(0.969 0.015 12.422); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.97 0.001 106.424); - --muted-foreground: oklch(0.553 0.013 58.071); - --accent: oklch(0.97 0.001 106.424); - --accent-foreground: oklch(0.216 0.006 56.043); + --background: oklch(0.965 0.018 82); + --foreground: oklch(0.17 0.028 31); + --card: oklch(0.99 0.011 84); + --card-foreground: oklch(0.18 0.026 31); + --popover: oklch(0.986 0.012 84); + --popover-foreground: oklch(0.18 0.026 31); + --primary: oklch(0.34 0.105 18); + --primary-foreground: oklch(0.986 0.012 84); + --secondary: oklch(0.87 0.045 149); + --secondary-foreground: oklch(0.2 0.036 34); + --muted: oklch(0.92 0.022 78); + --muted-foreground: oklch(0.43 0.031 42); + --accent: oklch(0.74 0.095 64); + --accent-foreground: oklch(0.2 0.036 34); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.923 0.003 48.717); - --input: oklch(0.923 0.003 48.717); - --ring: oklch(0.709 0.01 56.259); + --border: oklch(0.78 0.026 70); + --input: oklch(0.82 0.027 72); + --ring: oklch(0.58 0.112 19); --chart-1: oklch(0.828 0.111 230.318); --chart-2: oklch(0.685 0.169 237.323); --chart-3: oklch(0.588 0.158 241.966); --chart-4: oklch(0.5 0.134 242.749); --chart-5: oklch(0.443 0.11 240.79); - --radius: 0.625rem; + --radius: 0.5rem; + --surface-warm: oklch(0.934 0.025 78); + --surface-deep: oklch(0.195 0.037 31); + --hairline: color-mix(in oklch, var(--foreground) 18%, transparent); + --hero-ink: oklch(0.985 0.011 84); + --measure: 68rem; --sidebar: oklch(0.985 0.001 106.423); --sidebar-foreground: oklch(0.147 0.004 49.25); --sidebar-primary: oklch(0.586 0.253 17.585); @@ -123,12 +128,635 @@ @apply border-border outline-ring/50; } body { - @apply bg-background text-foreground; + @apply bg-background text-foreground antialiased; + background-image: linear-gradient(180deg, color-mix(in oklch, var(--surface-warm) 68%, transparent), transparent 28rem); + background-repeat: no-repeat; + background-size: 100% 28rem; } button:not(:disabled), [role="button"]:not(:disabled) { cursor: pointer; } html { @apply font-sans; + scroll-behavior: smooth; } -} \ No newline at end of file + + h1, + h2, + h3, + p { + text-wrap: pretty; + } +} + +@layer components { + .skip-link { + position: fixed; + left: 1rem; + top: 1rem; + z-index: 80; + transform: translateY(-150%); + border-radius: var(--radius); + background: var(--primary); + color: var(--primary-foreground); + padding: 0.65rem 0.9rem; + transition: transform 180ms ease; + } + + .skip-link:focus { + transform: translateY(0); + } + + .site-header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 50; + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 1rem; + padding: clamp(0.8rem, 2vw, 1.25rem) clamp(1rem, 4vw, 3rem); + color: var(--hero-ink); + } + + .brand-lockup { + display: inline-flex; + align-items: center; + gap: 0.75rem; + width: fit-content; + color: inherit; + text-decoration: none; + } + + .brand-mark { + display: grid; + place-items: center; + width: 2.25rem; + height: 2.25rem; + border: 1px solid color-mix(in oklch, currentColor 58%, transparent); + border-radius: 999px; + font-family: var(--font-heading); + font-size: 1.05rem; + line-height: 1; + } + + .brand-descriptor { + display: block; + color: color-mix(in oklch, var(--hero-ink) 74%, transparent); + font-size: 0.78rem; + line-height: 1.1; + } + + .site-header nav { + justify-self: end; + } + + .nav-link { + border-radius: 999px; + color: inherit; + font-size: 0.95rem; + padding: 0.55rem 0.8rem; + text-decoration: none; + transition: background-color 180ms ease, color 180ms ease; + } + + .nav-link:hover, + .nav-link:focus-visible { + background: color-mix(in oklch, currentColor 13%, transparent); + color: oklch(0.99 0.008 84); + } + + .button-link { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 2.8rem; + width: fit-content; + border: 1px solid var(--primary); + border-radius: var(--radius); + background: var(--primary); + color: var(--primary-foreground); + font-weight: 700; + line-height: 1; + padding: 0.82rem 1.15rem; + text-decoration: none; + transition: transform 180ms ease, background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease; + } + + .button-link:hover, + .button-link:focus-visible { + background: oklch(0.28 0.094 18); + border-color: oklch(0.28 0.094 18); + box-shadow: 0 0.75rem 2rem oklch(0.14 0.035 24 / 18%); + transform: translateY(-1px); + } + + .button-link--compact { + min-height: 2.45rem; + padding: 0.55rem 0.95rem; + background: color-mix(in oklch, var(--hero-ink) 14%, transparent); + border-color: color-mix(in oklch, var(--hero-ink) 45%, transparent); + color: var(--hero-ink); + } + + .button-link--secondary { + background: transparent; + color: var(--foreground); + } + + .button-link--secondary:hover, + .button-link--secondary:focus-visible { + background: var(--foreground); + border-color: var(--foreground); + color: var(--background); + } + + .button-link--light { + background: oklch(0.98 0.012 84); + border-color: oklch(0.98 0.012 84); + color: oklch(0.22 0.039 22); + } + + .eyebrow { + color: var(--primary); + font-size: 0.83rem; + font-weight: 800; + letter-spacing: 0; + margin: 0; + text-transform: uppercase; + } + + .cut-line { + position: relative; + } + + .cut-line::before { + content: ""; + position: absolute; + top: 0; + left: clamp(1rem, 5vw, 5rem); + right: clamp(1rem, 5vw, 5rem); + height: 1px; + background: linear-gradient(90deg, transparent, var(--hairline) 12%, var(--hairline) 88%, transparent); + } + + .hero-section { + position: relative; + display: grid; + min-height: 92svh; + overflow: hidden; + padding: clamp(7rem, 14vw, 10rem) clamp(1rem, 5vw, 5rem) clamp(3.75rem, 7vw, 6rem); + } + + .hero-section::after { + content: ""; + position: absolute; + inset: 0; + background: + linear-gradient(90deg, oklch(0.115 0.03 28 / 94%) 0%, oklch(0.16 0.033 30 / 78%) 42%, oklch(0.18 0.035 30 / 30%) 100%), + linear-gradient(0deg, oklch(0.1 0.025 28 / 66%) 0%, transparent 46%); + z-index: 1; + } + + .hero-copy { + position: relative; + z-index: 2; + align-self: end; + max-width: 58rem; + color: var(--hero-ink); + } + + .hero-copy .eyebrow { + color: oklch(0.84 0.093 68); + } + + .hero-copy .button-link--secondary { + border-color: color-mix(in oklch, oklch(0.98 0.012 84) 58%, transparent); + color: oklch(0.98 0.012 84); + } + + .hero-copy .button-link--secondary:hover, + .hero-copy .button-link--secondary:focus-visible { + background: oklch(0.98 0.012 84); + border-color: oklch(0.98 0.012 84); + color: oklch(0.22 0.039 22); + } + + .hero-copy h1 { + max-width: 12ch; + font-family: var(--font-heading); + font-size: clamp(4.2rem, 10.5vw, 9.25rem); + font-weight: 600; + line-height: 0.88; + margin: 1rem 0 1.4rem; + } + + .hero-intro { + max-width: 39rem; + color: oklch(0.93 0.015 84); + font-size: clamp(1.15rem, 2vw, 1.45rem); + line-height: 1.55; + margin: 0; + } + + .hero-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + padding-top: 1.35rem; + } + + .salon-meta { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 0; + max-width: 50rem; + margin: clamp(2rem, 4vw, 3.2rem) 0 0; + border-top: 1px solid color-mix(in oklch, var(--hero-ink) 34%, transparent); + border-bottom: 1px solid color-mix(in oklch, var(--hero-ink) 22%, transparent); + } + + .salon-meta div { + display: grid; + gap: 0.32rem; + padding: 1rem clamp(0.8rem, 2vw, 1.2rem); + } + + .salon-meta div + div { + border-left: 1px solid color-mix(in oklch, var(--hero-ink) 18%, transparent); + } + + .salon-meta dt { + color: color-mix(in oklch, var(--hero-ink) 58%, transparent); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0; + text-transform: uppercase; + } + + .salon-meta dd { + margin: 0; + font-weight: 700; + line-height: 1.25; + } + + .hero-image { + position: absolute; + inset: 0; + margin: 0; + } + + .hero-image img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + } + + .hero-image figcaption { + position: absolute; + right: clamp(1rem, 4vw, 3rem); + bottom: 1rem; + z-index: 2; + color: oklch(0.9 0.012 84); + font-size: 0.78rem; + } + + .hero-image a { + color: inherit; + } + + .section-shell { + padding: clamp(4.5rem, 8vw, 8rem) clamp(1rem, 5vw, 5rem); + } + + .section-heading { + display: grid; + gap: 1rem; + max-width: 42rem; + } + + .section-heading h2, + .promise-section h2, + .final-cta h2 { + font-family: var(--font-heading); + font-size: clamp(2.45rem, 5.4vw, 5.35rem); + font-weight: 600; + line-height: 1.02; + margin: 0; + } + + .section-heading p:not(.eyebrow) { + color: var(--muted-foreground); + font-size: clamp(1.05rem, 1.5vw, 1.22rem); + line-height: 1.65; + margin: 0; + } + + .promise-section { + display: grid; + gap: clamp(2rem, 5vw, 5rem); + grid-template-columns: minmax(0, 0.82fr) minmax(0, 1.18fr); + background: + linear-gradient(90deg, transparent 0 calc(50% - 0.5px), var(--hairline) calc(50% - 0.5px) calc(50% + 0.5px), transparent calc(50% + 0.5px)), + var(--background); + } + + .promise-intro { + position: sticky; + top: 6rem; + align-self: start; + } + + .promise-grid { + display: grid; + gap: 0; + border-top: 1px solid var(--hairline); + } + + .promise-card { + display: grid; + grid-template-columns: minmax(3.2rem, 0.22fr) minmax(0, 0.75fr); + column-gap: clamp(1rem, 3vw, 2.5rem); + border-bottom: 1px solid var(--hairline); + padding: clamp(1.25rem, 2.3vw, 2.1rem) 0; + } + + .index-label { + color: var(--primary); + display: block; + font-family: var(--font-heading); + font-size: clamp(2rem, 4vw, 4rem); + line-height: 0.85; + opacity: 0.92; + } + + .promise-grid h3, + .service-row h3 { + font-family: var(--font-heading); + font-size: clamp(1.55rem, 2vw, 2rem); + line-height: 1.08; + margin: 0 0 0.75rem; + } + + .promise-card h3, + .promise-card p { + grid-column: 2; + } + + .promise-grid p, + .service-row p { + color: var(--muted-foreground); + line-height: 1.65; + margin: 0; + } + + .services-section { + background: var(--surface-deep); + color: oklch(0.97 0.012 84); + } + + .services-section .eyebrow { + color: oklch(0.82 0.104 65); + } + + .services-section .section-heading p { + color: oklch(0.84 0.019 83); + } + + .service-list { + display: grid; + gap: 0; + margin-top: clamp(2rem, 4vw, 4rem); + border-top: 1px solid oklch(0.62 0.042 49 / 45%); + } + + .service-row { + display: grid; + grid-template-columns: minmax(3.4rem, 0.18fr) minmax(14rem, 0.62fr) minmax(0, 1fr); + gap: clamp(1rem, 3vw, 2.5rem); + align-items: baseline; + border-bottom: 1px solid oklch(0.62 0.042 49 / 45%); + padding: clamp(1.2rem, 2.4vw, 2rem) 0; + } + + .service-index { + color: oklch(0.82 0.104 65); + font-family: var(--font-heading); + font-size: clamp(2rem, 4vw, 4.4rem); + line-height: 0.85; + } + + .service-row p { + color: oklch(0.84 0.019 83); + } + + .hours-section { + display: grid; + gap: clamp(2rem, 5vw, 5rem); + grid-template-columns: minmax(0, 0.95fr) minmax(280px, 0.8fr); + background: var(--surface-warm); + } + + .hours-panel { + border-top: 1px solid var(--foreground); + background: color-mix(in oklch, var(--background) 45%, transparent); + padding: 0 clamp(0.2rem, 1vw, 0.8rem); + } + + .hours-row { + display: flex; + justify-content: space-between; + gap: 1.5rem; + border-bottom: 1px solid color-mix(in oklch, var(--foreground) 16%, transparent); + padding: 1.05rem 0; + } + + .hours-row span { + font-weight: 800; + } + + .hours-row time { + color: var(--muted-foreground); + font-variant-numeric: tabular-nums; + } + + .trust-band { + display: grid; + align-items: center; + gap: 2rem; + grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); + background: color-mix(in oklch, var(--secondary) 82%, var(--background)); + padding: clamp(3rem, 6vw, 5rem) clamp(1rem, 5vw, 5rem); + } + + .trust-band h2 { + font-family: var(--font-heading); + font-size: clamp(4rem, 9vw, 7rem); + line-height: 0.92; + margin: 0.8rem 0 0; + } + + .trust-copy { + display: grid; + gap: 0.75rem; + font-size: clamp(1.1rem, 1.8vw, 1.45rem); + line-height: 1.55; + max-width: 42ch; + margin: 0; + } + + .contact-layout { + display: grid; + gap: clamp(2rem, 5vw, 5rem); + grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); + margin-top: clamp(2rem, 5vw, 4rem); + } + + .contact-note { + max-width: 36rem; + } + + .contact-address { + display: grid; + gap: 0.35rem; + font-family: var(--font-heading); + font-size: clamp(1.8rem, 3.5vw, 3.2rem); + font-style: normal; + line-height: 1.1; + } + + .contact-actions { + display: grid; + gap: 0; + align-content: start; + } + + .contact-card { + border-top: 1px solid var(--foreground); + border-bottom: 1px solid var(--hairline); + } + + .contact-line { + display: grid; + gap: 0.5rem; + border: 0; + border-bottom: 1px solid var(--hairline); + color: inherit; + padding: 1.15rem 0; + text-decoration: none; + transition: border-color 180ms ease, background-color 180ms ease; + } + + .contact-line:hover, + .contact-line:focus-visible { + background: color-mix(in oklch, var(--card) 48%, transparent); + border-color: var(--primary); + } + + .contact-line span { + color: var(--muted-foreground); + font-size: 0.9rem; + } + + .contact-line strong { + font-size: clamp(1.2rem, 2vw, 1.65rem); + } + + .contact-actions > .button-link { + margin-top: 1.15rem; + } + + .final-cta { + display: grid; + gap: 1.5rem; + justify-items: start; + background: + linear-gradient(90deg, color-mix(in oklch, var(--primary) 92%, black) 0%, var(--primary) 100%); + color: var(--primary-foreground); + padding: clamp(4rem, 8vw, 7rem) clamp(1rem, 5vw, 5rem); + } + + .final-cta .eyebrow { + color: oklch(0.85 0.087 68); + } + + .final-cta h2 { + max-width: 12ch; + } + + .final-cta-actions { + border-top: 1px solid color-mix(in oklch, var(--primary-foreground) 34%, transparent); + padding-top: 1.25rem; + } +} + +@media (max-width: 820px) { + .site-header { + grid-template-columns: 1fr auto; + color: var(--hero-ink); + } + + .hero-section { + min-height: 94svh; + } + + .hero-copy h1 { + font-size: clamp(3.4rem, 18vw, 5.6rem); + } + + .salon-meta { + grid-template-columns: 1fr; + } + + .salon-meta div + div { + border-top: 1px solid color-mix(in oklch, var(--hero-ink) 18%, transparent); + border-left: 0; + } + + .promise-section, + .hours-section, + .trust-band, + .contact-layout { + grid-template-columns: 1fr; + } + + .promise-section { + background: var(--background); + } + + .promise-intro { + position: static; + } + + .promise-card, + .service-row { + grid-template-columns: 1fr; + } + + .promise-card h3, + .promise-card p { + grid-column: auto; + } + + .service-row { + gap: 0.75rem; + } + + .hours-row { + align-items: baseline; + } +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + } +}