diff --git a/.dockerignore b/.dockerignore index 377deb6..b8ef5a2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ .git +.gitea .gitignore .dockerignore Dockerfile diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml new file mode 100644 index 0000000..46bd129 --- /dev/null +++ b/.gitea/workflows/docker-publish.yml @@ -0,0 +1,58 @@ +name: Build and Push Container Image + +on: + push: + branches: + - main + - multilingual + - redesign + tags: + - 'v*' + workflow_dispatch: + +jobs: + build-and-push: + name: Build & Push to Gitea Package Registry + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to Gitea Container Registry + env: + REGISTRY_USER: ${{ gitea.actor }} + REGISTRY_TOKEN: ${{ secrets.GITEATOKEN }} + SERVER_URL: ${{ gitea.server_url }} + run: | + REGISTRY_HOST=$(echo "${SERVER_URL}" | sed -E 's#^https?://##; s#/$##') + echo "Logging in to ${REGISTRY_HOST} as ${REGISTRY_USER}..." + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY_HOST}" -u "${REGISTRY_USER}" --password-stdin + + - name: Build, Validate, and Push Docker Image + env: + SERVER_URL: ${{ gitea.server_url }} + REPO_NAME: ${{ gitea.repository }} + REF_NAME: ${{ gitea.ref_name }} + SHA: ${{ gitea.sha }} + run: | + REGISTRY_HOST=$(echo "${SERVER_URL}" | sed -E 's#^https?://##; s#/$##') + IMAGE_PATH=$(echo "${REPO_NAME}" | tr '[:upper:]' '[:lower:]') + FULL_IMAGE="${REGISTRY_HOST}/${IMAGE_PATH}" + SHORT_SHA=$(echo "${SHA}" | cut -c1-7) + SAFE_REF=$(echo "${REF_NAME}" | sed -E 's#[^a-zA-Z0-9_.-]#-#g') + + echo "Building image ${FULL_IMAGE}..." + docker build --pull \ + -t "${FULL_IMAGE}:${SAFE_REF}" \ + -t "${FULL_IMAGE}:${SHORT_SHA}" \ + -t "${FULL_IMAGE}:latest" \ + . + + echo "Validating PHP syntax inside built container..." + docker run --rm "${FULL_IMAGE}:${SHORT_SHA}" sh -c 'find /var/www/html -name "*.php" -exec php -l {} +' + + echo "Pushing tags to Gitea Package Registry..." + docker push "${FULL_IMAGE}:${SAFE_REF}" + docker push "${FULL_IMAGE}:${SHORT_SHA}" + docker push "${FULL_IMAGE}:latest" diff --git a/css/frontend.css b/css/frontend.css index cccaf2c..63cf63a 100644 --- a/css/frontend.css +++ b/css/frontend.css @@ -68,6 +68,52 @@ --container-max: 1200px; } +/* ========================================================================== + Dark Mode Theme Variables & Overrides + ========================================================================== */ +[data-theme="dark"] { + --primary: #15658a; + --primary-dark: #e2f1f8; + --primary-light: #1a3650; + --primary-hover: #1b7aa6; + + --accent: #38bdf8; + --accent-hover: #7dd3fc; + --accent-light: #1e3a52; + --accent-dark: #bae6fd; + + --secondary: #14b8a6; + --secondary-light: #134e4a; + + --bg-body: #0b1520; + --bg-card: #122130; + --bg-alt: #182b3e; + + --text-main: #e2e8f0; + --text-muted: #94a3b8; + --text-light: #64748b; + --text-white: #ffffff; + + --border: #23394f; + --border-focus: #38bdf8; + + --success: #86efac; + --success-bg: #064e3b; + --success-border: #059669; + --danger: #fca5a5; + --danger-bg: #450a0a; + --danger-border: #dc2626; + --warning: #fcd34d; + --warning-bg: #451a03; + + --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.35); + --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.45), 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -2px rgba(0, 0, 0, 0.35); + --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.55), 0 4px 6px -4px rgba(0, 0, 0, 0.4); + --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.6), 0 8px 10px -6px rgba(0, 0, 0, 0.45); + color-scheme: dark; +} + /* ========================================================================== Reset & Base Styles ========================================================================== */ @@ -374,6 +420,18 @@ li { display: block; } +.site-logo-dark { + display: none; +} + +[data-theme="dark"] .site-logo-light { + display: none; +} + +[data-theme="dark"] .site-logo-dark { + display: block; +} + .logo-badge { width: 44px; height: 44px; @@ -443,6 +501,7 @@ li { text-decoration: none; transition: var(--transition); position: relative; + white-space: nowrap; } .nav-link:hover { @@ -514,6 +573,8 @@ li { text-decoration: none; transition: var(--transition); box-shadow: 0 2px 8px rgba(2, 132, 199, 0.25); + white-space: nowrap; + flex-shrink: 0; } .header-cta-btn:hover { @@ -527,6 +588,7 @@ li { width: 16px; height: 16px; fill: currentColor; + flex-shrink: 0; } /* Mobile Toggle Hamburger */ @@ -578,7 +640,7 @@ li { .hero-content { position: relative; z-index: 2; - max-width: 820px; + max-width: 100%; } .hero-badge { @@ -710,6 +772,12 @@ li { cursor: pointer; transition: var(--transition); line-height: 1.2; + white-space: nowrap; + flex-shrink: 0; +} + +.btn svg { + flex-shrink: 0; } .btn:hover { @@ -1563,15 +1631,168 @@ textarea.form-control { gap: 1rem; } +.footer-bottom-links { + display: inline-flex; + align-items: center; + flex-wrap: wrap; + gap: 1rem; +} + .footer-bottom a { color: #cbd5e1; - margin-left: 1rem; + margin-left: 0; } .footer-bottom a:hover { color: #ffffff; } +/* Unobtrusive Footer Dark Mode Toggle */ +.theme-toggle-btn { + background: transparent; + border: none; + border-left: 1px solid rgba(255, 255, 255, 0.15); + padding: 0 0 0 1rem; + margin: 0; + color: #94a3b8; + font-family: inherit; + font-size: 0.82rem; + line-height: 1.4; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 0.35rem; + transition: var(--transition); +} + +.theme-toggle-btn:hover { + color: #e2e8f0; +} + +.theme-toggle-btn svg { + width: 14px; + height: 14px; + fill: currentColor; + opacity: 0.8; + flex-shrink: 0; +} + +.theme-icon-sun { + display: none; +} + +[data-theme="dark"] .theme-icon-moon { + display: none; +} + +[data-theme="dark"] .theme-icon-sun { + display: inline-block; +} + +/* ========================================================================== + Dark Mode Component Overrides + ========================================================================== */ +.team-staff-card { + background: linear-gradient(135deg, #f8fafc 0%, #e0f2fe 100%); +} + +.info-callout-box { + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); +} + +[data-theme="dark"] .top-bar { + background-color: #060d14; +} + +[data-theme="dark"] .site-header { + border-bottom: 1px solid var(--border); +} + +[data-theme="dark"] .site-footer { + background-color: #060d14; + border-top: 1px solid var(--border); +} + +[data-theme="dark"] .skip-link { + background-color: #0c2b3d; +} + +[data-theme="dark"] .page-banner { + background: linear-gradient(135deg, #071926 0%, #0d354a 100%); + border-bottom: 1px solid var(--border); +} + +[data-theme="dark"] .hero-banner { + background: linear-gradient(135deg, #061520 0%, #092c3e 55%, #0c3b50 100%); +} + +[data-theme="dark"] .btn-primary, +[data-theme="dark"] .header-cta-btn { + background-color: #0284c7; + color: #ffffff; +} + +[data-theme="dark"] .btn-primary:hover, +[data-theme="dark"] .header-cta-btn:hover { + background-color: #0369a1; + color: #ffffff; +} + +[data-theme="dark"] .card:hover, +[data-theme="dark"] .doctor-card:hover { + border-color: #335372; +} + +[data-theme="dark"] .highlight-panel { + background: linear-gradient(to right, #152738, #122130); +} + +[data-theme="dark"] .team-staff-card, +[data-theme="dark"] .info-callout-box { + background: linear-gradient(135deg, #152738 0%, #1a3650 100%); +} + +[data-theme="dark"] .condition-warning { + background-color: #3b1219; + border-color: #7f1d1d; + color: #fecdd3; +} + +[data-theme="dark"] .condition-info { + background-color: #132d42; + border-color: #1e496b; + color: #bae6fd; +} + +[data-theme="dark"] .chamber-badge { + color: #bae6fd; +} + +[data-theme="dark"] .form-control { + background-color: #0e1a27; + color: var(--text-main); + border-color: var(--border); +} + +[data-theme="dark"] .form-control::placeholder { + color: #64748b; +} + +[data-theme="dark"] .holiday-notice-banner { + background: linear-gradient(135deg, #102436 0%, #16324a 100%); + border-bottom-color: #1e496b; + color: #bae6fd; +} + +[data-theme="dark"] .holiday-dismiss-btn { + border-color: #38bdf8; + color: #bae6fd; +} + +[data-theme="dark"] .map-iframe-wrapper iframe { + filter: invert(90%) hue-rotate(180deg) brightness(95%) contrast(90%); +} + /* ========================================================================== Responsive Media Queries ========================================================================== */ @@ -1586,16 +1807,16 @@ textarea.form-control { min-width: 0; } -h1, h2, h3, h4, p, li, .card, .location-card, .contact-detail-value, .btn { +h1, h2, h3, h4, p, li, .card, .location-card, .contact-detail-value { overflow-wrap: break-word; word-wrap: break-word; hyphens: auto; } .btn { - flex-wrap: wrap; text-align: center; max-width: 100%; + hyphens: none; } /* Prioritized Address Hiding when space is limited */ @@ -1603,6 +1824,11 @@ h1, h2, h3, h4, p, li, .card, .location-card, .contact-detail-value, .btn { .top-bar-addr-satellite { display: none !important; } + + .site-logo-img { + height: 62px; + max-width: 300px; + } } /* Compact Header for Medium Laptops & Tablets (max 1100px) */ @@ -1770,8 +1996,8 @@ h1, h2, h3, h4, p, li, .card, .location-card, .contact-detail-value, .btn { text-align: center; } - .footer-bottom a { - margin: 0 0.5rem; + .footer-bottom-links { + justify-content: center; } .content-card { @@ -1852,6 +2078,13 @@ h1, h2, h3, h4, p, li, .card, .location-card, .contact-detail-value, .btn { flex-direction: column; } + .hero-actions .btn, + .btn { + white-space: normal; + flex-wrap: wrap; + flex-shrink: 1; + } + .hero-actions .btn { width: 100%; } diff --git a/img/logo_dialysezentrum_dark.png b/img/logo_dialysezentrum_dark.png new file mode 100644 index 0000000..c420cf3 Binary files /dev/null and b/img/logo_dialysezentrum_dark.png differ diff --git a/view/contact.php b/view/contact.php index f3e8bfc..89153d0 100644 --- a/view/contact.php +++ b/view/contact.php @@ -139,7 +139,7 @@ title="= $isEn ? 'Map Center Dialysis Berlin-Mahlsdorf' : 'Karte Zentrumsdialyse Berlin-Mahlsdorf' ?>"> -
+ class="site-logo-img site-logo-light">
+
@@ -435,10 +450,15 @@ if ($canonicalUrl && $currentAction !== 'main') {