diff --git a/api/session.php b/api/session.php
index 9aecee2..fac5235 100644
--- a/api/session.php
+++ b/api/session.php
@@ -1,5 +1,22 @@
$uid,
- 'email' => $email,
- 'token' => $token
- ];
- echo json_encode(['status' => 'success', 'message' => 'Logged in']);
- } else {
+ $uid = trim($_POST['uid'] ?? '');
+ $email = trim($_POST['email'] ?? '');
+ $token = trim($_POST['token'] ?? '');
+
+ // Validate required fields – reject obviously malformed requests early
+ if ($uid === '' || $email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
http_response_code(400);
- echo json_encode(['status' => 'error', 'message' => 'Missing UID']);
+ echo json_encode(['status' => 'error', 'message' => 'Invalid or missing uid/email']);
+ exit;
}
+
+ $_SESSION['fc_user'] = [
+ 'uid' => $uid,
+ 'email' => $email,
+ 'token' => $token, // Stored for potential future server-side verification
+ ];
+ echo json_encode(['status' => 'success', 'message' => 'Logged in']);
+
} elseif ($action === 'logout') {
$_SESSION = [];
- if (ini_get("session.use_cookies")) {
+ if (ini_get('session.use_cookies')) {
$params = session_get_cookie_params();
- setcookie(session_name(), '', time() - 42000,
- $params["path"], $params["domain"],
- $params["secure"], $params["httponly"]
+ setcookie(
+ session_name(), '', time() - 42000,
+ $params['path'], $params['domain'],
+ $params['secure'], $params['httponly']
);
}
session_destroy();
echo json_encode(['status' => 'success', 'message' => 'Logged out']);
+
} else {
http_response_code(400);
echo json_encode(['status' => 'error', 'message' => 'Invalid action']);
diff --git a/assets/style.css b/assets/style.css
index b6172b3..facdb34 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -1741,6 +1741,284 @@ body.nav-open { overflow: hidden; }
║ PHASE 2: AUTHENTICATION, FAVORITES & NEWSLETTER STYLES ║
╚══════════════════════════════════════════════════════════════╝ */
+/* ── Auth Page Layout ── */
+.auth-page {
+ padding: clamp(6rem, 10vh, 10rem) var(--grid-margin) clamp(4rem, 8vh, 6rem);
+ min-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+/* ── Auth Card ── */
+.auth-card {
+ width: 100%;
+ max-width: 500px;
+ background: var(--surface-2);
+ border: 1px solid var(--stroke);
+ box-shadow: var(--shadow);
+ border-radius: var(--radius);
+ padding: clamp(2rem, 5vw, 3.5rem);
+ position: relative;
+ overflow: hidden;
+ transition: transform 0.4s var(--ease-out-expo), opacity 0.4s ease;
+}
+
+/* ── Auth Tabs (Login / Register toggle) ── */
+.auth-tabs {
+ display: flex;
+ justify-content: center;
+ gap: 2rem;
+ margin-bottom: 2.5rem;
+ border-bottom: 1px solid var(--stroke);
+ padding-bottom: 0.8rem;
+}
+.auth-tab {
+ font-family: var(--font-serif-display);
+ font-size: 1.5rem;
+ color: var(--color-text-muted);
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ position: relative;
+ transition: color 0.3s ease;
+}
+.auth-tab.active {
+ color: var(--ink);
+}
+.auth-tab::after {
+ content: '';
+ position: absolute;
+ bottom: -0.9rem;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ background-color: var(--color-accent-gold);
+ transform: scaleX(0);
+ transform-origin: center;
+ transition: transform 0.4s var(--ease-out-expo);
+}
+.auth-tab.active::after {
+ transform: scaleX(1);
+}
+
+/* ── Auth Forms ── */
+.auth-form {
+ display: none;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+.auth-form.active {
+ display: flex;
+ animation: authFadeIn 0.5s var(--ease-out-expo) both;
+}
+@keyframes authFadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ── Auth Form Fields ── */
+.auth-field {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+.auth-field label {
+ font-family: var(--font-sans-clean);
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-weight: 700;
+ color: var(--muted);
+}
+.auth-field input,
+.auth-field select {
+ padding: 1rem 1.25rem;
+ border-radius: 10px;
+ border: 1px solid var(--stroke);
+ background: var(--surface);
+ color: var(--ink);
+ font-family: var(--font-sans-clean);
+ font-size: 0.95rem;
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
+ outline: none;
+}
+.auth-field input:focus,
+.auth-field select:focus {
+ border-color: var(--color-accent-gold);
+ box-shadow: 0 0 0 3px var(--accent-soft);
+}
+
+/* ── Auth Submit Button Row ── */
+.auth-btn-row {
+ margin-top: 1rem;
+}
+.auth-btn-row button {
+ width: 100%;
+ justify-content: center;
+}
+
+/* ── Auth Form Switch Link ── */
+.auth-switch-text {
+ text-align: center;
+ font-size: 0.9rem;
+ color: var(--muted);
+ margin-top: 1.5rem;
+}
+.auth-switch-text button {
+ background: none;
+ border: none;
+ color: var(--ink);
+ font-weight: 700;
+ cursor: pointer;
+}
+
+/* ── Auth Alert (Error Message) ── */
+.auth-alert {
+ background: #fdf2f2;
+ border: 1px solid #fbd5d5;
+ color: #9b1c1c;
+ padding: 1rem;
+ border-radius: 8px;
+ font-size: 0.9rem;
+ margin-bottom: 1.5rem;
+ display: none;
+ animation: authFadeIn 0.3s ease;
+}
+[data-theme="dark"] .auth-alert {
+ background: #2b1515;
+ border-color: #5a1818;
+ color: #ff9b9b;
+}
+
+/* ── Loading Spinner ── */
+.spinner {
+ width: 20px;
+ height: 20px;
+ border: 2px solid rgba(255, 255, 255, 0.3);
+ border-radius: 50%;
+ border-top-color: #fff;
+ animation: spin 0.8s linear infinite;
+ display: none;
+}
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+.btn-reveal:disabled {
+ opacity: 0.7;
+ cursor: not-allowed;
+}
+.btn-reveal:disabled .spinner {
+ display: inline-block;
+}
+.btn-reveal:disabled .btn-reveal-text {
+ display: none;
+}
+
+/* ── Profile & Dashboard Shell ── */
+.profile-shell {
+ width: 100%;
+ max-width: 1100px;
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 3rem;
+}
+@media (min-width: 850px) {
+ .profile-shell {
+ grid-template-columns: 320px 1fr;
+ }
+}
+
+/* ── Profile Sidebar ── */
+.profile-sidebar {
+ background: var(--surface-2);
+ border: 1px solid var(--stroke);
+ border-radius: var(--radius);
+ padding: 2.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+ height: fit-content;
+ box-shadow: var(--shadow);
+}
+.profile-avatar {
+ width: 80px;
+ height: 80px;
+ border-radius: 50%;
+ background: var(--accent-soft);
+ color: var(--color-accent-gold);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-family: var(--font-serif-display);
+ font-size: 2.5rem;
+ border: 1px solid rgba(195, 166, 119, 0.3);
+}
+.profile-meta h2 {
+ font-size: 1.75rem;
+ margin-bottom: 0.25rem;
+ font-family: var(--font-serif-display);
+}
+.profile-meta p {
+ font-size: 0.9rem;
+ color: var(--muted);
+}
+.profile-details {
+ border-top: 1px solid var(--stroke);
+ padding-top: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1.2rem;
+}
+.profile-stat-box {
+ display: flex;
+ flex-direction: column;
+ gap: 0.3rem;
+}
+.profile-stat-label {
+ font-size: 0.72rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ font-weight: 700;
+ color: var(--muted);
+}
+.profile-stat-val {
+ font-size: 1rem;
+ font-weight: 600;
+ color: var(--ink);
+}
+
+/* ── Profile Main Content Area ── */
+.profile-main {
+ display: flex;
+ flex-direction: column;
+ gap: 2rem;
+}
+.profile-favorites-title {
+ font-family: var(--font-serif-display);
+ font-size: 2rem;
+ border-bottom: 1px solid var(--stroke);
+ padding-bottom: 0.8rem;
+ margin-bottom: 1rem;
+}
+
+/* ── Favorites Grid ── */
+.favorites-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ gap: var(--grid-gap);
+}
+.favorites-empty {
+ text-align: center;
+ padding: 4rem 2rem;
+ background: var(--surface-2);
+ border: 1px dashed var(--stroke);
+ border-radius: var(--radius);
+ color: var(--muted);
+}
+
/* ── Premium Heart Buttons ── */
.card__image {
position: relative;
diff --git a/login.php b/login.php
index 5fddef4..e979cd5 100644
--- a/login.php
+++ b/login.php
@@ -1,4 +1,4 @@
-
-
@@ -536,8 +245,14 @@ function handleLogin(e) {
.catch(error => {
btn.disabled = false;
let errMsg = error.message;
- if (error.code === 'auth/wrong-password' || error.code === 'auth/user-not-found') {
- errMsg = "Invalid email or password.";
+ // Firebase v10 compat SDK consolidates wrong-password + user-not-found
+ // into auth/invalid-credential. Handle all three for backwards compatibility.
+ if (
+ error.code === 'auth/invalid-credential' ||
+ error.code === 'auth/wrong-password' ||
+ error.code === 'auth/user-not-found'
+ ) {
+ errMsg = 'Invalid email or password.';
}
showAlert(errMsg);
});
diff --git a/partials/head.php b/partials/head.php
index 68f9746..0202167 100644
--- a/partials/head.php
+++ b/partials/head.php
@@ -70,7 +70,9 @@ $description = $description ?? 'Seasonal recipes, tested tips, and approachable
-
+