Made consistent design for landing page and login. Landing page has all the text and design.

Added language support for english and german (swappable)
This commit is contained in:
2026-04-05 00:42:25 +02:00
parent 59b48683a8
commit 19a6bbd392
10 changed files with 1347 additions and 348 deletions
+2 -1
View File
@@ -226,7 +226,8 @@ def build_save_point_query():
@main_bp.route("/") @main_bp.route("/")
def home(): 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") @main_bp.route("/dashboard")
+230 -192
View File
@@ -1,10 +1,58 @@
/* ==================== Base ==================== */
body { body {
margin: 0; margin: 0;
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
background: #f5f7fa; 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 { .navbar {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -47,49 +95,103 @@ body {
text-decoration: underline; text-decoration: underline;
} }
/* 📦 Layout */
.container { /* ==================== Shared Buttons ==================== */
max-width: 900px;
margin: 30px auto; .btn {
padding: 10px; padding: 10px 15px;
margin: 5px;
border: none;
border-radius: 8px;
background: #3b82f6;
color: white;
cursor: pointer;
} }
/* 📇 Cards */ .btn:hover {
.card { background: #2563eb;
background: white;
padding: 20px;
margin-bottom: 25px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
} }
.flash-stack { .btn:disabled,
display: grid; .btn:disabled:hover {
gap: 12px; background: #94a3b8;
margin-bottom: 18px; 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; margin: 0;
padding: 12px 14px;
border-radius: 10px;
} }
.flash-message.success { .inline-actions .btn {
color: #166534; margin: 0;
background: #dcfce7;
} }
.flash-message.info {
color: #1d4ed8; /* ==================== Mode Switch And Main Actions ==================== */
background: #dbeafe;
.mode-switch {
margin-bottom: 15px;
} }
.flash-message.error { .mode-switch .btn {
color: #b91c1c; margin: 5px;
background: #fee2e2;
} }
.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 { .game-card {
text-align: center; text-align: center;
} }
@@ -121,6 +223,11 @@ body {
background: #bfdbfe; background: #bfdbfe;
} }
.board-size-info {
margin: 0 0 10px;
color: #475569;
}
.game-stats { .game-stats {
display: grid; display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -162,12 +269,14 @@ body {
font-weight: bold; font-weight: bold;
} }
/* ==================== Chessboard ==================== */
.board { .board {
overflow-x: auto; overflow-x: auto;
padding-bottom: 8px; padding-bottom: 8px;
} }
/* ♟️ Board */
.board-grid { .board-grid {
--label-size: 30px; --label-size: 30px;
--cell-size: 60px; --cell-size: 60px;
@@ -209,21 +318,6 @@ body {
transform: scale(1.05); 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 { .white {
background: #e2e8f0; background: #e2e8f0;
} }
@@ -237,74 +331,8 @@ body {
background: #ef4444 !important; background: #ef4444 !important;
} }
/* 🎛️ Buttons */
.controls {
margin-top: 15px;
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 10px;
}
.btn { /* ==================== Solver And Animation Details ==================== */
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-panel { .solver-panel {
margin-top: 12px; margin-top: 12px;
@@ -350,11 +378,6 @@ body {
margin-top: 14px; margin-top: 14px;
} }
.solver-controls .btn,
.controls .btn {
margin: 0;
}
.solver-speed { .solver-speed {
display: grid; display: grid;
grid-template-columns: auto 1fr auto; grid-template-columns: auto 1fr auto;
@@ -379,11 +402,91 @@ body {
font-variant-numeric: tabular-nums; font-variant-numeric: tabular-nums;
} }
.board-size-info { .cell.solver-place {
margin: 0 0 10px; box-shadow: inset 0 0 0 4px #22c55e;
color: #475569; 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 { .sidebar {
position: fixed; position: fixed;
top: 0; top: 0;
@@ -525,78 +628,8 @@ body {
margin: 0; margin: 0;
} }
.inline-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 10px;
}
.inline-actions form { /* ==================== Backdrops And Modal ==================== */
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;
}
.sidebar-backdrop { .sidebar-backdrop {
position: fixed; position: fixed;
@@ -680,6 +713,9 @@ body {
line-height: 1.6; line-height: 1.6;
} }
/* ==================== Animations ==================== */
@keyframes solverPulse { @keyframes solverPulse {
0% { 0% {
transform: scale(0.97); transform: scale(0.97);
@@ -694,6 +730,9 @@ body {
} }
} }
/* ==================== Responsive ==================== */
@media (max-width: 720px) { @media (max-width: 720px) {
.container { .container {
padding: 12px; padding: 12px;
@@ -703,7 +742,6 @@ body {
padding: 15px 18px; padding: 15px 18px;
} }
.nav-logout-form { .nav-logout-form {
margin-left: 0; margin-left: 0;
} }
+514
View File
@@ -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%;
}
}
+159 -98
View File
@@ -1,17 +1,21 @@
:root { :root {
--nav-bg-color: #172033; --bg: #f4efe7;
--panel-bg-color: rgba(255, 255, 255, 0.18); --bg-accent: #efe3cd;
--panel-border-color: rgba(255, 255, 255, 0.24); --surface: rgba(255, 250, 242, 0.88);
--text-color: #172033; --surface-strong: #fffaf2;
--muted-text-color: #475569; --surface-soft: rgba(255, 247, 235, 0.72);
--success-color: #15803d; --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; --info-color: #1d4ed8;
--error-color: #b91c1c; --error-color: #991b1b;
}
@font-face {
font-family: "Geist Sans";
src: url("https://assets.codepen.io/605876/GeistVF.ttf") format("truetype");
} }
*, *,
@@ -24,74 +28,103 @@ body {
margin: 0; margin: 0;
min-height: 100vh; min-height: 100vh;
color: var(--text-color); color: var(--text-color);
font-family: "Geist Sans", system-ui, sans-serif; font-family: Georgia, "Times New Roman", serif;
background: background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.45), transparent 32%), radial-gradient(circle at top left, rgba(255, 255, 255, 0.9), transparent 24%),
linear-gradient(135deg, #d2ecff 0%, #b8c6db 48%, #7f8ea3 100%); 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%; width: 100%;
min-height: 68px; margin: 0;
padding: 15px 30px; padding: 18px max(16px, calc((100vw - 1200px) / 2)) 16px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 20px; gap: 18px;
background: var(--nav-bg-color); 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; margin: 0;
padding: 0; padding: 0;
list-style: none; list-style: none;
display: flex; display: flex;
align-items: center;
flex-wrap: wrap; flex-wrap: wrap;
gap: 20px; gap: 12px;
color: #ecf4fb;
} }
nav a { .nav-links a,
color: white; .nav-button {
text-decoration: none; display: inline-flex;
font-size: 16px; 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 { .nav-links a:hover,
color: #93c5fd; .nav-button:hover {
transform: translateY(-2px);
border-color: rgba(231, 191, 119, 0.45);
color: #fff7eb;
} }
.nav-form { .nav-form {
margin: 0; 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 { .auth-shell {
min-height: calc(100vh - 68px); min-height: calc(100vh - 78px);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 32px 16px; padding: 38px 16px 48px;
} }
.settings-shell { .settings-shell {
@@ -99,18 +132,17 @@ nav a:hover {
} }
.auth-panel { .auth-panel {
width: min(420px, 100%); width: min(460px, 100%);
padding: 34px; padding: 34px;
border-radius: 20px; border-radius: 12px;
background: var(--panel-bg-color); background: linear-gradient(135deg, rgba(255, 250, 242, 0.94), rgba(245, 230, 205, 0.8));
border: 1px solid var(--panel-border-color); border: 1px solid var(--line);
backdrop-filter: blur(16px); box-shadow: var(--shadow);
box-shadow: 0 18px 45px rgba(15, 23, 42, 0.18);
animation: fadeIn 0.8s ease; animation: fadeIn 0.8s ease;
} }
.settings-panel { .settings-panel {
width: min(900px, 100%); width: min(980px, 100%);
} }
.settings-grid { .settings-grid {
@@ -134,12 +166,31 @@ nav a:hover {
h1, h1,
h2 { h2 {
margin-top: 0; 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 { .muted {
margin: 0 0 20px; margin: 0 0 20px;
color: var(--muted-text-color); font-size: 17px;
line-height: 1.5;
} }
form { form {
@@ -148,65 +199,56 @@ form {
gap: 8px; gap: 8px;
margin-top: 18px; margin-top: 18px;
padding: 24px; padding: 24px;
border-radius: 16px; border-radius: 10px;
background: rgba(255, 255, 255, 0.2); background: var(--surface-soft);
border: 1px solid rgba(255, 255, 255, 0.22); border: 1px solid rgba(31, 49, 64, 0.1);
} }
label { label {
margin-top: 8px; margin-top: 8px;
font-size: 14px; font-size: 14px;
font-weight: 600; font-weight: 700;
color: var(--navy);
letter-spacing: 0.03em;
} }
input { input {
width: 100%; width: 100%;
padding: 12px 14px; padding: 12px 14px;
border: 1px solid rgba(71, 85, 105, 0.25); border: 1px solid rgba(31, 49, 64, 0.16);
border-radius: 10px; border-radius: 0;
font: inherit; font: inherit;
background: rgba(255, 255, 255, 0.86); background: rgba(255, 255, 255, 0.86);
color: var(--text-color); color: var(--text-color);
} }
input:focus { input:focus {
outline: 2px solid rgba(59, 130, 246, 0.35); outline: 2px solid rgba(198, 139, 44, 0.28);
border-color: rgba(59, 130, 246, 0.5); border-color: rgba(198, 139, 44, 0.55);
} }
.field-hint { .field-hint {
margin: 0; margin: 0;
font-size: 13px; 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 { button {
margin-top: 14px; margin-top: 14px;
padding: 12px 16px; min-height: 50px;
padding: 12px 18px;
border: none; border: none;
border-radius: 10px; border-radius: 0;
background: linear-gradient(135deg, #eff6ff, #dbeafe); background: linear-gradient(135deg, var(--gold), var(--gold-soft));
color: #172033; color: #2d2418;
cursor: pointer; cursor: pointer;
font-size: 16px; font-size: 16px;
font-weight: 600; font-weight: 700;
transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease; transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
} }
button:hover { button:hover {
transform: translateY(-2px); 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 { button:active {
@@ -220,18 +262,20 @@ button:active {
} }
.button-row button { .button-row button {
flex: 1 1 160px; flex: 1 1 180px;
} }
.secondary-button { .secondary-button {
background: linear-gradient(135deg, #e2e8f0, #cbd5e1); background: linear-gradient(135deg, var(--navy), var(--navy-soft));
color: #f9fbfd;
} }
.message { .message {
margin: 0 0 14px; margin: 0 0 14px;
padding: 12px 14px; padding: 12px 14px;
border-radius: 10px; border-radius: 8px;
font-size: 14px; font-size: 14px;
font-family: "Trebuchet MS", "Segoe UI", sans-serif;
} }
.message.error { .message.error {
@@ -254,9 +298,9 @@ button:active {
justify-content: center; justify-content: center;
margin: 8px 0 6px; margin: 8px 0 6px;
padding: 18px; padding: 18px;
border-radius: 16px; border-radius: 10px;
background: rgba(255, 255, 255, 0.38); background: rgba(255, 250, 242, 0.72);
border: 1px solid rgba(255, 255, 255, 0.24); border: 1px solid rgba(31, 49, 64, 0.12);
} }
.qr-code { .qr-code {
@@ -266,7 +310,7 @@ button:active {
min-width: 220px; min-width: 220px;
min-height: 220px; min-height: 220px;
padding: 10px; padding: 10px;
border-radius: 14px; border-radius: 8px;
background: #ffffff; background: #ffffff;
color: var(--muted-text-color); color: var(--muted-text-color);
text-align: center; text-align: center;
@@ -276,15 +320,32 @@ button:active {
display: block; display: block;
} }
@media (max-width: 640px) { @media (max-width: 760px) {
nav { .auth-nav {
padding: 16px 20px; position: static;
padding: 18px 16px;
border-left-width: 0;
border-right-width: 0;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
} }
.brand {
width: 100%;
font-size: 16px;
}
.nav-links {
width: 100%;
}
.nav-links a,
.nav-button {
padding: 10px 14px;
}
.auth-shell { .auth-shell {
padding: 20px 12px 28px; padding: 22px 12px 28px;
} }
.auth-panel { .auth-panel {
+238
View File
@@ -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.<br><br>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.<br><br>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("<br")) {
element.innerHTML = translatedText;
return;
}
element.textContent = translatedText;
});
if (langSwitch) {
langSwitch.textContent = resolvedLanguage.toUpperCase();
langSwitch.setAttribute("aria-label", resolvedLanguage === "de" ? "Switch language to English" : "Sprache auf Deutsch wechseln");
langSwitch.title = resolvedLanguage === "de" ? "Switch to English" : "Zu Deutsch wechseln";
}
storeLanguage(resolvedLanguage);
}
function getPreferredLanguage() {
return normalizeLanguage(getStoredLanguage() || detectBrowserLanguage() || initialLanguage);
}
function createHeroBoard() {
if (!heroBoard) {
return;
}
heroBoard.innerHTML = "";
for (let row = 0; row < HERO_BOARD_SIZE; row += 1) {
for (let col = 0; col < HERO_BOARD_SIZE; col += 1) {
const cell = document.createElement("div");
cell.className = `board-cell ${(row + col) % 2 === 0 ? "light" : "dark"}`;
cell.dataset.row = String(row);
cell.dataset.col = String(col);
heroBoard.appendChild(cell);
}
}
}
function applyPattern(pattern) {
if (!heroBoard) {
return;
}
const cells = heroBoard.querySelectorAll(".board-cell");
cells.forEach((cell) => {
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);
}
+176 -49
View File
@@ -1,66 +1,193 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="{{ initial_language }}">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Solver App{% endblock %}</title> <title data-i18n="meta.title">8-Damen- und 8-Tuerme Solver</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/index.css') }}">
<!-- CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
{% block head_extra %}{% endblock %}
</head> </head>
<body> <body>
<header class="landing-nav">
<a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span data-i18n="nav.brand">8-Damen- und 8-Tuerme Solver</span>
</a>
<!-- Navigation --> <nav class="landing-links">
<header class="navbar"> <a href="#problem" data-i18n="nav.problem">Problem</a>
<div class="container"> <a href="#features" data-i18n="nav.features">Features</a>
<h1 class="logo">SolverApp</h1> <a href="#journey" data-i18n="nav.journey">Ablauf</a>
<button class="lang-switch" id="langSwitch" type="button" aria-label="Sprache wechseln">DE</button>
<nav> <a class="nav-cta secondary" href="{{ url_for('login.login') }}" data-i18n="nav.login">Login</a>
<a href="/">Home</a> <a class="nav-cta" href="{{ url_for('login.register') }}" data-i18n="nav.signup">Create Account</a>
<a href="/dashboard">Solver</a> </nav>
{% if session.get('user_id') %}
<a href="/dashboard">Dashboard</a>
<a href="/logout">Logout</a>
{% else %}
<a href="{{ url_for('login.login') }}">Login</a>
{% endif %}
</nav>
</div>
</header> </header>
<!-- Flash messages --> <main>
<main class="container"> <section class="hero">
{% with messages = get_flashed_messages(with_categories=true) %} <div class="hero-copy">
{% if messages %} <p class="eyebrow" data-i18n="hero.eyebrow">Interaktives Lernen mit Strategie, Logik und Backtracking
<div class="flash-container"> </p>
{% for category, message in messages %} <h1 data-i18n="hero.title">Entdecke das Acht-Damen- und Acht-Türme-Problem.</h1>
<div class="flash {{ category }}"> <p class="hero-text" data-i18n="hero.text">
{{ message }} Diese App macht aus einem klassischen Informatik- und Mathematikproblem ein interaktives Erlebnis:
</div> Setze Damen oder Türme, verfolge den Solver Schritt für Schritt, miss deine Zeit und speichere
{% endfor %} clevere Zwischenstände.
</div> </p>
{% endif %}
{% endwith %}
<!-- Page Content --> <div class="hero-actions">
{% block content %}{% endblock %} <a class="hero-button primary" href="{{ url_for('login.register') }}"
data-i18n="hero.primaryCta">Jetzt starten</a>
<a class="hero-button secondary" href="{{ url_for('login.login') }}"
data-i18n="hero.secondaryCta">Bereits Account? Login</a>
</div>
<div class="hero-facts">
<article>
<strong data-i18n="hero.fact1Title">Skalierbar</strong>
<span data-i18n="hero.fact1Text">Brettgrösse flexibel anpassen</span>
</article>
<article>
<strong data-i18n="hero.fact2Title">Schritt für Schritt</strong>
<span data-i18n="hero.fact2Text">Solver mit Animation</span>
</article>
<article>
<strong data-i18n="hero.fact3Title">Save-Points</strong>
<span data-i18n="hero.fact3Text">Zeit und Fortschritt behalten</span>
</article>
</div>
</div>
<div class="hero-visual">
<div class="board-showcase" id="heroBoard" aria-hidden="true"></div>
<div class="floating-card solver-card">
<p class="floating-label" data-i18n="hero.solverLabel">Solver</p>
<p class="floating-value" data-i18n="hero.solverValue">Backtracking live</p>
</div>
<div class="floating-card timer-card">
<p class="floating-label" data-i18n="hero.challengeLabel">Challenge</p>
<p class="floating-value" data-i18n="hero.challengeValue">Zeit und Schritte messen</p>
</div>
</div>
</section>
<section class="info-band" id="problem">
<div class="info-band-inner">
<p class="eyebrow" data-i18n="problem.eyebrow">Warum dieses Problem spannend ist</p>
<h2 data-i18n="problem.title">Die Acht-Damen- und Acht-Tuerme-Probleme sind mehr als Schachrätsel.</h2>
<p data-i18n="problem.text">
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.
<br><br>
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.
</p>
</div>
</section>
<section class="feature-section" id="features">
<div class="section-heading">
<p class="eyebrow" data-i18n="features.eyebrow">Was die Applikation bietet</p>
<h2 data-i18n="features.title">Von der Idee bis zur gelösten Stellung.</h2>
</div>
<div class="feature-grid">
<article class="feature-card">
<span class="feature-icon">♟</span>
<h3 data-i18n="features.card1Title">Interaktives Brett</h3>
<p data-i18n="features.card1Text">Setze Damen oder Tuerme direkt auf dem Brett und prüfe, ob deine
Anordnung konfliktfrei ist.</p>
</article>
<article class="feature-card">
<span class="feature-icon">⚙</span>
<h3 data-i18n="features.card2Title">Solver mit Animation</h3>
<p data-i18n="features.card2Text">Beobachte den Lösungsweg Schritt für Schritt und verstehe, wie
Backtracking arbeitet.</p>
</article>
<article class="feature-card">
<span class="feature-icon">⏱</span>
<h3 data-i18n="features.card3Title">Zeit und Schritte</h3>
<p data-i18n="features.card3Text">Miss deinen Fortschritt während des Spiels und vergleiche
unterschiedliche Versuche.</p>
</article>
<article class="feature-card">
<span class="feature-icon">💾</span>
<h3 data-i18n="features.card4Title">Save-Points</h3>
<p data-i18n="features.card4Text">Speichere Zwischenstaende mit Notizen, Zeiten und Schrittzahl für
späteres Weiterarbeiten.</p>
</article>
<article class="feature-card">
<span class="feature-icon">▦</span>
<h3 data-i18n="features.card5Title">Skalierbare Bretter</h3>
<p data-i18n="features.card5Text">Passe die Brettgrösse an und erkunde die Probleme nicht nur auf
dem klassischen 8x8-Brett.</p>
</article>
</div>
</section>
<section class="journey-section" id="journey">
<div class="section-heading">
<p class="eyebrow" data-i18n="journey.eyebrow">So funktioniert dein Einstieg</p>
<h2 data-i18n="journey.title">In wenigen Schritten vom ersten Besuch zur eigenen Lösung.</h2>
</div>
<div class="journey-grid">
<article class="journey-step">
<span class="step-number">01</span>
<h3 data-i18n="journey.step1Title">Account erstellen</h3>
<p data-i18n="journey.step1Text">Registriere dich und erhalte Zugang zu Dashboard, Save-Points und
deinem Fortschritt.</p>
</article>
<article class="journey-step">
<span class="step-number">02</span>
<h3 data-i18n="journey.step2Title">Spielmodus wählen</h3>
<p data-i18n="journey.step2Text">Arbeite mit Damen oder Türmen und passe die Brettgrösse deinem Ziel
an.</p>
</article>
<article class="journey-step">
<span class="step-number">03</span>
<h3 data-i18n="journey.step3Title">Lösen oder analysieren</h3>
<p data-i18n="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.</p>
</article>
</div>
</section>
<section class="cta-section">
<div class="cta-panel">
<p class="eyebrow" data-i18n="cta.eyebrow">Bereit für den ersten Versuch?</p>
<h2 data-i18n="cta.title">Mach aus einem bekannten Problem ein sichtbares Lernerlebnis.</h2>
<p data-i18n="cta.text">
Starte mit einem Account, speichere deine Zwischenstände und finde heraus, wie schnell du eine
konfliktfreie Lösung erreichst.
</p>
<div class="hero-actions">
<a class="hero-button primary" href="{{ url_for('login.register') }}"
data-i18n="cta.primaryCta">Create Account</a>
<a class="hero-button secondary" href="{{ url_for('login.login') }}"
data-i18n="cta.secondaryCta">Login</a>
</div>
</div>
</section>
</main> </main>
<!-- Footer --> <script>
<footer class="footer"> window.LANDING_INITIAL_LANGUAGE = "{{ initial_language }}";
<div class="container"> </script>
<p>© {{ 2026 }} SolverApp</p> <script src="{{ url_for('static', filename='js/index.js') }}"></script>
</div>
</footer>
<!-- JS -->
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
{% block scripts %}{% endblock %}
</body> </body>
</html> </html>
+7 -2
View File
@@ -9,8 +9,13 @@
</head> </head>
<body> <body>
<nav> <nav class="auth-nav">
<ul> <a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span>8-Damen- und 8-Tuerme Solver</span>
</a>
<ul class="nav-links">
<li><a href="{{ url_for('login.login') }}">Login</a></li> <li><a href="{{ url_for('login.login') }}">Login</a></li>
<li><a href="{{ url_for('login.register') }}">Create account</a></li> <li><a href="{{ url_for('login.register') }}">Create account</a></li>
</ul> </ul>
+7 -2
View File
@@ -10,8 +10,13 @@
</head> </head>
<body> <body>
<nav> <nav class="auth-nav">
<ul> <a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span>8-Damen- und 8-Tuerme Solver</span>
</a>
<ul class="nav-links">
<li><a href="{{ url_for('main.dashboard') }}">Dashboard</a></li> <li><a href="{{ url_for('main.dashboard') }}">Dashboard</a></li>
<li><a href="{{ url_for('login.login_settings') }}">Login settings</a></li> <li><a href="{{ url_for('login.login_settings') }}">Login settings</a></li>
</ul> </ul>
+7 -2
View File
@@ -9,8 +9,13 @@
</head> </head>
<body> <body>
<nav> <nav class="auth-nav">
<ul> <a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span>8-Damen- und 8-Tuerme Solver</span>
</a>
<ul class="nav-links">
<li><a href="{{ url_for('login.login') }}">Login</a></li> <li><a href="{{ url_for('login.login') }}">Login</a></li>
<li><a href="{{ url_for('login.register') }}">Create account</a></li> <li><a href="{{ url_for('login.register') }}">Create account</a></li>
</ul> </ul>
+7 -2
View File
@@ -9,8 +9,13 @@
</head> </head>
<body> <body>
<nav> <nav class="auth-nav">
<ul> <a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span>8-Damen- und 8-Tuerme Solver</span>
</a>
<ul class="nav-links">
<li><a href="{{ url_for('login.login') }}">Login</a></li> <li><a href="{{ url_for('login.login') }}">Login</a></li>
<li><a href="{{ url_for('login.register') }}">Create account</a></li> <li><a href="{{ url_for('login.register') }}">Create account</a></li>
</ul> </ul>