From 19a6bbd392e73a513040bfd1b4417f02f3c48ca4 Mon Sep 17 00:00:00 2001 From: ScriptSorcerer20 Date: Sun, 5 Apr 2026 00:42:25 +0200 Subject: [PATCH] Made consistent design for landing page and login. Landing page has all the text and design. Added language support for english and german (swappable) --- app/routes/main_routes.py | 3 +- app/static/css/dashboard.css | 422 ++++++++++++---------- app/static/css/index.css | 514 +++++++++++++++++++++++++++ app/static/css/login.css | 257 +++++++++----- app/static/js/index.js | 238 +++++++++++++ app/templates/index.html | 225 +++++++++--- app/templates/login.html | 9 +- app/templates/login_settings.html | 9 +- app/templates/sign_up.html | 9 +- app/templates/two_factor_verify.html | 9 +- 10 files changed, 1347 insertions(+), 348 deletions(-) create mode 100644 app/static/css/index.css create mode 100644 app/static/js/index.js diff --git a/app/routes/main_routes.py b/app/routes/main_routes.py index ad17578..fbb0f29 100644 --- a/app/routes/main_routes.py +++ b/app/routes/main_routes.py @@ -226,7 +226,8 @@ def build_save_point_query(): @main_bp.route("/") def home(): - return render_template("index.html") + initial_language = request.accept_languages.best_match(["de", "en"]) or "de" + return render_template("index.html", initial_language=initial_language) @main_bp.route("/dashboard") diff --git a/app/static/css/dashboard.css b/app/static/css/dashboard.css index b83c8e2..634fb38 100644 --- a/app/static/css/dashboard.css +++ b/app/static/css/dashboard.css @@ -1,10 +1,58 @@ +/* ==================== Base ==================== */ + body { margin: 0; font-family: Arial, sans-serif; background: #f5f7fa; } -/* 🔝 Navbar */ + +/* ==================== Layout And Cards ==================== */ + +.container { + max-width: 900px; + margin: 30px auto; + padding: 10px; +} + +.card { + background: white; + padding: 20px; + margin-bottom: 25px; + border-radius: 12px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); +} + +.flash-stack { + display: grid; + gap: 12px; + margin-bottom: 18px; +} + +.flash-message { + margin: 0; + padding: 12px 14px; + border-radius: 10px; +} + +.flash-message.success { + color: #166534; + background: #dcfce7; +} + +.flash-message.info { + color: #1d4ed8; + background: #dbeafe; +} + +.flash-message.error { + color: #b91c1c; + background: #fee2e2; +} + + +/* ==================== Navigation ==================== */ + .navbar { display: flex; justify-content: space-between; @@ -47,49 +95,103 @@ body { text-decoration: underline; } -/* 📦 Layout */ -.container { - max-width: 900px; - margin: 30px auto; - padding: 10px; + +/* ==================== Shared Buttons ==================== */ + +.btn { + padding: 10px 15px; + margin: 5px; + border: none; + border-radius: 8px; + background: #3b82f6; + color: white; + cursor: pointer; } -/* 📇 Cards */ -.card { - background: white; - padding: 20px; - margin-bottom: 25px; - border-radius: 12px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); +.btn:hover { + background: #2563eb; } -.flash-stack { - display: grid; - gap: 12px; - margin-bottom: 18px; +.btn:disabled, +.btn:disabled:hover { + background: #94a3b8; + cursor: not-allowed; } -.flash-message { +.btn.secondary { + background: #64748b; +} + +.btn.secondary:hover { + background: #475569; +} + +.btn.danger { + background: #ef4444; +} + +.btn.danger:hover { + background: #dc2626; +} + +.btn.active { + background: #22c55e; +} + +.btn.ghost { + background: #e2e8f0; + color: #1e293b; +} + +.btn.ghost:hover { + background: #cbd5e1; +} + + +/* ==================== User Card Details ==================== */ + +.inline-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 10px; +} + +.inline-actions form { margin: 0; - padding: 12px 14px; - border-radius: 10px; } -.flash-message.success { - color: #166534; - background: #dcfce7; +.inline-actions .btn { + margin: 0; } -.flash-message.info { - color: #1d4ed8; - background: #dbeafe; + +/* ==================== Mode Switch And Main Actions ==================== */ + +.mode-switch { + margin-bottom: 15px; } -.flash-message.error { - color: #b91c1c; - background: #fee2e2; +.mode-switch .btn { + margin: 5px; } +.controls { + margin-top: 15px; + display: flex; + justify-content: center; + flex-wrap: wrap; + gap: 10px; +} + +.solver-controls .btn, +.controls .btn { + margin: 0; +} + + +/* ==================== Game Overview ==================== */ + .game-card { text-align: center; } @@ -121,6 +223,11 @@ body { background: #bfdbfe; } +.board-size-info { + margin: 0 0 10px; + color: #475569; +} + .game-stats { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); @@ -162,12 +269,14 @@ body { font-weight: bold; } + +/* ==================== Chessboard ==================== */ + .board { overflow-x: auto; padding-bottom: 8px; } -/* ♟️ Board */ .board-grid { --label-size: 30px; --cell-size: 60px; @@ -209,21 +318,6 @@ body { transform: scale(1.05); } -.cell.solver-place { - box-shadow: inset 0 0 0 4px #22c55e; - animation: solverPulse 0.35s ease; -} - -.cell.solver-remove { - box-shadow: inset 0 0 0 4px #f59e0b; - animation: solverPulse 0.35s ease; -} - -.cell.solver-solution { - box-shadow: inset 0 0 0 4px #2563eb; - animation: solverPulse 0.35s ease; -} - .white { background: #e2e8f0; } @@ -237,74 +331,8 @@ body { background: #ef4444 !important; } -/* 🎛️ Buttons */ -.controls { - margin-top: 15px; - display: flex; - justify-content: center; - flex-wrap: wrap; - gap: 10px; -} -.btn { - padding: 10px 15px; - margin: 5px; - border: none; - border-radius: 8px; - background: #3b82f6; - color: white; - cursor: pointer; -} - -.btn:hover { - background: #2563eb; -} - -.btn:disabled, -.btn:disabled:hover { - background: #94a3b8; - cursor: not-allowed; -} - -.btn.secondary { - background: #64748b; -} - -.btn.secondary:hover { - background: #475569; -} - -.btn.danger { - background: #ef4444; -} - -.btn.danger:hover { - background: #dc2626; -} - -#queenList { - list-style: none; - padding: 0; -} - -#queenList li { - padding: 5px; - background: #f1f5f9; - margin: 4px 0; - border-radius: 6px; -} - -.mode-switch { - margin-bottom: 15px; -} - -.mode-switch .btn { - margin: 5px; -} - -.btn.active { - background: #22c55e; -} +/* ==================== Solver And Animation Details ==================== */ .solver-panel { margin-top: 12px; @@ -350,11 +378,6 @@ body { margin-top: 14px; } -.solver-controls .btn, -.controls .btn { - margin: 0; -} - .solver-speed { display: grid; grid-template-columns: auto 1fr auto; @@ -379,11 +402,91 @@ body { font-variant-numeric: tabular-nums; } -.board-size-info { - margin: 0 0 10px; - color: #475569; +.cell.solver-place { + box-shadow: inset 0 0 0 4px #22c55e; + animation: solverPulse 0.35s ease; } +.cell.solver-remove { + box-shadow: inset 0 0 0 4px #f59e0b; + animation: solverPulse 0.35s ease; +} + +.cell.solver-solution { + box-shadow: inset 0 0 0 4px #2563eb; + animation: solverPulse 0.35s ease; +} + + +/* ==================== Figure Details ==================== */ + +#queenList { + list-style: none; + padding: 0; +} + +#queenList li { + padding: 5px; + background: #f1f5f9; + margin: 4px 0; + border-radius: 6px; +} + + +/* ==================== Save History And Tables ==================== */ + +.history-header { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 12px; + margin-bottom: 14px; +} + +.history-header h3 { + margin: 0; +} + +.history-subtitle { + margin: 6px 0 0; + color: #64748b; +} + +.history-table-wrapper { + overflow-x: auto; +} + +.history-table { + width: 100%; + border-collapse: collapse; + min-width: 720px; +} + +.history-table th, +.history-table td { + padding: 12px 10px; + border-bottom: 1px solid #e2e8f0; + text-align: left; +} + +.history-table th { + color: #334155; + font-size: 14px; +} + +.history-table td { + color: #475569; + font-size: 14px; +} + +.history-empty { + color: #64748b; + text-align: center !important; +} + + +/* ==================== Sidebars And Save Points ==================== */ + .sidebar { position: fixed; top: 0; @@ -525,78 +628,8 @@ body { margin: 0; } -.inline-actions { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 10px; -} -.inline-actions form { - margin: 0; -} - -.inline-actions .btn { - margin: 0; -} - -.history-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 12px; - margin-bottom: 14px; -} - -.history-header h3 { - margin: 0; -} - -.history-subtitle { - margin: 6px 0 0; - color: #64748b; -} - -.history-table-wrapper { - overflow-x: auto; -} - -.history-table { - width: 100%; - border-collapse: collapse; - min-width: 720px; -} - -.history-table th, -.history-table td { - padding: 12px 10px; - border-bottom: 1px solid #e2e8f0; - text-align: left; -} - -.history-table th { - color: #334155; - font-size: 14px; -} - -.history-table td { - color: #475569; - font-size: 14px; -} - -.history-empty { - color: #64748b; - text-align: center !important; -} - -.btn.ghost { - background: #e2e8f0; - color: #1e293b; -} - -.btn.ghost:hover { - background: #cbd5e1; -} +/* ==================== Backdrops And Modal ==================== */ .sidebar-backdrop { position: fixed; @@ -680,6 +713,9 @@ body { line-height: 1.6; } + +/* ==================== Animations ==================== */ + @keyframes solverPulse { 0% { transform: scale(0.97); @@ -694,6 +730,9 @@ body { } } + +/* ==================== Responsive ==================== */ + @media (max-width: 720px) { .container { padding: 12px; @@ -703,7 +742,6 @@ body { padding: 15px 18px; } - .nav-logout-form { margin-left: 0; } diff --git a/app/static/css/index.css b/app/static/css/index.css new file mode 100644 index 0000000..1aaef0c --- /dev/null +++ b/app/static/css/index.css @@ -0,0 +1,514 @@ +:root { + --bg: #f4efe7; + --bg-accent: #efe3cd; + --surface: rgba(255, 250, 242, 0.82); + --surface-strong: #fffaf2; + --text: #1e1f1d; + --muted: #5f615c; + --navy: #1f3140; + --navy-soft: #324d63; + --gold: #c68b2c; + --gold-soft: #e7bf77; + --line: rgba(31, 49, 64, 0.12); + --shadow: 0 24px 60px rgba(37, 42, 48, 0.14); +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + min-height: 100vh; + color: var(--text); + font-family: Georgia, "Times New Roman", serif; + background: + radial-gradient(circle at top left, rgba(255, 255, 255, 0.9), transparent 24%), + radial-gradient(circle at 82% 18%, rgba(198, 139, 44, 0.2), transparent 20%), + linear-gradient(180deg, #f7f0e5 0%, #efe5d5 52%, #f4efe7 100%); +} + +a { + color: inherit; + text-decoration: none; +} + +main { + width: min(1200px, calc(100vw - 32px)); + margin: 0 auto; + padding: 34px 0 56px; +} + +.landing-nav { + width: 100%; + margin: 0; + padding: 18px max(16px, calc((100vw - 1200px) / 2)) 16px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + position: sticky; + top: 0; + z-index: 20; + border-top: 0; + border-right: 4px solid #12212d; + border-bottom: 4px solid #12212d; + border-left: 4px solid #12212d; + background: + linear-gradient(180deg, rgba(17, 29, 39, 0.96), rgba(31, 49, 64, 0.94)); + backdrop-filter: blur(12px); + box-shadow: 0 18px 40px rgba(31, 49, 64, 0.18); +} + +.brand { + display: inline-flex; + align-items: center; + gap: 12px; + color: #fff7eb; + font-size: 18px; + font-weight: 700; + letter-spacing: 0.03em; +} + +.brand-mark { + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: 8px; + background: linear-gradient(135deg, var(--gold), var(--gold-soft)); + color: #2c2215; + font-size: 22px; +} + +.landing-links { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 14px; + color: #ecf4fb; +} + +.landing-links a:not(.nav-cta) { + padding: 12px 14px; + border: 2px solid transparent; + background: rgba(255, 255, 255, 0.04); + opacity: 0.92; +} + +.landing-links a:not(.nav-cta):hover { + opacity: 1; + border-color: rgba(231, 191, 119, 0.45); +} + +.lang-switch, +.nav-cta, +.hero-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 13px 20px; + font-weight: 700; + transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease; +} + +.lang-switch { + min-width: 68px; + border: 2px solid rgba(231, 191, 119, 0.45); + background: rgba(255, 255, 255, 0.06); + color: #fff7eb; + cursor: pointer; + font-family: inherit; +} + +.nav-cta { + background: linear-gradient(135deg, var(--gold), var(--gold-soft)); + color: #2d2418; +} + +.nav-cta.secondary { + background: rgba(255, 255, 255, 0.08); + color: #f8fafc; + border: 1px solid rgba(255, 255, 255, 0.16); +} + +.lang-switch:hover, +.nav-cta:hover, +.hero-button:hover { + transform: translateY(-2px); +} + +.hero { + padding: 88px 0 56px; + display: grid; + grid-template-columns: minmax(0, 1.08fr) minmax(320px, 0.92fr); + gap: 42px; + align-items: center; +} + +.eyebrow { + margin: 0 0 12px; + color: var(--navy-soft); + text-transform: uppercase; + letter-spacing: 0.14em; + font-size: 13px; + font-weight: 700; +} + +.hero h1, +.info-band h2, +.section-heading h2, +.cta-panel h2 { + margin: 0; + line-height: 0.95; + letter-spacing: -0.03em; + font-size: clamp(2.8rem, 5vw, 5.5rem); + color: #182029; +} + +.hero-text, +.info-band p, +.feature-card p, +.journey-step p, +.cta-panel p { + font-family: "Trebuchet MS", "Segoe UI", sans-serif; + font-size: 18px; + line-height: 1.7; + color: var(--muted); +} + +.hero-text { + max-width: 640px; + margin: 24px 0 0; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 14px; + margin-top: 30px; +} + +.hero-button.primary { + background: linear-gradient(135deg, var(--navy), var(--navy-soft)); + color: #f9fbfd; + box-shadow: 0 14px 30px rgba(31, 49, 64, 0.22); +} + +.hero-button.secondary { + border: 1px solid rgba(31, 49, 64, 0.16); + background: rgba(255, 255, 255, 0.5); + color: var(--navy); +} + +.hero-facts { + margin-top: 30px; + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 14px; +} + +.hero-facts article { + padding: 18px; + border-radius: 10px; + background: rgba(255, 250, 242, 0.65); + border: 1px solid rgba(31, 49, 64, 0.08); + box-shadow: var(--shadow); +} + +.hero-facts strong { + display: block; + font-size: 18px; + color: var(--navy); +} + +.hero-facts span { + display: block; + margin-top: 6px; + font-family: "Trebuchet MS", "Segoe UI", sans-serif; + color: var(--muted); + line-height: 1.5; +} + +.hero-visual { + position: relative; + min-height: 620px; + display: grid; + place-items: center; +} + +.board-showcase { + width: min(100%, 520px); + aspect-ratio: 1; + padding: 18px; + display: grid; + grid-template-columns: repeat(8, 1fr); + gap: 0; + border-radius: 14px; + background: + linear-gradient(145deg, rgba(255, 255, 255, 0.85), rgba(247, 233, 210, 0.7)), + rgba(255, 255, 255, 0.5); + box-shadow: var(--shadow); + position: relative; + overflow: hidden; +} + +.board-cell { + position: relative; + display: grid; + place-items: center; + font-size: clamp(20px, 2vw, 30px); + transition: transform 0.45s ease, opacity 0.45s ease, background 0.45s ease; +} + +.board-cell.light { + background: #f7e6ca; +} + +.board-cell.dark { + background: #92613a; +} + +.board-cell.highlight { + background: #2f485f; +} + +.board-cell.queen::after { + content: "♛"; + color: #fff3df; + text-shadow: 0 8px 18px rgba(0, 0, 0, 0.28); + transform: scale(1); + animation: queenPulse 1.8s ease-in-out infinite; +} + +.floating-card { + position: absolute; + padding: 16px 18px; + border-radius: 8px; + background: rgba(255, 250, 242, 0.85); + border: 1px solid rgba(31, 49, 64, 0.08); + box-shadow: 0 18px 35px rgba(31, 49, 64, 0.14); + backdrop-filter: blur(10px); +} + +.solver-card { + top: 26px; + right: -8px; +} + +.timer-card { + left: -8px; + bottom: 34px; +} + +.floating-label { + margin: 0; + font-family: "Trebuchet MS", "Segoe UI", sans-serif; + font-size: 12px; + text-transform: uppercase; + letter-spacing: 0.12em; + color: var(--muted); +} + +.floating-value { + margin: 6px 0 0; + font-size: 22px; + color: var(--navy); +} + +.info-band, +.feature-section, +.journey-section, +.cta-section { + padding: 38px 0; +} + +.info-band-inner, +.cta-panel { + padding: 36px; + border-radius: 12px; + background: linear-gradient(135deg, rgba(255, 250, 242, 0.88), rgba(245, 230, 205, 0.7)); + border: 1px solid rgba(31, 49, 64, 0.08); + box-shadow: var(--shadow); +} + +.info-band h2 { + font-size: clamp(2.2rem, 4vw, 4.2rem); + margin-bottom: 18px; +} + +.section-heading { + max-width: 760px; + margin-bottom: 28px; +} + +.section-heading h2 { + font-size: clamp(2.2rem, 3.8vw, 4rem); +} + +.feature-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 18px; +} + +.feature-card, +.journey-step { + padding: 26px; + border-radius: 10px; + background: rgba(255, 250, 242, 0.72); + border: 1px solid rgba(31, 49, 64, 0.08); + box-shadow: var(--shadow); +} + +.feature-icon { + display: inline-grid; + place-items: center; + width: 54px; + height: 54px; + border-radius: 8px; + background: linear-gradient(135deg, var(--navy), var(--navy-soft)); + color: #fff3df; + font-size: 28px; +} + +.feature-card h3, +.journey-step h3 { + margin: 18px 0 10px; + font-size: 28px; + color: #1c2833; +} + +.journey-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 18px; +} + +.step-number { + display: inline-block; + margin-bottom: 18px; + color: var(--gold); + font-size: 14px; + font-weight: 700; + letter-spacing: 0.16em; +} + +.cta-panel { + text-align: center; +} + +.cta-panel h2 { + font-size: clamp(2.2rem, 4vw, 4.4rem); +} + +.cta-panel p { + max-width: 760px; + margin: 18px auto 0; +} + +.cta-panel .hero-actions { + justify-content: center; +} + +@keyframes queenPulse { + 0%, + 100% { + transform: scale(1) translateY(0); + } + + 50% { + transform: scale(1.08) translateY(-2px); + } +} + +@media (max-width: 980px) { + .hero { + grid-template-columns: 1fr; + gap: 28px; + padding-top: 54px; + } + + .hero-visual { + min-height: auto; + } + + .journey-grid, + .feature-grid, + .hero-facts { + grid-template-columns: 1fr; + } + + .solver-card, + .timer-card { + position: static; + } + + .hero-visual { + gap: 16px; + } +} + +@media (max-width: 760px) { + .landing-nav { + position: static; + width: 100%; + padding: 18px 16px; + border-left-width: 0; + border-right-width: 0; + gap: 14px; + align-items: flex-start; + flex-direction: column; + } + + .brand { + width: 100%; + justify-content: space-between; + gap: 8px; + font-size: 16px; + } + + .brand-mark { + display: none; + } + + .landing-links { + justify-content: flex-start; + gap: 10px; + width: 100%; + } + + .landing-links a:not(.nav-cta), + .lang-switch, + .nav-cta { + padding: 11px 14px; + } + + main { + width: min(100%, calc(100vw - 24px)); + } + + .hero h1, + .info-band h2, + .section-heading h2, + .cta-panel h2 { + line-height: 1.02; + } + + .info-band-inner, + .cta-panel, + .feature-card, + .journey-step { + padding: 24px; + } + + .floating-card { + width: 100%; + } +} diff --git a/app/static/css/login.css b/app/static/css/login.css index 21554a9..1431a37 100644 --- a/app/static/css/login.css +++ b/app/static/css/login.css @@ -1,17 +1,21 @@ :root { - --nav-bg-color: #172033; - --panel-bg-color: rgba(255, 255, 255, 0.18); - --panel-border-color: rgba(255, 255, 255, 0.24); - --text-color: #172033; - --muted-text-color: #475569; - --success-color: #15803d; + --bg: #f4efe7; + --bg-accent: #efe3cd; + --surface: rgba(255, 250, 242, 0.88); + --surface-strong: #fffaf2; + --surface-soft: rgba(255, 247, 235, 0.72); + --text-color: #1e1f1d; + --muted-text-color: #5f615c; + --navy: #1f3140; + --navy-soft: #324d63; + --gold: #c68b2c; + --gold-soft: #e7bf77; + --gold-pale: #f1ddae; + --line: rgba(31, 49, 64, 0.12); + --shadow: 0 24px 60px rgba(37, 42, 48, 0.14); + --success-color: #166534; --info-color: #1d4ed8; - --error-color: #b91c1c; -} - -@font-face { - font-family: "Geist Sans"; - src: url("https://assets.codepen.io/605876/GeistVF.ttf") format("truetype"); + --error-color: #991b1b; } *, @@ -24,74 +28,103 @@ body { margin: 0; min-height: 100vh; color: var(--text-color); - font-family: "Geist Sans", system-ui, sans-serif; + font-family: Georgia, "Times New Roman", serif; background: - radial-gradient(circle at top left, rgba(255, 255, 255, 0.45), transparent 32%), - linear-gradient(135deg, #d2ecff 0%, #b8c6db 48%, #7f8ea3 100%); + radial-gradient(circle at top left, rgba(255, 255, 255, 0.9), transparent 24%), + radial-gradient(circle at 82% 18%, rgba(198, 139, 44, 0.2), transparent 20%), + linear-gradient(180deg, #f7f0e5 0%, #efe5d5 52%, #f4efe7 100%); } -nav { +a { + color: inherit; + text-decoration: none; +} + +.auth-nav { width: 100%; - min-height: 68px; - padding: 15px 30px; + margin: 0; + padding: 18px max(16px, calc((100vw - 1200px) / 2)) 16px; display: flex; align-items: center; justify-content: space-between; - gap: 20px; - background: var(--nav-bg-color); + gap: 18px; + position: sticky; + top: 0; + z-index: 10; + border-top: 0; + border-right: 4px solid #12212d; + border-bottom: 4px solid #12212d; + border-left: 4px solid #12212d; + background: linear-gradient(180deg, rgba(17, 29, 39, 0.96), rgba(31, 49, 64, 0.94)); + backdrop-filter: blur(12px); + box-shadow: 0 18px 40px rgba(31, 49, 64, 0.18); } -nav ul { +.brand { + display: inline-flex; + align-items: center; + gap: 12px; + color: #fff7eb; + font-size: 18px; + font-weight: 700; + letter-spacing: 0.03em; +} + +.brand-mark { + width: 40px; + height: 40px; + display: grid; + place-items: center; + border-radius: 8px; + background: linear-gradient(135deg, var(--gold), var(--gold-soft)); + color: #2c2215; + font-size: 22px; +} + +.nav-links { margin: 0; padding: 0; list-style: none; display: flex; + align-items: center; flex-wrap: wrap; - gap: 20px; + gap: 12px; + color: #ecf4fb; } -nav a { - color: white; - text-decoration: none; - font-size: 16px; +.nav-links a, +.nav-button { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 46px; + padding: 11px 16px; + border: 2px solid transparent; + background: rgba(255, 255, 255, 0.06); + color: inherit; + font: inherit; + font-weight: 700; + cursor: pointer; + transition: transform 0.2s ease, border-color 0.2s ease, background 0.2s ease, box-shadow 0.2s ease; } -nav a:hover { - color: #93c5fd; +.nav-links a:hover, +.nav-button:hover { + transform: translateY(-2px); + border-color: rgba(231, 191, 119, 0.45); + color: #fff7eb; } .nav-form { margin: 0; - padding: 0; - width: auto; - background: none; - border: 0; - box-shadow: none; - backdrop-filter: none; -} - -.nav-button { - margin: 0; - padding: 0; - background: none; - border: 0; - color: white; - font: inherit; - cursor: pointer; -} - -.nav-button:hover { - transform: none; - box-shadow: none; - color: #93c5fd; } .auth-shell { - min-height: calc(100vh - 68px); + min-height: calc(100vh - 78px); display: flex; align-items: center; justify-content: center; - padding: 32px 16px; + padding: 38px 16px 48px; } .settings-shell { @@ -99,18 +132,17 @@ nav a:hover { } .auth-panel { - width: min(420px, 100%); + width: min(460px, 100%); padding: 34px; - border-radius: 20px; - background: var(--panel-bg-color); - border: 1px solid var(--panel-border-color); - backdrop-filter: blur(16px); - box-shadow: 0 18px 45px rgba(15, 23, 42, 0.18); + border-radius: 12px; + background: linear-gradient(135deg, rgba(255, 250, 242, 0.94), rgba(245, 230, 205, 0.8)); + border: 1px solid var(--line); + box-shadow: var(--shadow); animation: fadeIn 0.8s ease; } .settings-panel { - width: min(900px, 100%); + width: min(980px, 100%); } .settings-grid { @@ -134,12 +166,31 @@ nav a:hover { h1, h2 { margin-top: 0; + color: #182029; +} + +h1 { + margin-bottom: 10px; + line-height: 0.98; + letter-spacing: -0.03em; + font-size: clamp(2.2rem, 4vw, 3.6rem); +} + +h2 { + margin-bottom: 10px; + font-size: 1.55rem; +} + +.muted, +.field-hint { + font-family: "Trebuchet MS", "Segoe UI", sans-serif; + line-height: 1.6; + color: var(--muted-text-color); } .muted { margin: 0 0 20px; - color: var(--muted-text-color); - line-height: 1.5; + font-size: 17px; } form { @@ -148,65 +199,56 @@ form { gap: 8px; margin-top: 18px; padding: 24px; - border-radius: 16px; - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.22); + border-radius: 10px; + background: var(--surface-soft); + border: 1px solid rgba(31, 49, 64, 0.1); } label { margin-top: 8px; font-size: 14px; - font-weight: 600; + font-weight: 700; + color: var(--navy); + letter-spacing: 0.03em; } input { width: 100%; padding: 12px 14px; - border: 1px solid rgba(71, 85, 105, 0.25); - border-radius: 10px; + border: 1px solid rgba(31, 49, 64, 0.16); + border-radius: 0; font: inherit; background: rgba(255, 255, 255, 0.86); color: var(--text-color); } input:focus { - outline: 2px solid rgba(59, 130, 246, 0.35); - border-color: rgba(59, 130, 246, 0.5); + outline: 2px solid rgba(198, 139, 44, 0.28); + border-color: rgba(198, 139, 44, 0.55); } .field-hint { margin: 0; font-size: 13px; - color: var(--muted-text-color); -} - -.checkbox-row { - display: flex; - align-items: center; - gap: 10px; - margin: 10px 0 6px; -} - -.checkbox-row input { - width: auto; } button { margin-top: 14px; - padding: 12px 16px; + min-height: 50px; + padding: 12px 18px; border: none; - border-radius: 10px; - background: linear-gradient(135deg, #eff6ff, #dbeafe); - color: #172033; + border-radius: 0; + background: linear-gradient(135deg, var(--gold), var(--gold-soft)); + color: #2d2418; cursor: pointer; font-size: 16px; - font-weight: 600; + font-weight: 700; transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease; } button:hover { transform: translateY(-2px); - box-shadow: 0 10px 24px rgba(15, 23, 42, 0.18); + box-shadow: 0 12px 24px rgba(31, 49, 64, 0.16); } button:active { @@ -220,18 +262,20 @@ button:active { } .button-row button { - flex: 1 1 160px; + flex: 1 1 180px; } .secondary-button { - background: linear-gradient(135deg, #e2e8f0, #cbd5e1); + background: linear-gradient(135deg, var(--navy), var(--navy-soft)); + color: #f9fbfd; } .message { margin: 0 0 14px; padding: 12px 14px; - border-radius: 10px; + border-radius: 8px; font-size: 14px; + font-family: "Trebuchet MS", "Segoe UI", sans-serif; } .message.error { @@ -254,9 +298,9 @@ button:active { justify-content: center; margin: 8px 0 6px; padding: 18px; - border-radius: 16px; - background: rgba(255, 255, 255, 0.38); - border: 1px solid rgba(255, 255, 255, 0.24); + border-radius: 10px; + background: rgba(255, 250, 242, 0.72); + border: 1px solid rgba(31, 49, 64, 0.12); } .qr-code { @@ -266,7 +310,7 @@ button:active { min-width: 220px; min-height: 220px; padding: 10px; - border-radius: 14px; + border-radius: 8px; background: #ffffff; color: var(--muted-text-color); text-align: center; @@ -276,15 +320,32 @@ button:active { display: block; } -@media (max-width: 640px) { - nav { - padding: 16px 20px; +@media (max-width: 760px) { + .auth-nav { + position: static; + padding: 18px 16px; + border-left-width: 0; + border-right-width: 0; flex-direction: column; align-items: flex-start; } + .brand { + width: 100%; + font-size: 16px; + } + + .nav-links { + width: 100%; + } + + .nav-links a, + .nav-button { + padding: 10px 14px; + } + .auth-shell { - padding: 20px 12px 28px; + padding: 22px 12px 28px; } .auth-panel { diff --git a/app/static/js/index.js b/app/static/js/index.js new file mode 100644 index 0000000..be170c2 --- /dev/null +++ b/app/static/js/index.js @@ -0,0 +1,238 @@ +const HERO_BOARD_SIZE = 8; +const heroBoard = document.getElementById("heroBoard"); +const langSwitch = document.getElementById("langSwitch"); +const supportedLanguages = ["de", "en"]; +const initialLanguage = window.LANDING_INITIAL_LANGUAGE || "de"; + +const translations = { + de: { + "meta.title": "8-Damen- und 8-Türme Solver", + "nav.brand": "8-Damen- und 8-Türme Solver", + "nav.problem": "Problem", + "nav.features": "Features", + "nav.journey": "Ablauf", + "nav.login": "Login", + "nav.signup": "Create Account", + "hero.eyebrow": "Interaktives Lernen mit Strategie, Logik und Backtracking", + "hero.title": "Entdecke das Acht-Damen- und Acht-Türme-Problem.", + "hero.text": "Diese App macht aus einem klassischen Informatik- und Mathematikproblem ein interaktives Erlebnis: Setze Damen oder Türme, verfolge den Solver Schritt fuer Schritt, miss deine Zeit und speichere clevere Zwischenstände.", + "hero.primaryCta": "Jetzt starten", + "hero.secondaryCta": "Bereits Account? Login", + "hero.fact1Title": "Skalierbar", + "hero.fact1Text": "Brettgrösse flexibel anpassen", + "hero.fact2Title": "Schritt für Schritt", + "hero.fact2Text": "Solver mit Animation", + "hero.fact3Title": "Save-Points", + "hero.fact3Text": "Zeit und Fortschritt behalten", + "hero.solverLabel": "Solver", + "hero.solverValue": "Backtracking live", + "hero.challengeLabel": "Challenge", + "hero.challengeValue": "Zeit und Schritte messen", + "problem.eyebrow": "Warum dieses Problem spannend ist", + "problem.title": "Die Acht-Damen- und Acht-Türme-Probleme sind mehr als Schachrätsel.", + "problem.text": "Beim Acht-Damen-Problem suchst du eine Anordnung von acht Damen auf einem Schachbrett, sodass sich keine zwei Damen schlagen. Beim Acht-Tuerme-Problem gilt dieselbe Idee für Türme, nur mit einfacheren Konfliktregeln. Beide Aufgaben zeigen, wie Mustererkennung, systematische Suche und Backtracking funktionieren.

Gerade beim Damenproblem wird schnell sichtbar, warum Strategie wichtig ist: Es gibt unzählige moegliche Platzierungen, aber nur sehr wenige wirklich gültige Loesungen. Genau das macht diese App spannend, weil man nicht nur das Ergebnis sieht, sondern auch den Weg dorthin.", + "features.eyebrow": "Was die Applikation bietet", + "features.title": "Von der Idee bis zur gelösten Stellung.", + "features.card1Title": "Interaktives Brett", + "features.card1Text": "Setze Damen oder Türme direkt auf dem Brett und prüfe, ob deine Anordnung konfliktfrei ist.", + "features.card2Title": "Solver mit Animation", + "features.card2Text": "Beobachte den Loesungsweg Schritt fuer Schritt und verstehe, wie Backtracking arbeitet.", + "features.card3Title": "Zeit und Schritte", + "features.card3Text": "Miss deinen Fortschritt während des Spiels und vergleiche unterschiedliche Versuche.", + "features.card4Title": "Save-Points", + "features.card4Text": "Speichere Zwischenstände mit Notizen, Zeiten und Schrittzahl fuer späteres Weiterarbeiten.", + "features.card5Title": "Skalierbare Bretter", + "features.card5Text": "Passe die Brettgrösse an und erkunde die Probleme nicht nur auf dem klassischen 8x8-Brett.", + "journey.eyebrow": "So funktioniert dein Einstieg", + "journey.title": "In wenigen Schritten vom ersten Besuch zur eigenen Lösung.", + "journey.step1Title": "Account erstellen", + "journey.step1Text": "Registriere dich und erhalte Zugang zu Dashboard, Save-Points und deinem Fortschritt.", + "journey.step2Title": "Spielmodus wählen", + "journey.step2Text": "Arbeite mit Damen oder Türmen und passe die Brettgrösse deinem Ziel an.", + "journey.step3Title": "Loesen oder analysieren", + "journey.step3Text": "Teste eigene Ideen oder lass dir die Lösung visuell vom Solver lösen lassen, dabei wird 1:1 gezeigt wie der Solver vorgeht.", + "cta.eyebrow": "Bereit für den ersten Versuch?", + "cta.title": "Mach aus einem bekannten Problem ein sichtbares Lernerlebnis.", + "cta.text": "Starte mit einem Account, speichere deine Zwischenstände und finde heraus, wie schnell du eine konfliktfreie Lösung erreichst.", + "cta.primaryCta": "Create Account", + "cta.secondaryCta": "Login" + }, + en: { + "meta.title": "8 Queens and 8 Rooks Solver", + "nav.brand": "8 Queens and 8 Rooks Solver", + "nav.problem": "Problem", + "nav.features": "Features", + "nav.journey": "Journey", + "nav.login": "Login", + "nav.signup": "Create Account", + "hero.eyebrow": "Interactive learning with strategy, logic, and backtracking", + "hero.title": "Discover the Eight Queens and Eight Rooks puzzle.", + "hero.text": "This app turns a classic computer science and mathematics problem into an interactive experience: place queens or rooks, follow the solver step by step, track your time, and save clever checkpoints.", + "hero.primaryCta": "Get Started", + "hero.secondaryCta": "Already have an account? Login", + "hero.fact1Title": "Scalable", + "hero.fact1Text": "Adjust the board size freely", + "hero.fact2Title": "Step by step", + "hero.fact2Text": "Animated solver walkthrough", + "hero.fact3Title": "Save points", + "hero.fact3Text": "Keep your time and progress", + "hero.solverLabel": "Solver", + "hero.solverValue": "Backtracking in motion", + "hero.challengeLabel": "Challenge", + "hero.challengeValue": "Measure time and steps", + "problem.eyebrow": "Why this puzzle matters", + "problem.title": "The Eight Queens and Eight Rooks problems are more than chess puzzles.", + "problem.text": "In the Eight Queens problem, you search for a way to place eight queens on a chessboard so that no two queens can attack each other. The Eight Rooks problem follows the same idea for rooks, but with simpler conflict rules. Both tasks show how pattern recognition, systematic search, and backtracking work.

The queens puzzle quickly makes it obvious why strategy matters: there are countless possible placements, but only a few truly valid solutions. That is what makes this app interesting, because you do not just see the result, you also see the path to it.", + "features.eyebrow": "What the application offers", + "features.title": "From the first idea to a solved position.", + "features.card1Title": "Interactive board", + "features.card1Text": "Place queens or rooks directly on the board and check whether your setup is conflict-free.", + "features.card2Title": "Animated solver", + "features.card2Text": "Watch the solving path step by step and understand how backtracking works.", + "features.card3Title": "Time and steps", + "features.card3Text": "Track your progress during play and compare different attempts.", + "features.card4Title": "Save points", + "features.card4Text": "Store checkpoints with notes, time, and step count so you can continue later.", + "features.card5Title": "Scalable boards", + "features.card5Text": "Adjust the board size and explore the puzzle beyond the classic 8x8 board.", + "journey.eyebrow": "How your first session works", + "journey.title": "Reach your own solution in just a few steps.", + "journey.step1Title": "Create an account", + "journey.step1Text": "Sign up and unlock the dashboard, save points, and your tracked progress.", + "journey.step2Title": "Choose a game mode", + "journey.step2Text": "Work with queens or rooks and tune the board size to your goal.", + "journey.step3Title": "Solve or analyze", + "journey.step3Text": "Test your own ideas or let the solver show the solution visually, with a 1:1 view of each solving step.", + "cta.eyebrow": "Ready for your first attempt?", + "cta.title": "Turn a famous puzzle into a visible learning experience.", + "cta.text": "Start with an account, save your checkpoints, and find out how quickly you can reach a conflict-free solution.", + "cta.primaryCta": "Create Account", + "cta.secondaryCta": "Login" + } +}; + +const queenPatterns = [ + [0, 4, 7, 5, 2, 6, 1, 3], + [1, 3, 5, 7, 2, 0, 6, 4], + [2, 5, 7, 0, 3, 6, 4, 1] +]; + +let currentPatternIndex = 0; + +function normalizeLanguage(language) { + const shortLanguage = String(language || "").trim().slice(0, 2).toLowerCase(); + return supportedLanguages.includes(shortLanguage) ? shortLanguage : "de"; +} + +function getStoredLanguage() { + try { + return window.localStorage.getItem("preferredLanguage"); + } catch (error) { + return null; + } +} + +function storeLanguage(language) { + try { + window.localStorage.setItem("preferredLanguage", language); + } catch (error) { + return; + } +} + +function detectBrowserLanguage() { + const browserLanguage = navigator.language || navigator.userLanguage || initialLanguage; + return normalizeLanguage(browserLanguage); +} + +function setLanguage(language) { + const resolvedLanguage = normalizeLanguage(language); + const dictionary = translations[resolvedLanguage]; + const i18nElements = document.querySelectorAll("[data-i18n]"); + + document.documentElement.lang = resolvedLanguage; + document.body.dataset.language = resolvedLanguage; + + i18nElements.forEach((element) => { + const key = element.dataset.i18n; + const translatedText = dictionary[key]; + + if (!translatedText) { + return; + } + + if (element.tagName === "TITLE" || translatedText.includes(" { + const row = Number(cell.dataset.row); + const col = Number(cell.dataset.col); + const hasQueen = pattern[row] === col; + + cell.classList.toggle("queen", hasQueen); + cell.classList.toggle("highlight", hasQueen); + }); +} + +function rotatePattern() { + currentPatternIndex = (currentPatternIndex + 1) % queenPatterns.length; + applyPattern(queenPatterns[currentPatternIndex]); +} + +if (langSwitch) { + langSwitch.addEventListener("click", () => { + const nextLanguage = document.documentElement.lang === "de" ? "en" : "de"; + setLanguage(nextLanguage); + }); +} + +createHeroBoard(); +applyPattern(queenPatterns[currentPatternIndex]); +setLanguage(getPreferredLanguage()); + +if (heroBoard) { + window.setInterval(rotatePattern, 2200); +} diff --git a/app/templates/index.html b/app/templates/index.html index f364808..f90190e 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -1,66 +1,193 @@ - + - {% block title %}Solver App{% endblock %} - - - - - {% block head_extra %}{% endblock %} + 8-Damen- und 8-Tuerme Solver + +
+ + â™› + 8-Damen- und 8-Tuerme Solver + - - - -
- {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} -
- {% for category, message in messages %} -
- {{ message }} -
- {% endfor %} -
- {% endif %} - {% endwith %} +
+
+
+

Interaktives Lernen mit Strategie, Logik und Backtracking +

+

Entdecke das Acht-Damen- und Acht-TĂĽrme-Problem.

+

+ Diese App macht aus einem klassischen Informatik- und Mathematikproblem ein interaktives Erlebnis: + Setze Damen oder Türme, verfolge den Solver Schritt für Schritt, miss deine Zeit und speichere + clevere Zwischenstände. +

- - {% block content %}{% endblock %} + + +
+
+ Skalierbar + Brettgrösse flexibel anpassen +
+
+ Schritt fĂĽr Schritt + Solver mit Animation +
+
+ Save-Points + Zeit und Fortschritt behalten +
+
+
+ +
+ +
+

Solver

+

Backtracking live

+
+
+

Challenge

+

Zeit und Schritte messen

+
+
+
+ +
+
+

Warum dieses Problem spannend ist

+

Die Acht-Damen- und Acht-Tuerme-Probleme sind mehr als Schachrätsel.

+

+ Beim Acht-Damen-Problem suchst du eine Anordnung von acht Damen auf einem Schachbrett, sodass sich + keine zwei Damen schlagen. Beim Acht-TĂĽrme-Problem gilt dieselbe Idee fĂĽr TĂĽrme, nur mit + einfacheren Konfliktregeln. Beide Aufgaben zeigen, wie Mustererkennung, systematische Suche und + Backtracking funktionieren. +

+ Gerade beim Damenproblem wird schnell sichtbar, warum Strategie wichtig ist: Es gibt unzählige + mögliche Platzierungen, aber nur sehr wenige wirklich gültige Lösungen. Genau das macht diese + App spannend, weil man nicht nur das Ergebnis sieht, sondern auch den Weg dorthin. +

+
+
+ +
+
+

Was die Applikation bietet

+

Von der Idee bis zur gelösten Stellung.

+
+ +
+
+ ♟ +

Interaktives Brett

+

Setze Damen oder Tuerme direkt auf dem Brett und prĂĽfe, ob deine + Anordnung konfliktfrei ist.

+
+ +
+ âš™ +

Solver mit Animation

+

Beobachte den Lösungsweg Schritt für Schritt und verstehe, wie + Backtracking arbeitet.

+
+ +
+ ⏱ +

Zeit und Schritte

+

Miss deinen Fortschritt während des Spiels und vergleiche + unterschiedliche Versuche.

+
+ +
+ đź’ľ +

Save-Points

+

Speichere Zwischenstaende mit Notizen, Zeiten und Schrittzahl für + späteres Weiterarbeiten.

+
+ +
+ â–¦ +

Skalierbare Bretter

+

Passe die Brettgrösse an und erkunde die Probleme nicht nur auf + dem klassischen 8x8-Brett.

+
+
+
+ +
+
+

So funktioniert dein Einstieg

+

In wenigen Schritten vom ersten Besuch zur eigenen Lösung.

+
+ +
+
+ 01 +

Account erstellen

+

Registriere dich und erhalte Zugang zu Dashboard, Save-Points und + deinem Fortschritt.

+
+ +
+ 02 +

Spielmodus wählen

+

Arbeite mit Damen oder Türmen und passe die Brettgrösse deinem Ziel + an.

+
+ +
+ 03 +

Lösen oder analysieren

+

Teste eigene Ideen oder lass dir die Lösung visuell vom Solver + lösen lassen, dabei wird 1:1 + gezeigt wie der Solver vorgeht.

+
+
+
+ +
+
+

Bereit fĂĽr den ersten Versuch?

+

Mach aus einem bekannten Problem ein sichtbares Lernerlebnis.

+

+ Starte mit einem Account, speichere deine Zwischenstände und finde heraus, wie schnell du eine + konfliktfreie Lösung erreichst. +

+ + +
+
- -
-
-

© {{ 2026 }} SolverApp

-
-
- - - - {% block scripts %}{% endblock %} + + \ No newline at end of file diff --git a/app/templates/login.html b/app/templates/login.html index 3afe9fa..47d4de4 100644 --- a/app/templates/login.html +++ b/app/templates/login.html @@ -9,8 +9,13 @@ -