From bdc38bc84392e9e5cc713e6e6197c551abc1563d Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 16:58:12 +0800 Subject: [PATCH 01/23] feat: hook up `ColorScheme` for light mode and dark mode map styles --- apps/web/app/page.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index fdef184..6b1734d 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -4,6 +4,7 @@ import { APIProvider, Map as GoogleMap, AdvancedMarker, + ColorScheme, } from "@vis.gl/react-google-maps"; import { HeadsUpDisplay } from "@/components/HeadsUpDisplay"; import { NeonPin } from "@/components/NeonPin"; @@ -54,6 +55,8 @@ export default function Home() { ); }; + const [theme, setTheme] = useState<"dark" | "light">("light"); + const [pendingPinCoords, setPendingPinCoords] = useState<{ lat: number; lng: number; @@ -103,6 +106,7 @@ export default function Home() { defaultZoom={19} minZoom={17} mapId={process.env.NEXT_PUBLIC_MAP_ID || "71238adec955b8c6d66f595a"} + colorScheme={theme === "dark" ? ColorScheme.DARK : ColorScheme.LIGHT} gestureHandling={"greedy"} disableDefaultUI={true} onClick={(e) => { From 8eae5eb3e3683429b3fb86be8a2b32bcfb3aeb4d Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 17:05:16 +0800 Subject: [PATCH 02/23] feat: make theme toggle button functional --- apps/web/app/page.tsx | 4 +++- apps/web/components/TopBar.tsx | 38 ++++++++++++++++++++++++---------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 6b1734d..92548bc 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -220,8 +220,10 @@ export default function Home() { onSearchChange={setSearchQuery} activeRoutes={activeRoutes} onToggleRoute={handleToggleRoute} - activeZoneCategories={activeZoneCategories} + activeZoneCategories={activeZoneCategories} onToggleZoneCategory={handleToggleZoneCategory} + theme={theme} + onToggleTheme={() => setTheme(prev => prev === "dark" ? "light" : "dark")} /> {/* TARGETING CROSSHAIR (Only visible when armed) */} diff --git a/apps/web/components/TopBar.tsx b/apps/web/components/TopBar.tsx index 9fbb527..bb97a86 100644 --- a/apps/web/components/TopBar.tsx +++ b/apps/web/components/TopBar.tsx @@ -18,6 +18,8 @@ interface TopBarProps { onToggleRoute?: (routeId: string) => void; activeZoneCategories?: string[]; onToggleZoneCategory?: (categoryId: string) => void; + theme?: "dark" | "light"; + onToggleTheme?: () => void; } export function TopBar({ @@ -30,6 +32,8 @@ export function TopBar({ onToggleRoute = () => {}, activeZoneCategories = [], onToggleZoneCategory = () => {}, + theme = "dark", + onToggleTheme = () => {}, }: TopBarProps) { const router = useRouter(); const { data: sessionData } = useSession(); @@ -239,17 +243,29 @@ export function TopBar({ - From 479ac37a09c9e01ede51b3674bc96dc4d8ad6a25 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 17:13:37 +0800 Subject: [PATCH 03/23] feat: use `MapCameraChangedEvent` to store camera position when toggling themes --- apps/web/app/page.tsx | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 92548bc..7213fe2 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -5,6 +5,7 @@ import { Map as GoogleMap, AdvancedMarker, ColorScheme, + MapCameraChangedEvent, } from "@vis.gl/react-google-maps"; import { HeadsUpDisplay } from "@/components/HeadsUpDisplay"; import { NeonPin } from "@/components/NeonPin"; @@ -16,7 +17,7 @@ import { TargetLine } from "@/components/TargetLine"; import { Polyline } from "@/components/Polyline"; import { Polygon } from "@/components/Polygon"; import { JEEPNEY_ROUTES, CAMPUS_ZONES, ZONE_CATEGORIES } from "@/data/map-layers"; -import { useState, useEffect, useRef, useMemo } from "react"; +import { useState, useEffect, useRef, useMemo, useCallback } from "react"; import { trpc } from "@/lib/trpc"; export default function Home() { @@ -55,6 +56,18 @@ export default function Home() { ); }; + const [cameraProps, setCameraProps] = useState({ + center: { lat: 14.6549, lng: 121.0645 }, + zoom: 19, + }); + + const handleCameraChange = useCallback((ev: MapCameraChangedEvent) => { + setCameraProps({ + center: ev.detail.center, + zoom: ev.detail.zoom, + }); + }, []); + const [theme, setTheme] = useState<"dark" | "light">("light"); const [pendingPinCoords, setPendingPinCoords] = useState<{ @@ -102,8 +115,9 @@ export default function Home() { > {/* MAP LAYER */} Date: Sun, 22 Mar 2026 17:43:45 +0800 Subject: [PATCH 04/23] feat: start using design tokens --- apps/web/app/globals.css | 45 +++++++++++++++++-------------- apps/web/app/layout.tsx | 25 +++++++++-------- apps/web/app/page.tsx | 5 ++-- apps/web/components/TopBar.tsx | 13 +++------ apps/web/lib/ThemeContext.tsx | 49 ++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 44 deletions(-) create mode 100644 apps/web/lib/ThemeContext.tsx diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 82ed1df..8059fac 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -3,28 +3,33 @@ @tailwind utilities; :root { - /* DARK MODE */ - --bg-color: #0f1115; - --map-grid: rgba(255, 255, 255, 0.03); - --hud-glass: rgba(15, 17, 21, 0.85); - --hud-glass-solid: rgba(20, 22, 26, 0.95); - --hud-border: 1px solid rgba(255, 255, 255, 0.15); - - /* NEON PALETTE */ - --neon-maroon: #FF0055; - --neon-green: #00FF99; - --neon-blue: #00E5FF; - --neon-yellow: #FFD700; - --neon-pink: #FF00FF; + --bg-base: #f0f2f5; + --bg-panel: rgba(255, 255, 255, 0.9); + --bg-panel-hover: rgba(0, 0, 0, 0.05); + + --text-primary: #121212; + --text-secondary: #555555; + + --border-color: rgba(0, 0, 0, 0.15); + --shadow-glow: rgba(0, 136, 255, 0.2); +} - --text-main: #FFFFFF; - --text-muted: #8899A6; - --shadow-hard: 0 10px 30px rgba(0, 0, 0, 0.6); +.dark { + --bg-base: #0f1115; + --bg-panel: rgba(10, 10, 12, 0.85); + --bg-panel-hover: rgba(255, 255, 255, 0.1); + + --text-primary: #ffffff; + --text-secondary: #8899A6; + + --border-color: rgba(255, 255, 255, 0.15); + --shadow-glow: rgba(0, 229, 255, 0.2); } body { - background-color: var(--bg-color); - color: var(--text-main); + background-color: var(--bg-base); + color: var(--text-primary); + transition: background-color 0.3s ease, color 0.3s ease; margin: 0; padding: 0; overflow: hidden; @@ -146,9 +151,9 @@ p, .font-desc, .details-card p { /* --- SIDE CONTROLS --- */ .icon-button, .control-button { width: 44px; height: 44px; - background: rgba(10, 10, 12, 0.9); + background: var(--bg-panel); backdrop-filter: blur(12px); - border: 1px solid rgba(255, 255, 255, 0.1); + border: 1px solid var(--border-color); border-radius: 12px; color: white; display: flex; align-items: center; justify-content: center; diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index 2486d8d..5dbd06a 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -3,6 +3,7 @@ import { Chakra_Petch, Nunito } from "next/font/google"; import localFont from "next/font/local"; import "./globals.css"; import { TRPCProvider } from "@/components/TRPCProvider"; +import { ThemeProvider } from "@/lib/ThemeContext"; const chakra = Chakra_Petch({ weight: ["300", "400", "500", "600", "700"], @@ -40,17 +41,19 @@ export const metadata: Metadata = { }; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode; }>) { - return ( - - - - {children} - - - - ); + return ( + + + + + {children} + + + + + ); } diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx index 7213fe2..f2b34a0 100644 --- a/apps/web/app/page.tsx +++ b/apps/web/app/page.tsx @@ -18,6 +18,7 @@ import { Polyline } from "@/components/Polyline"; import { Polygon } from "@/components/Polygon"; import { JEEPNEY_ROUTES, CAMPUS_ZONES, ZONE_CATEGORIES } from "@/data/map-layers"; import { useState, useEffect, useRef, useMemo, useCallback } from "react"; +import { useTheme } from "@/lib/ThemeContext"; import { trpc } from "@/lib/trpc"; export default function Home() { @@ -68,7 +69,7 @@ export default function Home() { }); }, []); - const [theme, setTheme] = useState<"dark" | "light">("light"); + const { theme } = useTheme(); const [pendingPinCoords, setPendingPinCoords] = useState<{ lat: number; @@ -236,8 +237,6 @@ export default function Home() { onToggleRoute={handleToggleRoute} activeZoneCategories={activeZoneCategories} onToggleZoneCategory={handleToggleZoneCategory} - theme={theme} - onToggleTheme={() => setTheme(prev => prev === "dark" ? "light" : "dark")} /> {/* TARGETING CROSSHAIR (Only visible when armed) */} diff --git a/apps/web/components/TopBar.tsx b/apps/web/components/TopBar.tsx index bb97a86..6d675e2 100644 --- a/apps/web/components/TopBar.tsx +++ b/apps/web/components/TopBar.tsx @@ -3,6 +3,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { trpc } from "@/lib/trpc"; +import { useTheme } from "@/lib/ThemeContext"; import { useSession } from "@/lib/auth-client"; import { JEEPNEY_ROUTES, ZONE_CATEGORIES } from "@/data/map-layers"; @@ -18,8 +19,6 @@ interface TopBarProps { onToggleRoute?: (routeId: string) => void; activeZoneCategories?: string[]; onToggleZoneCategory?: (categoryId: string) => void; - theme?: "dark" | "light"; - onToggleTheme?: () => void; } export function TopBar({ @@ -32,13 +31,12 @@ export function TopBar({ onToggleRoute = () => {}, activeZoneCategories = [], onToggleZoneCategory = () => {}, - theme = "dark", - onToggleTheme = () => {}, }: TopBarProps) { const router = useRouter(); const { data: sessionData } = useSession(); const [isTransitMenuOpen, setIsTransitMenuOpen] = useState(false); const [isZoneMenuOpen, setIsZoneMenuOpen] = useState(false); + const { theme, toggleTheme } = useTheme(); const handleProfileClick = () => { if (sessionData?.user) { @@ -243,12 +241,7 @@ export function TopBar({ - - @@ -175,7 +175,7 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { .modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; - background: rgba(0, 0, 0, 0.7); + background: rgba(0, 0, 0, 0.7); /* Intentional absolute dark overlay */ backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); display: flex; @@ -188,11 +188,8 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { .modal-card { width: 100%; max-width: 400px; - background: rgba(10, 10, 12, 0.85); - border: 1px solid rgba(0, 229, 255, 0.3); - border-radius: 16px; padding: 24px; - box-shadow: 0 0 30px rgba(0, 229, 255, 0.1); + /* tactical-panel handles the colors, we just add the layout */ animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); } @@ -201,13 +198,13 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { align-items: center; gap: 12px; margin-bottom: 24px; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + border-bottom: 1px solid var(--border-color); padding-bottom: 16px; } .modal-title { font-family: var(--font-cubao-wide), sans-serif; - color: white; + color: var(--text-primary); font-size: 20px; margin: 0; letter-spacing: 0.05em; @@ -225,7 +222,7 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { gap: 8px; } - label { + .input-group span { font-family: var(--font-chakra), sans-serif; font-size: 12px; color: var(--neon-blue, #00E5FF); @@ -234,11 +231,11 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { } input, textarea { - background: rgba(0, 0, 0, 0.5); - border: 1px solid rgba(255, 255, 255, 0.2); + background: var(--bg-base); + border: 1px solid var(--border-color); border-radius: 8px; padding: 12px; - color: white; + color: var(--text-primary); font-family: var(--font-nunito), sans-serif; font-size: 14px; outline: none; @@ -247,7 +244,7 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { input:focus, textarea:focus { border-color: var(--neon-blue, #00E5FF); - box-shadow: 0 0 10px rgba(0, 229, 255, 0.2); + box-shadow: 0 0 10px var(--shadow-glow); } .type-selector { @@ -257,9 +254,9 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { } .type-btn { - background: rgba(255, 255, 255, 0.05); - border: 1px solid rgba(255, 255, 255, 0.1); - color: #aaa; + background: transparent; + border: 1px solid var(--border-color); + color: var(--text-secondary); padding: 10px; border-radius: 6px; font-family: var(--font-chakra), sans-serif; @@ -269,14 +266,15 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { } .type-btn:hover { - background: rgba(255, 255, 255, 0.1); + background: var(--bg-panel-hover); + color: var(--text-primary); } .type-btn.active { background: rgba(0, 229, 255, 0.15); border-color: var(--neon-blue, #00E5FF); color: var(--neon-blue, #00E5FF); - box-shadow: inset 0 0 8px rgba(0, 229, 255, 0.2); + box-shadow: inset 0 0 8px var(--shadow-glow); } .action-row { @@ -285,39 +283,11 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { margin-top: 12px; } - .cancel-btn, .save-btn { + .action-row button { flex: 1; padding: 14px; - border-radius: 8px; - font-family: var(--font-chakra), sans-serif; - font-weight: 700; font-size: 13px; letter-spacing: 0.05em; - cursor: pointer; - transition: all 0.2s; - } - - .cancel-btn { - background: transparent; - border: 1px solid rgba(255, 255, 255, 0.2); - color: #aaa; - } - - .cancel-btn:hover { - background: rgba(255, 255, 255, 0.1); - color: white; - } - - .save-btn { - background: rgba(0, 229, 255, 0.1); - border: 1px solid var(--neon-blue, #00E5FF); - color: var(--neon-blue, #00E5FF); - } - - .save-btn:hover:not(:disabled) { - background: var(--neon-blue, #00E5FF); - color: black; - box-shadow: 0 0 15px rgba(0, 229, 255, 0.4); } .save-btn:disabled { @@ -332,4 +302,4 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { `} ); -} +} \ No newline at end of file From 2329ce91e3538a34e1ff3d7c9bf2f2e142abb356 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:13:04 +0800 Subject: [PATCH 16/23] chore: remove spacing --- apps/web/components/AddPinModal.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/web/components/AddPinModal.tsx b/apps/web/components/AddPinModal.tsx index 9240f3d..f6d0d56 100644 --- a/apps/web/components/AddPinModal.tsx +++ b/apps/web/components/AddPinModal.tsx @@ -45,7 +45,7 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { const utils = trpc.useUtils(); const createPin = trpc.pin.userCreate.useMutation({ onSuccess: (newPin) => { - utils.pin.getAll.invalidate(); // this forces a refresh on the main page + utils.pin.getAll.invalidate(); if (!newPin) return; onSave(newPin.id); }, @@ -175,7 +175,7 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { .modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; - background: rgba(0, 0, 0, 0.7); /* Intentional absolute dark overlay */ + background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); display: flex; @@ -189,7 +189,6 @@ export function AddPinModal({ coords, onSave, onCancel }: AddPinModalProps) { width: 100%; max-width: 400px; padding: 24px; - /* tactical-panel handles the colors, we just add the layout */ animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1); } From 998f8a0d998a220f85377b7e9c443de2f73ce0bf Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:21:43 +0800 Subject: [PATCH 17/23] refactor: use variables for `AddPinModal` --- apps/web/components/ExpandedPinView.tsx | 318 ++++++++++-------------- 1 file changed, 128 insertions(+), 190 deletions(-) diff --git a/apps/web/components/ExpandedPinView.tsx b/apps/web/components/ExpandedPinView.tsx index c569e32..88969fe 100644 --- a/apps/web/components/ExpandedPinView.tsx +++ b/apps/web/components/ExpandedPinView.tsx @@ -67,29 +67,11 @@ const CommentNode = ({ {new Date(comment.createdAt).toLocaleString("default")} - {/* {depth === 0 && comment.rating && ( - - {"★".repeat(comment.rating)} {comment.rating}/5 - - )} */}

{comment.message}

- {/* */} {!isReplying ? ( sessionData && depth < 3 && ( @@ -102,10 +84,14 @@ const CommentNode = ({ ) ) : ( -
- - - +
@@ -123,19 +109,37 @@ const CommentNode = ({
); @@ -183,11 +187,7 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) { ); return ( - // biome-ignore lint/a11y/noStaticElementInteractions: - // biome-ignore lint/a11y/useKeyWithClickEvents:
- {/** biome-ignore lint/a11y/noStaticElementInteractions: */} - {/** biome-ignore lint/a11y/useKeyWithClickEvents: */}
e.stopPropagation()}>
{/* HEADER */} @@ -220,20 +220,6 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) { {pin?.images?.map((img) => (
- {/* - - - - */}
))}
@@ -273,12 +259,6 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) {
- {/* RATING */} - {/*
- AVG RATING - ★ 5.0 / 5.0 -
*/} - {/* TIMESTAMPS */}
CREATED AT @@ -302,28 +282,22 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) {
+ {/* OWNER ACTIONS */} {!isDeleting && sessionData?.user.id === pin?.ownerId && ( - )} {isDeleting && ( -
- Are you sure you want to delete? -
- -
@@ -338,15 +312,19 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) { className="action-btn" onClick={() => setIsReplying(true)} > - COMMENT + + ADD COMMENT ) ) : ( -
- - - +
)} @@ -371,20 +349,17 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) { } .modal-content { - background: rgba(10, 10, 12, 0.95); - border: 1px solid rgba(255, 255, 255, 0.15); + background: var(--bg-panel); + border: 1px solid var(--border-color); border-top: 4px solid ${color}; border-radius: 24px; width: 100%; max-width: 500px; - height: 70vh; - display: flex; flex-direction: column; - box-shadow: 0 30px 60px rgba(0, 0, 0, 0.8); + box-shadow: 0 30px 60px rgba(0, 0, 0, 0.3); animation: scalePop 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); - overflow: hidden; position: relative; padding: 0; @@ -403,157 +378,120 @@ export function ExpandedPinView({ pinId, onClose }: ExpandedPinViewProps) { left: 0; width: 100%; height: 80px; - /* Fades from solid background color to transparent */ - background: linear-gradient(to top, rgba(10, 10, 12, 1) 10%, rgba(10, 10, 12, 0) 100%); - pointer-events: none; /* Ensures you can still click text behind the fade */ + /* Fades to transparent seamlessly in both Light and Dark mode */ + background: linear-gradient(to top, var(--bg-base) 10%, transparent 100%); + pointer-events: none; border-bottom-left-radius: 24px; border-bottom-right-radius: 24px; } - .custom-vertical-scrollbar::-webkit-scrollbar { - width: 6px; - } - .custom-vertical-scrollbar::-webkit-scrollbar-track { - background: transparent; - margin-top: 24px; - margin-bottom: 24px; - } - .custom-vertical-scrollbar::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.15); - border-radius: 10px; - } - .custom-vertical-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.3); - } + /* Custom Scrollbars */ + .custom-vertical-scrollbar::-webkit-scrollbar { width: 6px; } + .custom-vertical-scrollbar::-webkit-scrollbar-track { background: transparent; margin: 24px 0; } + .custom-vertical-scrollbar::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; } + .custom-vertical-scrollbar::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); } + .custom-scrollbar::-webkit-scrollbar { height: 6px; } + .custom-scrollbar::-webkit-scrollbar-track { background: transparent; } + .custom-scrollbar::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 10px; transition: background 0.2s; } + .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: var(--text-secondary); } + + /* Header & Images */ .modal-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 20px; } .badge { font-family: var(--font-cubao-wide); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; } - h2 { font-family: var(--font-chakra); color: white; font-size: 26px; font-weight: 800; margin: 4px 0 0 0; } + h2 { font-family: var(--font-chakra); color: var(--text-primary); font-size: 26px; font-weight: 800; margin: 4px 0 0 0; } .close-btn { - background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 50%; width: 44px; height: 44px; color: white; cursor: pointer; + background: transparent; border: 1px solid var(--border-color); + border-radius: 50%; width: 44px; height: 44px; color: var(--text-primary); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; flex-shrink: 0; } - .close-btn:active { transform: scale(0.9); background: rgba(255, 255, 255, 0.1); } + .close-btn:hover { background: var(--bg-panel-hover); } + .close-btn:active { transform: scale(0.9); } .photo-gallery { - display: grid; - grid-template-rows: repeat(2, 90px); - grid-auto-flow: column; - gap: 12px; - margin-bottom: 24px; - overflow-x: auto; - overscroll-behavior-x: contain; - padding-bottom: 8px; - scroll-snap-type: x mandatory; + display: grid; grid-template-rows: repeat(2, 90px); grid-auto-flow: column; + gap: 12px; margin-bottom: 24px; overflow-x: auto; overscroll-behavior-x: contain; + padding-bottom: 8px; scroll-snap-type: x mandatory; } .no-scrollbar::-webkit-scrollbar { display: none; } .photo-placeholder { - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-panel-hover); + border: 1px solid var(--border-color); border-radius: 16px; - display: flex; - align-items: center; - justify-content: center; - scroll-snap-align: start; - position: relative; - overflow: hidden; - } - - .photo-placeholder::after { - content: ''; position: absolute; top: 0; left: -100%; width: 50%; height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.05), transparent); - animation: shimmer 2s infinite; - } - - .photo-placeholder.large { - grid-row: span 2; /* Spans both rows (192px tall including gap) */ - width: 192px; - } - - .photo-placeholder.small { - grid-row: span 1; /* Spans 1 row (90px tall) */ - width: 140px; - } - - .custom-scrollbar::-webkit-scrollbar { - height: 6px; /* Thin horizontal scrollbar */ - } - .custom-scrollbar::-webkit-scrollbar-track { - background: rgba(255, 255, 255, 0.02); - border-radius: 10px; - } - .custom-scrollbar::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.15); - border-radius: 10px; - transition: background 0.2s; - } - .custom-scrollbar::-webkit-scrollbar-thumb:hover { - background: rgba(255, 255, 255, 0.3); /* Highlights when hovered */ + display: flex; align-items: center; justify-content: center; + scroll-snap-align: start; position: relative; overflow: hidden; } + .photo-placeholder.large { grid-row: span 2; width: 192px; } + /* Body Data */ .modal-body { display: flex; flex-direction: column; gap: 24px; } - .description { font-family: var(--font-nunito); font-size: 15px; color: #ccc; line-height: 1.5; margin: 0; } + .description { font-family: var(--font-nunito); font-size: 15px; color: var(--text-primary); line-height: 1.5; margin: 0; } .meta-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; - background: rgba(0, 0, 0, 0.3); border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-panel-hover); border: 1px solid var(--border-color); border-radius: 16px; padding: 20px; } .meta-item { display: flex; flex-direction: column; gap: 4px; } + .col-span-2 { grid-column: span 2; } + .meta-label { font-family: var(--font-chakra); font-size: 10px; font-weight: 800; color: var(--text-secondary); letter-spacing: 0.1em; } + .meta-value { font-family: var(--font-nunito); font-size: 14px; font-weight: 700; color: var(--text-primary); } + + .font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; letter-spacing: 0.05em; } + .font-cubao-wide { font-family: var(--font-cubao-wide); font-weight: 100; letter-spacing: 0.1em;} + .text-muted { color: var(--text-secondary); font-style: italic; } + .text-neon-green { color: var(--neon-green, #00FF99); text-shadow: 0 0 10px rgba(0, 255, 153, 0.3); } + + .danger-btn { border-color: #ff4d4d; color: #ff4d4d; padding: 12px; } + .danger-btn:hover { background: rgba(255, 77, 77, 0.1); transform: none;} + + .delete-confirm-box { + background: rgba(255, 77, 77, 0.05); + border: 1px solid #ff4d4d; + border-radius: 12px; + padding: 16px; + display: flex; + flex-direction: column; + gap: 12px; + } + .delete-confirm-box p { margin: 0; color: var(--text-primary); font-family: var(--font-nunito); font-size: 14px; } + .delete-actions { display: flex; gap: 8px; } + .danger-btn-solid { background: rgba(255, 77, 77, 0.2); border-color: #ff4d4d; color: #ff4d4d; box-shadow: none; flex: 1; padding: 10px;} + .danger-btn-solid:hover { background: #ff4d4d; color: white; box-shadow: 0 0 15px rgba(255, 77, 77, 0.4); } + .forum-section { - margin-top: 16px; - border-top: 1px solid rgba(255, 255, 255, 0.05); + margin-top: 8px; + border-top: 1px solid var(--border-color); padding-top: 24px; } - .section-title { - font-family: var(--font-chakra); - font-size: 12px; - font-weight: 900; - letter-spacing: 0.15em; - color: #666; - margin-bottom: 20px; + font-family: var(--font-chakra); font-size: 12px; font-weight: 900; + letter-spacing: 0.15em; color: var(--text-secondary); margin-bottom: 20px; } .action-btn { - background: none; - border: none; - color: #888; - font-family: var(--font-chakra); - font-size: 11px; - font-weight: 700; - cursor: pointer; - display: flex; - align-items: center; - gap: 4px; - padding: 0; - transition: color 0.2s; + background: none; border: none; color: var(--text-secondary); + font-family: var(--font-chakra); font-size: 11px; font-weight: 700; + cursor: pointer; display: flex; align-items: center; gap: 4px; padding: 0; transition: color 0.2s; } + .action-btn:hover { color: var(--text-primary); } - .action-btn:hover { - color: #fff; + .reply-form { display: flex; gap: 8px; width: 100%; align-items: center; margin-bottom: 24px; } + .reply-input { + flex: 1; background: var(--bg-base); border: 1px solid var(--border-color); + border-radius: 6px; padding: 10px 12px; color: var(--text-primary); + font-family: var(--font-nunito); font-size: 13px; outline: none; } - - .col-span-2 { grid-column: span 2; } - - .meta-label { font-family: var(--font-chakra); font-size: 10px; font-weight: 800; color: #666; letter-spacing: 0.1em; } - .meta-value { font-family: var(--font-nunito); font-size: 14px; font-weight: 700; color: #eee; } - - .font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; letter-spacing: 0.05em; } - .font-cubao-wide { font-family: var(--font-cubao-wide); font-weight: 100; letter-spacing: 0.1em;} - .text-muted { color: #888; font-style: italic; } - .text-neon-green { color: var(--neon-green, #00FF99); text-shadow: 0 0 10px rgba(0, 255, 153, 0.3); } - .text-neon-yellow { color: var(--neon-yellow, #FFD700); text-shadow: 0 0 10px rgba(255, 215, 0, 0.3); } + .reply-input:focus { border-color: var(--neon-blue, #00E5FF); } + .form-btn { padding: 10px 16px; font-size: 11px; border-radius: 6px; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes scalePop { from { opacity: 0; transform: scale(0.95) translateY(20px); } to { opacity: 1; transform: scale(1) translateY(0); } } - @keyframes shimmer { 100% { left: 200%; } } `} ); -} +} \ No newline at end of file From 62b34d0a5f686b3aaf8e8fb31acbc37aa2e5d989 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:23:23 +0800 Subject: [PATCH 18/23] fix: match `tactical-button-primary` with `tactical-button` --- apps/web/app/globals.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 8806c4e..400218f 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -85,6 +85,11 @@ p, .font-desc, .details-card p { border: 1px solid var(--neon-blue); color: var(--neon-blue); box-shadow: 0 0 10px var(--shadow-glow); + border-radius: 8px; + font-family: var(--font-chakra), sans-serif; + font-weight: 600; + cursor: pointer; + transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .tactical-button-primary:hover { From 7f0b8439fd59d05f305503d266ccfac62737ca9e Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:25:19 +0800 Subject: [PATCH 19/23] refactor: use variables for `HeadsUpDisplay` --- apps/web/components/HeadsUpDisplay.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/web/components/HeadsUpDisplay.tsx b/apps/web/components/HeadsUpDisplay.tsx index 9971667..a73aede 100644 --- a/apps/web/components/HeadsUpDisplay.tsx +++ b/apps/web/components/HeadsUpDisplay.tsx @@ -1,7 +1,6 @@ "use client"; import { useMemo, useState } from "react"; -import { getFilterColor } from "@/components/TopBar"; import { PinDetailsCard } from "@/components/PinDetailsCard"; import { ExpandedPinView } from "@/components/ExpandedPinView"; import { useSession } from "@/lib/auth-client"; @@ -29,7 +28,8 @@ export function HeadsUpDisplay({ }; const { data: sessionData } = useSession(); - const isLoggedIn = useMemo(() => !!sessionData?.user.id, [sessionData]); + const isLoggedIn = useMemo(() => !!sessionData?.user?.id, [sessionData]); + return (
); -} +} \ No newline at end of file From 45aec965f0a37c01dc6906976bcc3483ca4d7678 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:29:29 +0800 Subject: [PATCH 20/23] refactor: use variables for `MapCursor` --- apps/web/components/MapCursor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/MapCursor.tsx b/apps/web/components/MapCursor.tsx index 58d2aff..2cad9e4 100644 --- a/apps/web/components/MapCursor.tsx +++ b/apps/web/components/MapCursor.tsx @@ -41,7 +41,7 @@ export function MapCursor({ heading = 0 }: MapCursorProps) { .cursor-arrow { position: relative; z-index: 10; - filter: drop-shadow(0 0 8px var(--neon-blue, #00E5FF)); + filter: drop-shadow(0 0 8px var(--neon-blue)); transition: transform 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } From abd568a808b12f289fbf3e89968484c742b8abc2 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:31:46 +0800 Subject: [PATCH 21/23] refactor: use variables for `NeonPin` --- apps/web/components/NeonPin.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/components/NeonPin.tsx b/apps/web/components/NeonPin.tsx index 6324b82..d5a9d7f 100644 --- a/apps/web/components/NeonPin.tsx +++ b/apps/web/components/NeonPin.tsx @@ -51,7 +51,7 @@ export function NeonPin({ height: "100%", transform: "rotate(45deg)", border: `2px solid ${color}`, - backgroundColor: isSelected ? color : "rgba(15, 17, 21, 0.6)", + backgroundColor: isSelected ? color : "var(--bg-panel)", boxShadow: isLocked ? `0 0 30px 5px ${color}` : isSelected @@ -68,7 +68,7 @@ export function NeonPin({ style={{ transform: "rotate(-45deg)", fontSize: "18px", - color: isSelected ? "#000" : color, + color: isSelected ? "var(--bg-base)" : color, fontFamily: "var(--font-cubao-wide), sans-serif", }} > @@ -77,4 +77,4 @@ export function NeonPin({ ); -} +} \ No newline at end of file From 9800c27d9c5a2db603ee36a34d972ad57d3d6ffb Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:34:16 +0800 Subject: [PATCH 22/23] refactor: use variables for `PinDetailsCard` --- apps/web/components/PinDetailsCard.tsx | 40 +++++++++++++++----------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/web/components/PinDetailsCard.tsx b/apps/web/components/PinDetailsCard.tsx index 13f4157..494fc4f 100644 --- a/apps/web/components/PinDetailsCard.tsx +++ b/apps/web/components/PinDetailsCard.tsx @@ -71,7 +71,8 @@ export function PinDetailsCard({ className="lock-button" onClick={onLockClick} style={{ - background: isLocked ? "var(--neon-blue, #00D1FF)" : "white", + background: isLocked ? "var(--neon-blue)" : "var(--text-primary)", + color: "var(--bg-base)", }} > {isLocked ? "TARGET LOCKED" : "LOCK TARGET"} @@ -80,20 +81,21 @@ export function PinDetailsCard({ ); -} +} \ No newline at end of file From 83813cd01a1842101b1fc80df34d42718cf407f8 Mon Sep 17 00:00:00 2001 From: ptrn23 Date: Sun, 22 Mar 2026 19:38:38 +0800 Subject: [PATCH 23/23] fix: changed ALL button to be black when selected in light mode --- apps/web/components/TopBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/components/TopBar.tsx b/apps/web/components/TopBar.tsx index 28135a8..29116ea 100644 --- a/apps/web/components/TopBar.tsx +++ b/apps/web/components/TopBar.tsx @@ -323,6 +323,6 @@ export const getFilterColor = (type: string) => { case "utility": return "#00d1ff"; default: - return "#ffffff"; + return "var(--text-primary)"; } };