diff --git a/assets/css/main.css b/assets/css/main.css new file mode 100644 index 0000000..6343b61 --- /dev/null +++ b/assets/css/main.css @@ -0,0 +1,61 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --primary-color: theme('colors.primary.DEFAULT'); + --accent-color: theme('colors.accent.DEFAULT'); + --text-color: theme('colors.gray.800'); + --background-color: theme('colors.white'); + --card-background: theme('colors.white'); + --header-background: theme('colors.white'); + } + + .dark { + --primary-color: theme('colors.primary.400'); + --accent-color: theme('colors.accent.400'); + --text-color: theme('colors.gray.200'); + --background-color: theme('colors.gray.900'); + --card-background: theme('colors.gray.800'); + --header-background: theme('colors.gray.900'); + } +} + +@keyframes slideInLeft { + from { + transform: translateX(-100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideInRight { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +.slide-in-left { + animation: slideInLeft 1s ease-out forwards; +} + +.slide-in-right { + animation: slideInRight 1s ease-out forwards; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; +} +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} \ No newline at end of file diff --git a/assets/js/menu.js b/assets/js/menu.js new file mode 100644 index 0000000..1d902a4 --- /dev/null +++ b/assets/js/menu.js @@ -0,0 +1,44 @@ +document.addEventListener('DOMContentLoaded', function() { + // Menu category switching + const tabButtons = document.querySelectorAll('[data-tab]'); + const menuContents = document.querySelectorAll('.menu-content'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + const targetId = `${button.dataset.tab}-content`; + + // Update button styles + tabButtons.forEach(btn => { + const isActive = btn === button; + btn.classList.toggle('bg-pizza-red', isActive); + btn.classList.toggle('text-white', isActive); + btn.classList.toggle('bg-gray-200', !isActive); + btn.classList.toggle('dark:bg-pizza-darker', !isActive); + btn.classList.toggle('text-gray-700', !isActive); + btn.classList.toggle('dark:text-white', !isActive); + }); + + // Show/hide content + menuContents.forEach(content => { + content.classList.toggle('hidden', content.id !== targetId); + }); + }); + }); + + // Pizza price group toggles + const priceGroupHeaders = document.querySelectorAll('.price-group-header'); + priceGroupHeaders.forEach(header => { + header.addEventListener('click', () => { + const content = header.nextElementSibling; + const arrow = header.querySelector('svg'); + + // Toggle content visibility + content.classList.toggle('hidden'); + + // Rotate arrow + arrow.style.transform = content.classList.contains('hidden') + ? 'rotate(0deg)' + : 'rotate(180deg)'; + }); + }); +}); \ No newline at end of file diff --git a/assets/js/opening-status.js b/assets/js/opening-status.js new file mode 100644 index 0000000..6856188 --- /dev/null +++ b/assets/js/opening-status.js @@ -0,0 +1,95 @@ +function updateTimeAndStatus() { + const now = new Date(); + const timeString = now.toLocaleTimeString('de-DE', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + + // Update time display + const timeDisplay = document.getElementById('current-time'); + if (timeDisplay) { + timeDisplay.textContent = timeString; + } + + fetch('/api/check-open-status') + .then(response => response.json()) + .then(status => { + const statusBar = document.getElementById('status-bar'); + const statusText = document.getElementById('status-text'); + const deliveryText = document.querySelector('.delivery-text'); + + if (statusBar && statusText) { + statusBar.className = status.isOpen + ? 'bg-status-green transition-colors duration-300' + : 'bg-status-red transition-colors duration-300'; + + const textOpen = statusText.dataset.textOpen; + const textClosed = statusText.dataset.textClosed; + statusText.textContent = status.isOpen ? textOpen : textClosed; + } + + // Debug information + const debugInfo = document.getElementById('debug-info'); + if (debugInfo && !status.isOpen) { + const nextOpen = document.getElementById('next-open'); + const currentDay = document.getElementById('current-day'); + + if (currentDay) { + currentDay.textContent = now.toLocaleDateString('de-DE', { weekday: 'long' }); + } + + if (nextOpen) { + const hoursRows = document.querySelectorAll('.hours-row'); + let nextOpenTime = null; + let foundToday = false; + + // First check today's remaining times + hoursRows.forEach(row => { + if (row.dataset.day === status.currentDay) { + const openTime = row.dataset.open; + if (openTime > timeString) { + nextOpenTime = `Today at ${openTime}`; + foundToday = true; + } + } + }); + + // If no times found today, find next day's opening + if (!foundToday) { + hoursRows.forEach(row => { + const dayIndex = parseInt(row.dataset.dayIndex || 0); + const currentDayIndex = now.getDay(); + + if (dayIndex > currentDayIndex || (dayIndex === 0 && currentDayIndex !== 0)) { + const openTime = row.dataset.open; + const dayName = row.dataset.dayName; + if (!nextOpenTime) { + nextOpenTime = `${dayName} at ${openTime}`; + } + } + }); + } + + nextOpen.textContent = nextOpenTime || 'Check opening hours'; + } + } + + // Update other elements + if (deliveryText) { + deliveryText.className = status.isOpen + ? 'delivery-text text-status-green' + : 'delivery-text text-status-red'; + } + + document.querySelectorAll('.hours-row').forEach(row => { + const isCurrentDay = row.dataset.day === status.currentDay; + row.classList.toggle('border-2', isCurrentDay && status.isOpen); + row.classList.toggle('border-status-green', isCurrentDay && status.isOpen); + }); + }); +} + +// Update immediately and then every minute +updateTimeAndStatus(); +setInterval(updateTimeAndStatus, 60000); \ No newline at end of file diff --git a/layouts/404.html b/layouts/404.html new file mode 100644 index 0000000..29c103e --- /dev/null +++ b/layouts/404.html @@ -0,0 +1,60 @@ +{{ define "main" }} +
+
+

+ 4 + + + + + + + + + + + + + 4 +

+ +
+

+ {{ i18n "404_oops" | default "Oops! Looks like this slice is missing!" }} +

+ +

+ {{ i18n "404_text" | default "The page you're looking for has been eaten or never existed." }} +

+ + +
+
+ + + + + + + + + +
+
+ + +
+
+
+{{ end }} \ No newline at end of file diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html new file mode 100644 index 0000000..4e213eb --- /dev/null +++ b/layouts/_default/baseof.html @@ -0,0 +1,26 @@ + + + + + + {{ if .IsHome }}{{ .Site.Title }}{{ else }}{{ .Title }} - {{ .Site.Title }}{{ end }} + + + + {{ partial "header.html" . }} +
+ {{ block "main" . }}{{ end }} +
+ {{ partial "footer.html" . }} + + + + + + {{ with resources.Get "js/opening-status.js" }} + {{ with . | resources.Minify }} + + {{ end }} + {{ end }} + + diff --git a/layouts/_default/check-open-status.json b/layouts/_default/check-open-status.json new file mode 100644 index 0000000..7f56fc6 --- /dev/null +++ b/layouts/_default/check-open-status.json @@ -0,0 +1,5 @@ +{ + "isOpen": {{ $status := partial "check-open-status.html" . }}{{ $status.isOpen }}, + "currentDay": "{{ now.Format "Monday" }}", + "currentTime": "{{ now.Format "15:04" }}" +} \ No newline at end of file diff --git a/layouts/_default/list.html b/layouts/_default/list.html new file mode 100644 index 0000000..58b1765 --- /dev/null +++ b/layouts/_default/list.html @@ -0,0 +1,12 @@ +{{ define "main" }} +

{{ .Title }}

+
+ {{ range .Pages }} +
+

{{ .Title }}

+ +

{{ .Summary }}

+
+ {{ end }} +
+{{ end }} \ No newline at end of file diff --git a/layouts/_default/single.html b/layouts/_default/single.html new file mode 100644 index 0000000..da6bab1 --- /dev/null +++ b/layouts/_default/single.html @@ -0,0 +1,9 @@ +{{ define "main" }} +
+

{{ .Title }}

+ +
+ {{ .Content }} +
+
+{{ end }} \ No newline at end of file diff --git a/layouts/_default/single.json b/layouts/_default/single.json new file mode 100644 index 0000000..2807823 --- /dev/null +++ b/layouts/_default/single.json @@ -0,0 +1,5 @@ +{ + "layout": "{{ .Layout }}", + "title": "{{ .Title }}", + "type": "{{ .Type }}" +} \ No newline at end of file diff --git a/layouts/about/about.html b/layouts/about/about.html new file mode 100644 index 0000000..83fdd12 --- /dev/null +++ b/layouts/about/about.html @@ -0,0 +1,105 @@ +{{ define "main" }} +
+ +
+
+
+
+
+
+

{{ .Site.Data.about.hero.title }}

+

{{ .Site.Data.about.hero.description }}

+
+
+
+ + +
+
+ +
+
+

{{ .Site.Data.about.story.title }}

+
+

+ {{ .Site.Data.about.story.content }} +

+
+
    + {{ range .Site.Data.about.story.features }} +
  • + + + + {{ . }} +
  • + {{ end }} +
+
+
+ Restaurant Interior +
+
+ + +
+
+

{{ .Site.Data.about.team.title }}

+

+ {{ .Site.Data.about.team.description }} +

+
+
+ {{ range .Site.Data.about.team.members }} +
+
+ {{ .name }} +
+
+

{{ .name }}

+

{{ .role }}

+

+ {{ .description }} +

+
+
+ {{ end }} +
+
+ + +
+
+
+

{{ .Site.Data.about.contact.title }}

+
+

+ + + + + {{ .Site.Data.about.contact.address }} +

+

+ + + + {{ .Site.Params.contact.phone }} +

+
+
+
+ Restaurant Exterior +
+
+
+
+
+
+{{ end }} \ No newline at end of file diff --git a/layouts/en/about/about.html b/layouts/en/about/about.html new file mode 100644 index 0000000..6c188b7 --- /dev/null +++ b/layouts/en/about/about.html @@ -0,0 +1,105 @@ +{{ define "main" }} +
+ +
+
+
+
+
+
+

{{ .Site.Data.en.about.hero.title }}

+

{{ .Site.Data.en.about.hero.description }}

+
+
+
+ + +
+
+ +
+
+

{{ .Site.Data.en.about.story.title }}

+
+

+ {{ .Site.Data.en.about.story.content }} +

+
+
    + {{ range .Site.Data.en.about.story.features }} +
  • + + + + {{ . }} +
  • + {{ end }} +
+
+
+ Restaurant Interior +
+
+ + +
+
+

{{ .Site.Data.en.about.team.title }}

+

+ {{ .Site.Data.en.about.team.description }} +

+
+
+ {{ range .Site.Data.en.about.team.members }} +
+
+ {{ .name }} +
+
+

{{ .name }}

+

{{ .role }}

+

+ {{ .description }} +

+
+
+ {{ end }} +
+
+ + +
+
+
+

{{ .Site.Data.en.about.contact.title }}

+
+

+ + + + + {{ .Site.Data.en.about.contact.address }} +

+

+ + + + {{ .Site.Params.contact.phone }} +

+
+
+
+ Restaurant Exterior +
+
+
+
+
+
+{{ end }} diff --git a/layouts/en/index.html b/layouts/en/index.html new file mode 100644 index 0000000..5a37790 --- /dev/null +++ b/layouts/en/index.html @@ -0,0 +1,25 @@ +{{ define "main" }} +
+
+ +
+ +
+ {{ partial "opening-hours.html" . }} +
+ + +
+ Delicious Pizza +
+
+ + + {{ partial "menu-card.html" . }} +
+
+ + +{{ $menu := resources.Get "js/menu.js" | resources.Minify | resources.Fingerprint }} + +{{ end }} \ No newline at end of file diff --git a/layouts/index.html b/layouts/index.html new file mode 100644 index 0000000..777102e --- /dev/null +++ b/layouts/index.html @@ -0,0 +1,26 @@ +{{ define "main" }} +
+
+ +
+ +
+ {{ partial "opening-hours.html" . }} +
+ + +
+ Delicious Pizza +
+
+ + + {{ partial "menu-card.html" . }} +
+
+ + + +{{ $menu := resources.Get "js/menu.js" | resources.Minify | resources.Fingerprint }} + +{{ end }} \ No newline at end of file diff --git a/layouts/partials/check-open-status.html b/layouts/partials/check-open-status.html new file mode 100644 index 0000000..eba2922 --- /dev/null +++ b/layouts/partials/check-open-status.html @@ -0,0 +1,32 @@ +{{ $currentTime := now.Format "15:04" }} +{{ $currentDay := now.Format "Monday" }} +{{ $isOpen := false }} +{{ $holidayInfo := dict }} +{{ $isHoliday := false }} + + +{{ range .Site.Data.hours.holidays }} + {{ $holidayDate := time .date }} + {{ if eq (now.Format "2006-01-02") ($holidayDate.Format "2006-01-02") }} + {{ $isHoliday = true }} + {{ $holidayInfo = . }} + {{ if not .closed }} + {{ if and (ge $currentTime .open) (le $currentTime .close) }} + {{ $isOpen = true }} + {{ end }} + {{ end }} + {{ end }} +{{ end }} + + +{{ if not $isHoliday }} + {{ range .Site.Data.hours.regular_hours }} + {{ if in .days $currentDay }} + {{ if and (ge $currentTime .open) (le $currentTime .close) }} + {{ $isOpen = true }} + {{ end }} + {{ end }} + {{ end }} +{{ end }} + +{{ return dict "isOpen" $isOpen "isHoliday" $isHoliday "holidayInfo" $holidayInfo "currentDay" $currentDay "currentTime" $currentTime }} \ No newline at end of file diff --git a/layouts/partials/components/language-switcher.html b/layouts/partials/components/language-switcher.html new file mode 100644 index 0000000..942a98e --- /dev/null +++ b/layouts/partials/components/language-switcher.html @@ -0,0 +1,13 @@ +
+ {{ if eq .Site.Language.Lang "en" }} + + Deutsch + DE + + {{ else }} + + English + EN + + {{ end }} +
\ No newline at end of file diff --git a/layouts/partials/components/theme-switcher.html b/layouts/partials/components/theme-switcher.html new file mode 100644 index 0000000..309cbd2 --- /dev/null +++ b/layouts/partials/components/theme-switcher.html @@ -0,0 +1,70 @@ + +{{ $class := .Class }} +{{ if site.Params.theme_switcher }} + + + + +{{ end }} diff --git a/layouts/partials/footer.html b/layouts/partials/footer.html new file mode 100644 index 0000000..5cb67e7 --- /dev/null +++ b/layouts/partials/footer.html @@ -0,0 +1,27 @@ + \ No newline at end of file diff --git a/layouts/partials/head.html b/layouts/partials/head.html new file mode 100644 index 0000000..a86f3ce --- /dev/null +++ b/layouts/partials/head.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/layouts/partials/header.html b/layouts/partials/header.html new file mode 100644 index 0000000..44a3687 --- /dev/null +++ b/layouts/partials/header.html @@ -0,0 +1,161 @@ +
+ + {{ $status := partial "check-open-status.html" . }} +
+
+
+
+ {{ if $status.isOpen }} + {{ i18n "open" }} + {{ else }} + {{ i18n "closed" }} + {{ end }} +
+
+ ({{ now.Format "15:04" }} {{ i18n "time_suffix" }}) +
+
+
+
+ + + +
+ + + diff --git a/layouts/partials/logo.html b/layouts/partials/logo.html new file mode 100644 index 0000000..e4d1848 --- /dev/null +++ b/layouts/partials/logo.html @@ -0,0 +1,5 @@ +{{ if .Site.Params.logo }} + +{{ else }} + {{ .Site.Title }} +{{ end }} \ No newline at end of file diff --git a/layouts/partials/menu-card.html b/layouts/partials/menu-card.html new file mode 100644 index 0000000..8551152 --- /dev/null +++ b/layouts/partials/menu-card.html @@ -0,0 +1,180 @@ + + \ No newline at end of file diff --git a/layouts/partials/opening-hours.html b/layouts/partials/opening-hours.html new file mode 100644 index 0000000..c66ebea --- /dev/null +++ b/layouts/partials/opening-hours.html @@ -0,0 +1,83 @@ +{{ $now := now }} +{{ $currentDay := $now.Format "Monday" }} +{{ $currentTime := $now.Format "15:04" }} + +
+

{{ i18n "opening_hours" }}

+ + +
+ {{ range .Site.Data.hours.regular_hours }} + {{ $isOpen := and (in .days $currentDay) (ge $currentTime .open) (le $currentTime .close) }} +
+
+
+ {{ $numDays := len .days }} + {{ if eq $numDays 1 }} + {{ index .days 0 }} + {{ else if eq $numDays 2 }} + {{ i18n (index .days 0) }} & {{ i18n (index .days 1) }} + {{ else }} + {{ $lastIndex := sub $numDays 1 }} + {{ range $i, $day := .days }} + {{ if eq $i 0 }} + {{ i18n $day }} + {{ else if eq $i $lastIndex }} + - {{ i18n $day }} + {{ end }} + {{ end }} + {{ end }} +
+
+
+ {{ .open }} - {{ .close }} +
+
+ {{ i18n "delivery_until" }} {{ .close }} +
+
+
+ + + {{ if $isOpen }} + + {{ end }} +
+ {{ end }} +
+ + + {{ range .Site.Data.hours.holidays }} + {{ $holidayDate := time .date }} + {{ if eq ($now.Format "2006-01-02") ($holidayDate.Format "2006-01-02") }} +
+
+ {{ .name }}: + {{ if .closed }} + {{ i18n "closed" }} + {{ else }} + {{ .open }} - {{ .close }} Uhr +
+ {{ i18n "delivery_until" }} {{ .delivery_until }} Uhr +
+ {{ end }} +
+
+ {{ end }} + {{ end }} + +
\ No newline at end of file diff --git a/layouts/partials/price-table.html b/layouts/partials/price-table.html new file mode 100644 index 0000000..15c7932 --- /dev/null +++ b/layouts/partials/price-table.html @@ -0,0 +1,5 @@ +
+ {{ i18n "size" }} + {{ i18n "pickup" }} + {{ i18n "delivery" }} +
\ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..fb4edbe --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,1760 @@ +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; + --tw-contain-size: ; + --tw-contain-layout: ; + --tw-contain-paint: ; + --tw-contain-style: ; +} + +/* +! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + letter-spacing: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +input:where([type='button']), +input:where([type='reset']), +input:where([type='submit']) { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden]:where(:not([hidden="until-found"])) { + display: none; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.visible { + visibility: visible; +} + +.fixed { + position: fixed; +} + +.absolute { + position: absolute; +} + +.relative { + position: relative; +} + +.inset-0 { + inset: 0px; +} + +.bottom-4 { + bottom: 1rem; +} + +.left-0 { + left: 0px; +} + +.left-1\/2 { + left: 50%; +} + +.right-0 { + right: 0px; +} + +.right-4 { + right: 1rem; +} + +.top-0 { + top: 0px; +} + +.top-1\/2 { + top: 50%; +} + +.z-10 { + z-index: 10; +} + +.z-50 { + z-index: 50; +} + +.mx-0 { + margin-left: 0px; + margin-right: 0px; +} + +.mx-1 { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mb-12 { + margin-bottom: 3rem; +} + +.mb-16 { + margin-bottom: 4rem; +} + +.mb-2 { + margin-bottom: 0.5rem; +} + +.mb-4 { + margin-bottom: 1rem; +} + +.mb-6 { + margin-bottom: 1.5rem; +} + +.mb-8 { + margin-bottom: 2rem; +} + +.ml-2 { + margin-left: 0.5rem; +} + +.mr-2 { + margin-right: 0.5rem; +} + +.mt-12 { + margin-top: 3rem; +} + +.mt-2 { + margin-top: 0.5rem; +} + +.mt-3 { + margin-top: 0.75rem; +} + +.mt-4 { + margin-top: 1rem; +} + +.mt-6 { + margin-top: 1.5rem; +} + +.mt-8 { + margin-top: 2rem; +} + +.mt-auto { + margin-top: auto; +} + +.block { + display: block; +} + +.inline-block { + display: inline-block; +} + +.flex { + display: flex; +} + +.inline-flex { + display: inline-flex; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.aspect-square { + aspect-ratio: 1 / 1; +} + +.h-12 { + height: 3rem; +} + +.h-16 { + height: 4rem; +} + +.h-24 { + height: 6rem; +} + +.h-3 { + height: 0.75rem; +} + +.h-32 { + height: 8rem; +} + +.h-5 { + height: 1.25rem; +} + +.h-6 { + height: 1.5rem; +} + +.h-64 { + height: 16rem; +} + +.h-8 { + height: 2rem; +} + +.h-80 { + height: 20rem; +} + +.h-\[40vh\] { + height: 40vh; +} + +.h-full { + height: 100%; +} + +.min-h-screen { + min-height: 100vh; +} + +.w-24 { + width: 6rem; +} + +.w-3 { + width: 0.75rem; +} + +.w-32 { + width: 8rem; +} + +.w-5 { + width: 1.25rem; +} + +.w-6 { + width: 1.5rem; +} + +.w-auto { + width: auto; +} + +.w-full { + width: 100%; +} + +.max-w-2xl { + max-width: 42rem; +} + +.max-w-4xl { + max-width: 56rem; +} + +.flex-shrink-0 { + flex-shrink: 0; +} + +.origin-center { + transform-origin: center; +} + +.-translate-x-1\/2 { + --tw-translate-x: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.-translate-y-1\/2 { + --tw-translate-y: -50%; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.transform { + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +@keyframes bounce { + 0%, 100% { + transform: translateY(-25%); + animation-timing-function: cubic-bezier(0.8,0,1,1); + } + + 50% { + transform: none; + animation-timing-function: cubic-bezier(0,0,0.2,1); + } +} + +.animate-bounce-subtle { + animation: bounce 2s infinite; +} + +@keyframes driveBy { + 0% { + transform: translateX(-200%); + } + + 100% { + transform: translateX(200%); + } +} + +.animate-drive-by { + animation: driveBy 10s linear infinite; +} + +@keyframes pulse { + 50% { + opacity: .5; + } +} + +.animate-pulse { + animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.animate-spin-slow { + animation: spin 8s linear infinite; +} + +.cursor-pointer { + cursor: pointer; +} + +.resize { + resize: both; +} + +.list-inside { + list-style-position: inside; +} + +.list-disc { + list-style-type: disc; +} + +.grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); +} + +.grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} + +.flex-col { + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.justify-around { + justify-content: space-around; +} + +.gap-2 { + gap: 0.5rem; +} + +.gap-4 { + gap: 1rem; +} + +.gap-6 { + gap: 1.5rem; +} + +.gap-8 { + gap: 2rem; +} + +.space-x-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.75rem * var(--tw-space-x-reverse)); + margin-left: calc(0.75rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1rem * var(--tw-space-x-reverse)); + margin-left: calc(1rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem * var(--tw-space-x-reverse)); + margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-y-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); +} + +.space-y-16 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(4rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(4rem * var(--tw-space-y-reverse)); +} + +.space-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); +} + +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + +.space-y-4 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1rem * var(--tw-space-y-reverse)); +} + +.space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); +} + +.space-y-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(2rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(2rem * var(--tw-space-y-reverse)); +} + +.overflow-hidden { + overflow: hidden; +} + +.overflow-x-auto { + overflow-x: auto; +} + +.scroll-smooth { + scroll-behavior: smooth; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.rounded-full { + border-radius: 9999px; +} + +.rounded-lg { + border-radius: 0.5rem; +} + +.rounded-xl { + border-radius: 0.75rem; +} + +.border { + border-width: 1px; +} + +.border-2 { + border-width: 2px; +} + +.border-b { + border-bottom-width: 1px; +} + +.border-t { + border-top-width: 1px; +} + +.border-gray-100 { + --tw-border-opacity: 1; + border-color: rgb(243 244 246 / var(--tw-border-opacity, 1)); +} + +.border-gray-200 { + --tw-border-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-border-opacity, 1)); +} + +.border-pizza-red\/20 { + border-color: rgb(255 56 80 / 0.2); +} + +.border-status-green { + --tw-border-opacity: 1; + border-color: rgb(16 185 129 / var(--tw-border-opacity, 1)); +} + +.bg-black\/50 { + background-color: rgb(0 0 0 / 0.5); +} + +.bg-gray-100 { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); +} + +.bg-gray-200 { + --tw-bg-opacity: 1; + background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1)); +} + +.bg-pizza-red { + --tw-bg-opacity: 1; + background-color: rgb(255 56 80 / var(--tw-bg-opacity, 1)); +} + +.bg-pizza-red\/10 { + background-color: rgb(255 56 80 / 0.1); +} + +.bg-status-green { + --tw-bg-opacity: 1; + background-color: rgb(16 185 129 / var(--tw-bg-opacity, 1)); +} + +.bg-status-red { + --tw-bg-opacity: 1; + background-color: rgb(239 68 68 / var(--tw-bg-opacity, 1)); +} + +.bg-white { + --tw-bg-opacity: 1; + background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); +} + +.bg-white\/5 { + background-color: rgb(255 255 255 / 0.05); +} + +.bg-white\/90 { + background-color: rgb(255 255 255 / 0.9); +} + +.bg-white\/95 { + background-color: rgb(255 255 255 / 0.95); +} + +.bg-\[url\(\'\/images\/pizza-hero\.jpg\'\)\] { + background-image: url('/images/pizza-hero.jpg'); +} + +.bg-cover { + background-size: cover; +} + +.bg-center { + background-position: center; +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + +.p-2 { + padding: 0.5rem; +} + +.p-4 { + padding: 1rem; +} + +.p-6 { + padding: 1.5rem; +} + +.p-8 { + padding: 2rem; +} + +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-3 { + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + +.px-8 { + padding-left: 2rem; + padding-right: 2rem; +} + +.py-1\.5 { + padding-top: 0.375rem; + padding-bottom: 0.375rem; +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} + +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +.py-2\.5 { + padding-top: 0.625rem; + padding-bottom: 0.625rem; +} + +.py-3 { + padding-top: 0.75rem; + padding-bottom: 0.75rem; +} + +.py-8 { + padding-top: 2rem; + padding-bottom: 2rem; +} + +.pb-2 { + padding-bottom: 0.5rem; +} + +.pb-3 { + padding-bottom: 0.75rem; +} + +.pt-2 { + padding-top: 0.5rem; +} + +.pt-24 { + padding-top: 6rem; +} + +.pt-3 { + padding-top: 0.75rem; +} + +.pt-4 { + padding-top: 1rem; +} + +.pt-6 { + padding-top: 1.5rem; +} + +.pt-8 { + padding-top: 2rem; +} + +.text-center { + text-align: center; +} + +.text-right { + text-align: right; +} + +.text-2xl { + font-size: 1.5rem; + line-height: 2rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + +.text-5xl { + font-size: 3rem; + line-height: 1; +} + +.text-9xl { + font-size: 8rem; + line-height: 1; +} + +.text-base { + font-size: 1rem; + line-height: 1.5rem; +} + +.text-lg { + font-size: 1.125rem; + line-height: 1.75rem; +} + +.text-sm { + font-size: 0.875rem; + line-height: 1.25rem; +} + +.text-xl { + font-size: 1.25rem; + line-height: 1.75rem; +} + +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.font-bold { + font-weight: 700; +} + +.font-medium { + font-weight: 500; +} + +.font-semibold { + font-weight: 600; +} + +.tracking-widest { + letter-spacing: 0.1em; +} + +.text-emerald-400 { + --tw-text-opacity: 1; + color: rgb(52 211 153 / var(--tw-text-opacity, 1)); +} + +.text-gray-400 { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity, 1)); +} + +.text-gray-500 { + --tw-text-opacity: 1; + color: rgb(107 114 128 / var(--tw-text-opacity, 1)); +} + +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity, 1)); +} + +.text-gray-700 { + --tw-text-opacity: 1; + color: rgb(55 65 81 / var(--tw-text-opacity, 1)); +} + +.text-gray-800 { + --tw-text-opacity: 1; + color: rgb(31 41 55 / var(--tw-text-opacity, 1)); +} + +.text-gray-900 { + --tw-text-opacity: 1; + color: rgb(17 24 39 / var(--tw-text-opacity, 1)); +} + +.text-pizza-red { + --tw-text-opacity: 1; + color: rgb(255 56 80 / var(--tw-text-opacity, 1)); +} + +.text-status-green { + --tw-text-opacity: 1; + color: rgb(16 185 129 / var(--tw-text-opacity, 1)); +} + +.text-status-red { + --tw-text-opacity: 1; + color: rgb(239 68 68 / var(--tw-text-opacity, 1)); +} + +.text-white { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); +} + +.opacity-0 { + opacity: 0; +} + +.opacity-100 { + opacity: 1; +} + +.shadow-lg { + --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.shadow-md { + --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); + --tw-shadow-colored: 0 4px 6px -1px var(--tw-shadow-color), 0 2px 4px -2px var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + +.backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + +.transition-all { + transition-property: all; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.transition-transform { + transition-property: transform; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +.duration-200 { + transition-duration: 200ms; +} + +.duration-300 { + transition-duration: 300ms; +} + +.duration-500 { + transition-duration: 500ms; +} + +.duration-700 { + transition-duration: 700ms; +} + +@keyframes slideInLeft { + from { + transform: translateX(-100%); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } +} + +@keyframes slideInRight { + from { + transform: translateX(100%); + opacity: 0; + } + + to { + transform: translateX(0); + opacity: 1; + } +} + +.slide-in-left { + animation: slideInLeft 1s ease-out forwards; +} + +.slide-in-right { + animation: slideInRight 1s ease-out forwards; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; +} + +.scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; +} + +.last\:border-0:last-child { + border-width: 0px; +} + +.hover\:scale-105:hover { + --tw-scale-x: 1.05; + --tw-scale-y: 1.05; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.hover\:bg-gray-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1)); +} + +.hover\:bg-gray-300:hover { + --tw-bg-opacity: 1; + background-color: rgb(209 213 219 / var(--tw-bg-opacity, 1)); +} + +.hover\:bg-red-600:hover { + --tw-bg-opacity: 1; + background-color: rgb(220 38 38 / var(--tw-bg-opacity, 1)); +} + +.hover\:text-pizza-red:hover { + --tw-text-opacity: 1; + color: rgb(255 56 80 / var(--tw-text-opacity, 1)); +} + +.hover\:text-status-green:hover { + --tw-text-opacity: 1; + color: rgb(16 185 129 / var(--tw-text-opacity, 1)); +} + +.hover\:text-status-green\/80:hover { + color: rgb(16 185 129 / 0.8); +} + +.focus\:outline-none:focus { + outline: 2px solid transparent; + outline-offset: 2px; +} + +.group:hover .group-hover\:-translate-y-1 { + --tw-translate-y: -0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.group:hover .group-hover\:translate-y-1 { + --tw-translate-y: 0.25rem; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.group:hover .group-hover\:scale-110 { + --tw-scale-x: 1.1; + --tw-scale-y: 1.1; + transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); +} + +.group:hover .group-hover\:opacity-0 { + opacity: 0; +} + +.group:hover .group-hover\:opacity-100 { + opacity: 1; +} + +.dark\:block:is(.dark *) { + display: block; +} + +.dark\:hidden:is(.dark *) { + display: none; +} + +.dark\:border-gray-700:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(55 65 81 / var(--tw-border-opacity, 1)); +} + +.dark\:border-pizza-dark:is(.dark *) { + --tw-border-opacity: 1; + border-color: rgb(26 29 36 / var(--tw-border-opacity, 1)); +} + +.dark\:bg-gray-900\/95:is(.dark *) { + background-color: rgb(17 24 39 / 0.95); +} + +.dark\:bg-pizza-dark:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(26 29 36 / var(--tw-bg-opacity, 1)); +} + +.dark\:bg-pizza-dark\/90:is(.dark *) { + background-color: rgb(26 29 36 / 0.9); +} + +.dark\:bg-pizza-darker:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(21 23 28 / var(--tw-bg-opacity, 1)); +} + +.dark\:bg-pizza-darker\/5:is(.dark *) { + background-color: rgb(21 23 28 / 0.05); +} + +.dark\:bg-pizza-red\/20:is(.dark *) { + background-color: rgb(255 56 80 / 0.2); +} + +.dark\:text-gray-100:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(243 244 246 / var(--tw-text-opacity, 1)); +} + +.dark\:text-gray-200:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity, 1)); +} + +.dark\:text-gray-300:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(209 213 219 / var(--tw-text-opacity, 1)); +} + +.dark\:text-gray-400:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity, 1)); +} + +.dark\:text-pizza-gray:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(156 163 175 / var(--tw-text-opacity, 1)); +} + +.dark\:text-white:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity, 1)); +} + +.dark\:opacity-0:is(.dark *) { + opacity: 0; +} + +.dark\:opacity-100:is(.dark *) { + opacity: 1; +} + +.dark\:hover\:bg-gray-800:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1)); +} + +.dark\:hover\:bg-pizza-dark:hover:is(.dark *) { + --tw-bg-opacity: 1; + background-color: rgb(26 29 36 / var(--tw-bg-opacity, 1)); +} + +.dark\:hover\:text-status-green:hover:is(.dark *) { + --tw-text-opacity: 1; + color: rgb(16 185 129 / var(--tw-text-opacity, 1)); +} + +@media (min-width: 640px) { + .sm\:mx-0 { + margin-left: 0px; + margin-right: 0px; + } + + .sm\:mb-8 { + margin-bottom: 2rem; + } + + .sm\:mt-16 { + margin-top: 4rem; + } + + .sm\:h-10 { + height: 2.5rem; + } + + .sm\:h-16 { + height: 4rem; + } + + .sm\:h-20 { + height: 5rem; + } + + .sm\:space-y-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(1.5rem * var(--tw-space-y-reverse)); + } + + .sm\:p-6 { + padding: 1.5rem; + } + + .sm\:text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; + } + + .sm\:text-sm { + font-size: 0.875rem; + line-height: 1.25rem; + } +} + +@media (min-width: 768px) { + .md\:flex { + display: flex; + } + + .md\:hidden { + display: none; + } + + .md\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .md\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .md\:text-2xl { + font-size: 1.5rem; + line-height: 2rem; + } + + .md\:text-6xl { + font-size: 3.75rem; + line-height: 1; + } +} + +@media (min-width: 1024px) { + .lg\:-ml-16 { + margin-left: -4rem; + } + + .lg\:-mt-8 { + margin-top: -2rem; + } + + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} \ No newline at end of file diff --git a/static/icons/de.svg b/static/icons/de.svg new file mode 100644 index 0000000..02c2384 --- /dev/null +++ b/static/icons/de.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/static/icons/gb.svg b/static/icons/gb.svg new file mode 100644 index 0000000..c00b03a --- /dev/null +++ b/static/icons/gb.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/static/images/logo.webp b/static/images/logo.webp new file mode 100644 index 0000000..a892557 Binary files /dev/null and b/static/images/logo.webp differ diff --git a/static/images/pizza-aussen.webp b/static/images/pizza-aussen.webp new file mode 100644 index 0000000..4c094a0 Binary files /dev/null and b/static/images/pizza-aussen.webp differ diff --git a/static/images/pizza-innen.webp b/static/images/pizza-innen.webp new file mode 100644 index 0000000..2844725 Binary files /dev/null and b/static/images/pizza-innen.webp differ diff --git a/static/images/pizza.webp b/static/images/pizza.webp new file mode 100644 index 0000000..de55e60 Binary files /dev/null and b/static/images/pizza.webp differ diff --git a/static/images/team/Daisy.webp b/static/images/team/Daisy.webp new file mode 100644 index 0000000..29cb24b Binary files /dev/null and b/static/images/team/Daisy.webp differ diff --git a/static/images/team/Luigi.webp b/static/images/team/Luigi.webp new file mode 100644 index 0000000..5fb312b Binary files /dev/null and b/static/images/team/Luigi.webp differ diff --git a/static/images/team/Wario.webp b/static/images/team/Wario.webp new file mode 100644 index 0000000..c065c2d Binary files /dev/null and b/static/images/team/Wario.webp differ diff --git a/static/js/header.js b/static/js/header.js new file mode 100644 index 0000000..375a09f --- /dev/null +++ b/static/js/header.js @@ -0,0 +1,38 @@ +document.addEventListener('DOMContentLoaded', function() { + const header = document.querySelector('.header'); + const navToggle = document.getElementById('nav-toggle'); + const navMenu = document.getElementById('nav-menu'); + + // Scroll behavior + let lastScroll = 0; + window.addEventListener('scroll', () => { + const currentScroll = window.pageYOffset; + + if (currentScroll <= 0) { + header.classList.remove('scrolled'); + return; + } + + if (currentScroll > lastScroll && !header.contains(document.activeElement)) { + header.style.transform = 'translateY(-100%)'; + } else { + header.style.transform = 'translateY(0)'; + header.classList.add('scrolled'); + } + lastScroll = currentScroll; + }); + + // Close mobile menu when clicking outside + document.addEventListener('click', (e) => { + if (!header.contains(e.target)) { + navToggle.checked = false; + } + }); + + // Close mobile menu when window is resized to desktop + window.addEventListener('resize', () => { + if (window.innerWidth >= 1024) { + navToggle.checked = false; + } + }); +}); \ No newline at end of file diff --git a/static/js/language.js b/static/js/language.js new file mode 100644 index 0000000..cac183a --- /dev/null +++ b/static/js/language.js @@ -0,0 +1,23 @@ +document.addEventListener('DOMContentLoaded', function() { + // Only run on the home page to avoid redirect loops + if (window.location.pathname === '/') { + const userLang = navigator.language || navigator.userLanguage; + const currentLang = document.documentElement.lang; + + // Simple language mapping + const supportedLanguages = { + 'de': '/de/', + 'de-DE': '/de/', + 'de-AT': '/de/', + 'de-CH': '/de/', + 'en': '/en/', + 'en-US': '/en/', + 'en-GB': '/en/' + }; + + // Check if user's language is different from current and is supported + if (supportedLanguages[userLang] && currentLang !== userLang.substring(0,2)) { + window.location.href = supportedLanguages[userLang]; + } + } +}); \ No newline at end of file diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 0000000..60de208 --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,274 @@ +document.addEventListener('DOMContentLoaded', function() { + // Mobile menu toggle + const mobileMenuButton = document.querySelector('.mobile-menu-button'); + const mobileMenu = document.querySelector('.mobile-menu'); + + mobileMenuButton.addEventListener('click', () => { + mobileMenu.classList.toggle('hidden'); + + // Update button state + mobileMenuButton.setAttribute('aria-expanded', !isExpanded); + + // Toggle icons + menuIconOpen.classList.toggle('hidden'); + menuIconClosed.classList.toggle('hidden'); + }); + + // Close mobile menu when clicking outside + document.addEventListener('click', (e) => { + if (!mobileMenuButton.contains(e.target) && !mobileMenu.contains(e.target)) { + mobileMenu.classList.add('hidden'); + mobileMenuButton.setAttribute('aria-expanded', 'false'); + menuIconOpen.classList.remove('hidden'); + menuIconClosed.classList.add('hidden'); + } + }); + + // Theme toggle functionality + function updateTheme(isDark) { + const html = document.documentElement; + if (isDark) { + html.classList.add('dark'); + } else { + html.classList.remove('dark'); + } + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + } + + // Desktop theme toggle + const darkModeToggle = document.getElementById('darkModeToggle'); + darkModeToggle.addEventListener('click', () => { + const isDark = document.documentElement.classList.contains('dark'); + updateTheme(!isDark); + }); + + // Mobile theme toggle + const darkModeToggleMobile = document.getElementById('darkModeToggleMobile'); + darkModeToggleMobile.addEventListener('click', () => { + const isDark = document.documentElement.classList.contains('dark'); + updateTheme(!isDark); + }); + + // Initialize theme + const savedTheme = localStorage.getItem('theme'); + if (savedTheme) { + updateTheme(savedTheme === 'dark'); + } else { + // Default to dark theme + updateTheme(true); + } + + // Header scroll behavior + const header = document.querySelector('header'); + let lastScroll = 0; + + window.addEventListener('scroll', () => { + const currentScroll = window.pageYOffset; + + if (currentScroll <= 0) { + header.classList.remove('shadow-lg', 'bg-white/95', 'dark:bg-gray-900/95'); + header.style.transform = 'translateY(0)'; + return; + } + + if (currentScroll > lastScroll && !header.contains(document.activeElement)) { + // Scrolling down + header.style.transform = 'translateY(-100%)'; + } else { + // Scrolling up + header.style.transform = 'translateY(0)'; + header.classList.add('shadow-lg', 'bg-white/95', 'dark:bg-gray-900/95'); + } + + lastScroll = currentScroll; + }); + + // Menu tab functionality + const tabButtons = document.querySelectorAll('.tab-button'); + const menuContents = document.querySelectorAll('.menu-content'); + + tabButtons.forEach(button => { + button.addEventListener('click', () => { + // Remove active state from all buttons + tabButtons.forEach(btn => { + btn.classList.remove('bg-pizza-red', 'text-white'); + btn.classList.add('bg-gray-200', 'dark:bg-pizza-darker', 'text-gray-700', 'dark:text-white'); + }); + + // Add active state to clicked button + button.classList.remove('bg-gray-200', 'dark:bg-pizza-darker', 'text-gray-700', 'dark:text-white'); + button.classList.add('bg-pizza-red', 'text-white'); + + // Hide all content sections + menuContents.forEach(content => { + content.classList.add('hidden'); + }); + + // Show selected content + const targetContent = document.getElementById(`${button.dataset.tab}-content`); + targetContent.classList.remove('hidden'); + }); + }); + + // Price group toggles + const priceGroups = document.querySelectorAll('.price-group-header'); + + priceGroups.forEach(group => { + group.addEventListener('click', () => { + const content = group.nextElementSibling; + const arrow = group.querySelector('svg'); + + // Toggle content visibility + content.classList.toggle('hidden'); + + // Rotate arrow + if (content.classList.contains('hidden')) { + arrow.style.transform = 'rotate(0deg)'; + } else { + arrow.style.transform = 'rotate(180deg)'; + } + }); + }); + + // Auto-expand first price group + const firstPriceGroup = document.querySelector('.price-group-header'); + if (firstPriceGroup) { + firstPriceGroup.click(); + } + + // Legend hover effect + const legendContainer = document.querySelector('.legend-container'); + const legend = legendContainer.querySelector('.price-legend'); + + legendContainer.addEventListener('mouseenter', () => { + legend.style.opacity = '0'; + }); + + legendContainer.addEventListener('mouseleave', () => { + legend.style.opacity = '1'; + }); + + // Smooth scroll for menu links + const menuLinks = document.querySelectorAll('a[href*="#menu-section"]'); + + menuLinks.forEach(link => { + link.addEventListener('click', function(e) { + e.preventDefault(); + console.log('Menu link clicked'); // Debug log + + const menuSection = document.getElementById('menu-section'); + if (menuSection) { + const headerHeight = document.querySelector('header').offsetHeight; + const menuPosition = menuSection.offsetTop - headerHeight; + + window.scrollTo({ + top: menuPosition, + behavior: 'smooth' + }); + } else { + console.error('Menu section not found'); // Debug log + } + }); + }); + + // Language dropdown functionality + const languageButton = document.getElementById('languageButton'); + const languageDropdown = document.getElementById('languageDropdown'); + + if (languageButton && languageDropdown) { + // Toggle dropdown + languageButton.addEventListener('click', (e) => { + e.stopPropagation(); + languageDropdown.classList.toggle('hidden'); + }); + + // Close dropdown when clicking outside + document.addEventListener('click', (e) => { + if (!languageButton.contains(e.target) && !languageDropdown.contains(e.target)) { + languageDropdown.classList.add('hidden'); + } + }); + + // Close dropdown when clicking a language option + languageDropdown.querySelectorAll('a').forEach(link => { + link.addEventListener('click', () => { + languageDropdown.classList.add('hidden'); + }); + }); + } + + // Auto-detect browser language + function detectLanguage() { + const browserLang = navigator.language.split('-')[0]; + const supportedLangs = ['de', 'en']; // Add your supported languages here + + if (!localStorage.getItem('userLanguage')) { + const detectedLang = supportedLangs.includes(browserLang) ? browserLang : 'de'; + localStorage.setItem('userLanguage', detectedLang); + + // Redirect if needed + const currentLang = document.documentElement.lang; + if (currentLang !== detectedLang) { + const newPath = window.location.pathname.replace(`/${currentLang}/`, `/${detectedLang}/`); + window.location.href = newPath; + } + } + } + + detectLanguage(); + + function checkOpenStatus() { + const now = new Date(); + const currentTime = now.toLocaleTimeString('de-DE', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + const currentDay = now.toLocaleDateString('de-DE', { weekday: 'long' }); + + // Get opening hours from data attributes + const hoursRows = document.querySelectorAll('.hours-row'); + let isOpen = false; + + hoursRows.forEach(row => { + const rowDay = row.dataset.day; + const openTime = row.dataset.open; + const closeTime = row.dataset.close; + + // Convert times to comparable numbers (e.g., "14:30" -> 1430) + const currentNum = parseInt(currentTime.replace(':', '')); + const openNum = parseInt(openTime.replace(':', '')); + const closeNum = parseInt(closeTime.replace(':', '')); + + if (rowDay === currentDay && currentNum >= openNum && currentNum <= closeNum) { + isOpen = true; + row.classList.add('border-2', 'border-status-green'); + } else { + row.classList.remove('border-2', 'border-status-green'); + } + }); + + // Update status bar + const statusBar = document.getElementById('status-bar'); + const statusText = document.getElementById('status-text'); + + if (statusBar && statusText) { + if (isOpen) { + statusBar.classList.remove('bg-status-red'); + statusBar.classList.add('bg-status-green'); + statusText.textContent = statusText.dataset.textOpen; + } else { + statusBar.classList.remove('bg-status-green'); + statusBar.classList.add('bg-status-red'); + statusText.textContent = statusText.dataset.textClosed; + } + } + + return { isOpen, currentDay, currentTime }; + } + + // Update status every minute + setInterval(checkOpenStatus, 60000); + // Initial check + checkOpenStatus(); +}); \ No newline at end of file diff --git a/static/js/menu.js b/static/js/menu.js new file mode 100644 index 0000000..055a51b --- /dev/null +++ b/static/js/menu.js @@ -0,0 +1,29 @@ +// Menu category handling +document.addEventListener('DOMContentLoaded', function() { + // Handle tab switching + const allTabButtons = document.querySelectorAll('[data-tab]'); + allTabButtons.forEach(button => { + button.addEventListener('click', function() { + const tabId = this.getAttribute('data-tab'); + + // Hide all content + document.querySelectorAll('.menu-content').forEach(content => { + content.classList.add('hidden'); + }); + + // Show selected content + document.getElementById(tabId + '-content').classList.remove('hidden'); + + // Update active states for all buttons with same data-tab + allTabButtons.forEach(btn => { + if (btn.getAttribute('data-tab') === tabId) { + btn.classList.add('bg-pizza-red', 'text-white'); + btn.classList.remove('bg-gray-200', 'dark:bg-pizza-darker', 'text-gray-700'); + } else { + btn.classList.remove('bg-pizza-red', 'text-white'); + btn.classList.add('bg-gray-200', 'dark:bg-pizza-darker', 'text-gray-700'); + } + }); + }); + }); +}); \ No newline at end of file diff --git a/static/js/opening-status.js b/static/js/opening-status.js new file mode 100644 index 0000000..1c9d527 --- /dev/null +++ b/static/js/opening-status.js @@ -0,0 +1,49 @@ +function updateTime() { + const now = new Date(); + const currentTime = now.toLocaleTimeString('de-DE', { + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + + // Update displayed time + const timeDisplay = document.getElementById('current-time'); + if (timeDisplay) { + timeDisplay.textContent = currentTime; + } + + // Check opening status + fetch('/api/check-open-status') + .then(response => response.json()) + .then(status => { + // Update status bar + const statusBar = document.getElementById('status-bar'); + const statusText = document.getElementById('status-text'); + + if (statusBar) { + statusBar.className = status.isOpen + ? 'bg-status-green transition-colors duration-300' + : 'bg-status-red transition-colors duration-300'; + } + + if (statusText) { + const textOpen = statusText.dataset.textOpen; + const textClosed = statusText.dataset.textClosed; + statusText.textContent = status.isOpen ? textOpen : textClosed; + } + + // Update opening hours rows + document.querySelectorAll('.hours-row').forEach(row => { + const open = row.dataset.open; + const close = row.dataset.close; + const isCurrentTimeSlot = currentTime >= open && currentTime <= close; + + row.classList.toggle('border-2', isCurrentTimeSlot && status.isOpen); + row.classList.toggle('border-status-green', isCurrentTimeSlot && status.isOpen); + }); + }); +} + +// Update immediately and then every minute +updateTime(); +setInterval(updateTime, 60000); \ No newline at end of file diff --git a/static/js/theme.js b/static/js/theme.js new file mode 100644 index 0000000..48e7b93 --- /dev/null +++ b/static/js/theme.js @@ -0,0 +1,42 @@ +// Theme handling +function getPreferredTheme() { + if (localStorage.getItem('theme') === 'dark' || + (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) { + return 'dark'; + } + return 'light'; +} + +function setTheme(theme) { + if (theme === 'dark') { + document.documentElement.classList.add('dark'); + } else { + document.documentElement.classList.remove('dark'); + } + localStorage.setItem('theme', theme); +} + +function toggleTheme() { + const isDark = document.documentElement.classList.contains('dark'); + setTheme(isDark ? 'light' : 'dark'); +} + +// Initialize theme +if (getPreferredTheme() === 'dark') { + setTheme('dark'); +} + +// Add click event listener +document.addEventListener('DOMContentLoaded', () => { + const darkModeToggle = document.getElementById('darkModeToggle'); + if (darkModeToggle) { + darkModeToggle.addEventListener('click', toggleTheme); + } +}); + +// Watch for system theme changes +window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => { + if (!localStorage.getItem('theme')) { + setTheme(e.matches ? 'dark' : 'light'); + } +}); \ No newline at end of file