diff --git a/.env.sample b/.env.sample
index bc9711c..24e1e91 100644
--- a/.env.sample
+++ b/.env.sample
@@ -2,5 +2,5 @@ MODE=local
MONGO_URI=mongodb://:@127.0.0.1:27017/?authSource=admin&retryWrites=true&w=majority
DOMAIN=https://localhost:8001
PORT=8001
-API_VERSION=""
-APP_NAMe="LOCAL"
\ No newline at end of file
+API_VERSION="/api/v1"
+APP_NAME="LOCAL"
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ae1cf18..8a7644e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,3 +59,6 @@ poetry.lock
*.tmp
*.temp
*.bak
+
+
+assets/images/qr/*
\ No newline at end of file
diff --git a/app/main.py b/app/main.py
index c960eeb..932e5e4 100644
--- a/app/main.py
+++ b/app/main.py
@@ -2,14 +2,19 @@
from contextlib import asynccontextmanager
from pathlib import Path
import logging
-import traceback
import asyncio
from fastapi import FastAPI, Request
+
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from starlette.middleware.sessions import SessionMiddleware
+# from fastapi.exceptions import RequestValidationError
+# from starlette.exceptions import HTTPException as StarletteHTTPException
+from fastapi.exceptions import HTTPException as FastAPIHTTPException
+from fastapi.templating import Jinja2Templates
+
from app.routes import ui_router
from app.utils import db
from app.utils.cache import cleanup_expired
@@ -20,6 +25,7 @@
from app.utils.config import (
CACHE_TTL,
SESSION_SECRET,
+ QR_DIR,
)
@@ -90,22 +96,67 @@ async def lifespan(app: FastAPI):
app = FastAPI(title="TinyURL", lifespan=lifespan)
app.add_middleware(SessionMiddleware, secret_key=SESSION_SECRET)
+templates = Jinja2Templates(directory="app/templates")
+# Mount QR static files
BASE_DIR = Path(__file__).resolve().parent
-STATIC_DIR = BASE_DIR / "static"
-
-app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")
+# Mount QR static files
+app.mount(
+ "/static",
+ StaticFiles(directory=str(BASE_DIR / "static")),
+ name="static",
+)
+# Ensure QR directory exists at startup
+QR_DIR.mkdir(parents=True, exist_ok=True)
+app.mount(
+ "/qr",
+ StaticFiles(directory=str(QR_DIR)),
+ name="qr",
+)
# -----------------------------
# Global error handler
# -----------------------------
-@app.exception_handler(Exception)
-async def global_exception_handler(request: Request, exc: Exception):
- traceback.print_exc()
+# @app.exception_handler(Exception)
+# async def global_exception_handler(request: Request, exc: Exception):
+# traceback.print_exc()
+# return JSONResponse(
+# status_code=500,
+# content={"success": False, "error": "INTERNAL_SERVER_ERROR"},
+# )
+
+
+# @app.exception_handler(404)
+# async def custom_404_handler(request: Request, exc):
+# return templates.TemplateResponse(
+# "404.html",
+# {"request": request},
+# status_code=404,
+# )
+
+
+@app.exception_handler(FastAPIHTTPException)
+async def http_exception_handler(request: Request, exc: FastAPIHTTPException):
+
+ # If it's API/UI route → return JSON
+ if request.url.path.startswith("/cache") or request.url.path.startswith("/api"):
+ return JSONResponse(
+ status_code=exc.status_code,
+ content={"error": exc.detail},
+ )
+
+ # If it's browser route → return HTML page
+ if exc.status_code == 404:
+ return templates.TemplateResponse(
+ "404.html",
+ {"request": request},
+ status_code=404,
+ )
+
return JSONResponse(
- status_code=500,
- content={"success": False, "error": "INTERNAL_SERVER_ERROR"},
+ status_code=exc.status_code,
+ content={"success": False, "error": exc.detail},
)
diff --git a/app/routes.py b/app/routes.py
index 004484f..ba5f165 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -1,6 +1,5 @@
import os
from datetime import datetime, timezone
-from pathlib import Path
from typing import Optional
from app.utils.cache import list_cache_clean, clear_cache
from fastapi import (
@@ -22,6 +21,7 @@
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel, Field
+
from app import __version__
from app.utils import db
from app.utils.cache import (
@@ -34,13 +34,12 @@
remove_cache_key,
rev_cache,
)
-from app.utils.config import DOMAIN, MAX_RECENT_URLS, CACHE_PURGE_TOKEN
+from app.utils.config import DOMAIN, MAX_RECENT_URLS, CACHE_PURGE_TOKEN, QR_DIR
from app.utils.helper import generate_code, is_valid_url, sanitize_url, format_date
from app.utils.qr import generate_qr_with_logo
-BASE_DIR = Path(__file__).resolve().parent
-templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
-
+# templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
+templates = Jinja2Templates(directory="app/templates")
# Routers
ui_router = APIRouter()
api_router = APIRouter()
@@ -67,10 +66,8 @@ async def index(request: Request):
if qr_enabled and new_short_url and short_code:
qr_data = new_short_url
qr_filename = f"{short_code}.png"
- qr_dir = BASE_DIR / "static" / "qr"
- qr_dir.mkdir(parents=True, exist_ok=True)
- generate_qr_with_logo(qr_data, str(qr_dir / qr_filename))
- qr_image = f"/static/qr/{qr_filename}"
+ generate_qr_with_logo(qr_data, str(QR_DIR / qr_filename))
+ qr_image = f"/qr/{qr_filename}"
recent_urls = db.get_recent_urls(MAX_RECENT_URLS) or get_recent_from_cache(
MAX_RECENT_URLS
@@ -137,7 +134,7 @@ async def create_short_url(
return RedirectResponse("/", status_code=status.HTTP_303_SEE_OTHER)
-@ui_router.get("/recent", response_class=HTMLResponse)
+@ui_router.get("/history", response_class=HTMLResponse)
async def recent_urls(request: Request):
recent_urls_list = db.get_recent_urls(MAX_RECENT_URLS) or get_recent_from_cache(
MAX_RECENT_URLS
@@ -222,10 +219,11 @@ def redirect_short_ui(short_code: str, background_tasks: BackgroundTasks):
set_cache_pair(short_code, original_url)
return RedirectResponse(original_url)
- return PlainTextResponse("Invalid short URL", status_code=404)
+ # return PlainTextResponse("Invalid short URL", status_code=404)
+ raise HTTPException(status_code=404, detail="Page not found")
-@ui_router.delete("/recent/{short_code}")
+@ui_router.delete("/history/{short_code}")
def delete_recent_api(short_code: str):
recent = get_recent_from_cache(MAX_RECENT_URLS) or []
removed_from_cache = False
@@ -233,6 +231,7 @@ def delete_recent_api(short_code: str):
for i, item in enumerate(recent):
code = item.get("short_code") or item.get("code")
if code == short_code:
+ recent.pop(i) # remove from cache
removed_from_cache = True
break
@@ -242,7 +241,6 @@ def delete_recent_api(short_code: str):
if db_available:
db_deleted = db.delete_by_short_code(short_code)
- # ✅ If nothing was deleted anywhere → 404
if not removed_from_cache and not db_deleted:
raise HTTPException(
status_code=404, detail=f"short_code '{short_code}' not found"
diff --git a/app/static/css/tiny.css b/app/static/css/tiny.css
index 873f075..daf3df1 100644
--- a/app/static/css/tiny.css
+++ b/app/static/css/tiny.css
@@ -16,80 +16,163 @@ body {
background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%);
}
-body {
- background: var(--bg);
- color: var(--text-primary);
- font-family: "Inter", system-ui, sans-serif;
- margin: 0;
- overflow-x: hidden;
- background-image: radial-gradient(circle at 50% -20%, #1e1e2e 0%, transparent 50%);
-}
-
/* Light theme overrides */
body.light-theme {
+ /* background + glass */
--bg: #f9fafb;
--glass: rgba(0, 0, 0, 0.03);
--glass-border: rgba(0, 0, 0, 0.07);
- --accent: #2563eb;
+
+ /* main card + text */
+ --card: #ffffff;
--text-primary: #111827;
--text-secondary: #4b5563;
+ --text-color: #111827;
+
+ /* accent */
+ --accent: #2563eb;
- /* Remove or soften the dark gradient */
+ /* Remove the dark radial gradient */
background-image: none;
- /* clean white background */
- /* Or use a subtle light gradient if you prefer */
- /* background-image: radial-gradient(circle at 50% -20%, #e5e7eb 0%, transparent 50%); */
}
/* Layout */
.main-layout {
max-width: 900px;
margin: 0 auto;
- padding: 4rem 1rem;
+ padding: 6rem 1rem 4rem;
display: flex;
flex-direction: column;
gap: 2rem;
}
-.site-header {
+.page {
+ padding-top: 6rem;
+}
+
+.app-header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 55px;
display: flex;
- justify-content: space-between;
align-items: center;
- padding: 1rem 1.5rem;
+ justify-content: space-between;
+ padding: 0 10px;
+ box-sizing: border-box;
+
background: var(--glass);
border-bottom: 1px solid var(--glass-border);
- backdrop-filter: blur(10px);
+ z-index: 1000;
+}
+
+body.light-theme .app-header {
+ background: #ffffff;
+ /* solid background */
+ border-bottom: 1px solid #e5e7eb;
+ /* clear separation */
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
}
.header-left,
.header-right {
display: flex;
- gap: 1rem;
align-items: center;
+ gap: 12px;
}
-.header-center {
- flex: 1;
- text-align: center;
+.app-logo {
+ width: 36px;
+ height: 36px;
+ background: linear-gradient(135deg, #2563eb, #5ab9ff);
+ color: #ffffff;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
}
-.logo {
- margin: 0;
+.app-name {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary);
}
-.icon-btn {
- background: none;
- border: none;
+.header-nav {
+ display: flex;
+ gap: 26px;
+ margin: 0 auto;
+}
+
+.nav-link,
+.nav-link:link,
+.nav-link:visited {
+ text-decoration: none;
color: var(--text-primary);
- font-size: 1.2rem;
+ font-weight: 500;
+ position: relative;
+}
+
+body.dark-theme .app-header {
+ background: linear-gradient(180deg, #0b1220, #050b14);
+}
+
+.dark-theme .nav-link {
+ color: #e5e7eb;
+}
+
+.nav-link:hover {
+ color: #2563eb;
+}
+
+.nav-link.active::after {
+ content: "";
+ position: absolute;
+ bottom: -6px;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ background: #111827;
+}
+
+.nav-link {
+ position: relative;
+}
+
+.nav-link::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ bottom: -4px;
+ width: 0;
+ height: 2px;
+ background: var(--text-primary);
+ transition: width 0.3s;
+}
+
+.nav-link.active::after {
+ width: 100%;
+}
+
+
+.dark-theme .nav-link.active::after {
+ background: #f8fafc;
+}
+
+.theme-toggle {
+ background: transparent;
+ border: none;
cursor: pointer;
- transition: color 0.3s;
+ padding: 8px;
+ border-radius: 8px;
+ font-weight: 700;
+ background: var(--glass);
+ color: var(--text-primary);
}
-.icon-btn:hover {
+.theme-toggle:hover {
color: var(--accent);
}
@@ -251,6 +334,7 @@ body.light-theme {
justify-content: space-between;
margin-bottom: 1rem;
padding: 0 0.5rem;
+ align-items: center;
}
.scroll-container {
@@ -281,17 +365,237 @@ body.light-theme {
font-weight: bold;
}
-.original-url {
- color: var(--text-secondary);
- font-size: 0.8rem;
+.hero {
+ text-align: center;
+ margin: 40px 0;
+}
+
+.hero h1 {
+ font-size: 42px;
+ font-weight: 700;
+}
+
+/* ===============================
+ MODERN GLASS RECENT TABLE
+================================= */
+/* PAGE CONTAINER */
+.recent-page-container {
+ width: 100%;
+ max-width: 1200px;
+ /* controls table width */
+ margin: 0 auto;
+ /* centers */
+ padding: 0 24px;
+ /* space left & right */
+ box-sizing: border-box;
+}
+
+/* Wrapper */
+.recent-table-wrapper {
+ width: 100%;
+ /*margin-top: 20px;
+ margin-bottom: 20px;*/
+ overflow-x: auto;
+}
+
+/* ===============================
+ TABLE BASE
+================================= */
+
+.recent-table {
+ width: 100%;
+ border-collapse: collapse;
+ border-radius: 12px;
+ overflow: hidden;
+ table-layout: fixed;
+ min-width: 800px;
+}
+
+/* Header */
+.recent-table thead {
+ background: var(--glass);
+}
+
+.recent-table th {
+ padding: 10px 14px;
+ text-align: left;
+ font-size: 13px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ font-weight: 700;
+ color: var(--muted);
+ border-bottom: 1px solid var(--glass-border);
+ white-space: nowrap;
+}
+
+/* Body cells */
+.recent-table td {
+ padding: 14px;
+ font-size: 14px;
+ color: var(--text-primary);
+ border-bottom: 1px solid var(--glass-border);
+ vertical-align: middle;
+ transition: 0.25s ease;
white-space: nowrap;
+}
+
+/* Row hover */
+.recent-table tbody tr:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+/* ===============================
+ COLUMN WIDTH CONTROL
+================================= */
+
+/* # column */
+.recent-table th:nth-child(1),
+.recent-table td:nth-child(1) {
+ width: 45px;
+ text-align: center;
+ padding-left: 6px;
+ padding-right: 6px;
+}
+
+/* Short URL */
+.recent-table th:nth-child(2),
+.recent-table td:nth-child(2) {
+ width: 170px;
+}
+
+/* Original URL (main space owner) */
+.recent-table th:nth-child(3),
+.recent-table td:nth-child(3) {
+ width: 45%;
+ min-width: 0;
+}
+
+/* Created */
+.recent-table th:nth-child(4),
+.recent-table td:nth-child(4) {
+ width: 170px;
+}
+
+/* Visits */
+.recent-table th:nth-child(5),
+.recent-table td:nth-child(5) {
+ width: 80px;
+ text-align: center;
+ font-weight: 700;
+ color: var(--accent-2);
+}
+
+/* Actions */
+.recent-table th:nth-child(6),
+.recent-table td:nth-child(6) {
+ width: 120px;
+}
+
+/* ===============================
+ LINKS
+================================= */
+
+.short-code a {
+ color: var(--accent);
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.short-code a:hover {
+ color: var(--accent-2);
+ text-decoration: underline;
+}
+
+/* Original URL truncate */
+.original-url {
+ word-break: break-all;
+}
+
+.original-url a {
+ display: block;
overflow: hidden;
text-overflow: ellipsis;
+ white-space: nowrap;
+ color: var(--text-secondary);
+}
+
+.original-url a:hover {
+ color: var(--accent);
+}
+
+/* Created time */
+.created-time {
+ font-size: 13px;
+ color: var(--muted);
+ white-space: nowrap;
+}
+
+/* ===============================
+ ACTION BUTTONS
+================================= */
+
+.action-col {
+ display: flex;
+ gap: 10px;
+ justify-content: flex-start;
+}
+
+.action-btn {
+ width: 36px;
+ height: 36px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ font-size: 16px;
+ transition: 0.2s ease;
+}
+
+.open-btn {
+ background: #3b82f6;
+ color: #fff;
+}
+
+.delete-btn {
+ background: #ef4444;
+ color: #fff;
+}
+
+/* ===============================
+ DARK MODE
+================================= */
+
+.dark-theme .recent-table th,
+.dark-theme .recent-table td {
+ color: #e5e7eb;
+ border-bottom: 1px solid var(--glass-border);
+}
+
+/* Tablet */
+@media (max-width: 1024px) {
+ .recent-page-container {
+ padding: 0 18px;
+ }
+}
+
+/* Mobile */
+@media (max-width: 768px) {
+ .recent-page-container {
+ padding: 0 12px;
+ }
+}
+
+/* Small phones */
+@media (max-width: 480px) {
+ .recent-page-container {
+ padding: 0 8px;
+ }
}
/* Footer */
-.big-footer {
- background: rgba(255, 255, 255, 0.01);
+footer.big-footer {
+ background: var(--bg);
border-top: 1px solid var(--glass-border);
padding: 4rem 1rem 2rem;
margin-top: 4rem;
@@ -372,6 +676,27 @@ body.light-theme {
color: var(--accent);
}
+/* Dark mode footer adjustments */
+body.dark-theme footer.big-footer {
+ background: #020617 !important;
+ border-top: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+body.dark-theme .footer-col h4 {
+ color: #f3f4f6;
+}
+
+body.dark-theme .footer-col p,
+body.dark-theme .footer-col ul li a,
+body.dark-theme .footer-bottom {
+ color: #cbd5e1;
+}
+
+body.dark-theme .footer-col ul li a:hover,
+body.dark-theme .footer-bottom a {
+ color: #a5f3fc;
+}
+
/* Responsive adjustments */
@media (max-width: 900px) {
.footer-grid {
@@ -413,3 +738,53 @@ body.light-theme {
text-align: center;
}
}
+
+
+
+
+/* allow wrapping */
+.recent-tray .recent-item .original-url,
+.recent-tray .recent-item .original-url a {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+
+ -webkit-line-clamp: 3;
+ /* ⭐ change 2 or 3 lines here */
+ line-clamp: 3;
+
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ white-space: normal;
+ word-break: break-word;
+ overflow-wrap: anywhere;
+}
+
+/* IMPORTANT — remove width restriction */
+.recent-tray .recent-item {
+ min-width: 0;
+ /* allows shrinking inside flex/grid */
+ max-width: 100%;
+}
+
+/* ===============================
+ VIEW HISTORY COLOR BY THEME
+================================= */
+
+body.dark-theme .history-link {
+ color: #ffffff;
+}
+
+body.light-theme .history-link {
+ color: #000000;
+}
+
+.history-link {
+ text-decoration: line;
+ font-weight: 600;
+ transition: 0.2s ease;
+}
+
+.history-link:hover {
+ opacity: 0.7;
+}
\ No newline at end of file
diff --git a/app/static/style.css b/app/static/style.css
index b754687..8dd7671 100644
--- a/app/static/style.css
+++ b/app/static/style.css
@@ -1,815 +1,1063 @@
-html,
-body {
- height: 100%;
- margin: 0;
- font-family: Arial;
- padding: 0;
- font-family: "Poppins", system-ui, Arial, sans-serif;
- background: var(--bg);
- background-size: cover;
- background-position: center;
- background-size: cover;
- background-position: center;
-}
-input {
- width: 70%;
- margin-top: 2px;
- margin-bottom: 2px;
- font-size: 16px;
-}
-.admin-box {
- margin: 120px auto 60px;
- /* space from header + footer */
-}
-.app-layout {
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- margin-top: var(--header-height);
-}
-button {
- padding: 8px;
- margin: 5px;
-}
-.error-box {
- margin-bottom: 15px;
- padding: 10px;
- color: #ff4d4d;
- border-radius: 8px;
- font-weight: 600;
-}
-
-.dark-theme h1 {
- background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff);
- -webkit-background-clip: text;
- background-clip: text;
- -webkit-text-fill-color: transparent;
- text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4);
-}
-
-.dark-theme p {
- background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff);
- -webkit-background-clip: text;
- background-clip: text;
- -webkit-text-fill-color: transparent;
- text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4);
-}
-.dark-theme {
- --bg-overlay: rgba(0, 0, 0, 0.75);
- --glass-bg: rgba(0, 0, 0, 0.4);
- --text-color: #fff;
- --input-bg: rgba(50, 50, 50, 0.8);
- --input-text-color: #fff;
-}
-
-@keyframes pop {
- 0% {
- transform: scale(0.7);
- opacity: 0;
- }
- 100% {
- transform: scale(1);
- opacity: 1;
- }
-}
-/* INPUT CONTAINER */
-.input-field {
- flex: 1 1 700px;
- display: flex;
- align-items: center;
- gap: 12px;
- border-radius: 12px;
- border: 2px solid rgb(6, 0, 0);
- background: transparent; /* IMPORTANT */
- padding: 12px 12px;
-}
-.dark-theme .input-field {
- border-color: #ffffff;
-}
-/* INPUT ITSELF */
-.input-field input[type="text"] {
- width: 100%;
- border: none;
- outline: none;
- background-color: transparent !important;
- background-image: none !important;
- box-shadow: none !important;
- font-size: 23px;
-}
-
-.input-field input {
- color: #000 !important;
-}
-
-.dark-theme .input-field input {
- color: #fff !important;
-}
-
-.input-field input:-webkit-autofill,
-.input-field input:-webkit-autofill:hover,
-.input-field input:-webkit-autofill:focus,
-.input-field input:-webkit-autofill:active {
- -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
- box-shadow: 0 0 0 1000px transparent inset !important;
- background-color: transparent !important;
- background-image: none !important;
- transition: background-color 9999s ease-in-out 0s;
-}
-
-.input-field input:-webkit-autofill {
- -webkit-text-fill-color: #000 !important;
-}
-
-.dark-theme .input-field input:-webkit-autofill {
- -webkit-text-fill-color: #fff !important;
-}
-.input-field input::selection,
-.input-field input::-moz-selection {
- background: transparent;
- color: inherit;
-}
-.short-code {
- color: #0a0000; /* blue like links */
- font-weight: 700;
-}
-
-.app-header {
- position: fixed;
- top: 0;
- left: 0;
- width: 97%;
- height: 55px;
- background: white;
- display: flex;
- align-items: center;
- padding: 0 28px;
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
- z-index: 1000;
-}
-
-/* Dark mode */
-.dark-theme .app-header {
- background: linear-gradient(180deg, #0b1220, #050b14);
-}
-
-footer {
- margin-top: 0;
-}
-
-body.dark-theme,
-body.dark-theme .page,
-body.dark-theme main,
-body.dark-theme section {
- background: #0f1720 !important;
-}
-
-.header-left {
- display: flex;
- align-items: center;
- gap: 12px;
-}
-
-.app-logo {
- width: 36px;
- height: 36px;
- background: linear-gradient(135deg, #2563eb, #5ab9ff);
- color: white;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 18px;
-}
-
-.app-name {
- font-size: 20px;
- font-weight: 700;
- color: #111827;
-}
-
-.dark-theme .app-name {
- color: #f8fafc;
-}
-
-.header-nav {
- position: absolute;
- left: 50%;
- transform: translateX(-50%);
- display: flex;
- gap: 26px;
-}
-
-.nav-link {
- text-decoration: none;
- color: #111827;
- font-weight: 500;
- position: relative;
-}
-
-.dark-theme .nav-link {
- color: #e5e7eb;
-}
-
-.nav-link:hover {
- color: #2563eb;
-}
-
-.nav-link.active::after {
- content: "";
- position: absolute;
- bottom: -6px;
- left: 0;
- width: 100%;
- height: 2px;
- background: #111827;
-}
-
-.dark-theme .nav-link.active::after {
- background: #f8fafc;
-}
-
-.header-right {
- margin-left: auto;
- display: flex;
- align-items: center;
-}
-
-:root {
- --header-height: 55px;
- --bg: #eefaf8;
- --card: rgba(255, 255, 255, 0.95);
- --muted: #7b8b8a;
- --accent-1: #5ab9ff;
- --accent-2: #4cb39f;
- --accent-grad: linear-gradient(90deg, #4cb39f, #5ab9ff);
- --success: #2fb06e;
- --glass: rgba(255, 255, 255, 0.85);
-}
-
-.dark-theme {
- --bg-overlay: rgba(0, 0, 0, 0.75);
- --glass-bg: rgba(0, 0, 0, 0.4);
- --text-color: #f3f3f3;
- --input-bg: rgba(11, 10, 10, 0.8);
- --button-bg: linear-gradient(90deg, #4444ff, #2266ff);
- --recent-bg: rgba(255, 255, 255, 0.1);
-}
-
-/* Preserve your dark theme variables too */
-body.dark-theme {
- --bg: #0f1720;
- --card: rgba(10, 14, 18, 0.92);
- --muted: #9aa7a6;
-}
-
-.page {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 1rem;
- padding: 2rem;
- min-height: 80vh;
-}
-
-.theme-toggle {
- background: transparent;
- border: none;
- cursor: pointer;
- padding: 8px;
- border-radius: 8px;
- font-weight: 700;
- background: var(--card);
-}
-
-/* Hero */
-.hero {
- width: 100%;
- max-width: 1100px;
- background: transparent;
- text-align: center;
- padding: 10px;
-}
-
-.hero h1 {
- margin: 10px 0 14px;
- font-size: 36px;
- line-height: 1.05;
- color: #000606;
-}
-
-.hero p {
- margin: var(--bg-overlay);
- color: var(--muted);
- max-width: 820px;
- margin-left: auto;
- margin-right: auto;
- color: #000606;
-}
-
-/* Main card & input */
-.card {
- width: 100%;
- max-width: 1100px;
- background: var(--card);
- border-radius: 14px;
- padding: 15px;
- box-shadow: 0 18px 50px rgba(8, 24, 24, 0.06);
-}
-
-.cta {
- min-width: 220px;
- padding: 14px 22px;
- border-radius: 12px;
- border: none;
- color: rgb(12, 1, 1);
- font-weight: 700;
- cursor: pointer;
- background: var(--accent-grad);
- box-shadow: 0 12px 28px rgba(77, 163, 185, 0.12);
-}
-
-.small-action {
- display: flex;
- align-items: center;
- gap: 8px;
- color: var(--muted);
- margin-top: 10px;
-}
-
-.result {
- margin-top: 26px;
- background: white;
- border-radius: 12px;
- padding: 20px;
- border: 1px solid rgba(22, 60, 55, 0.03);
- box-shadow: 0 8px 28px rgba(7, 20, 20, 0.03);
-}
-
-.result-header {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 12px;
-}
-
-.result-header .dot {
- width: 30px;
- height: 30px;
- background: var(--success);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- color: white;
- font-weight: 700;
-}
-
-.short-actions {
- display: flex;
- justify-content: center;
- align-items: center;
- gap: 8px;
- padding: 10px 14px;
- border-radius: 12px;
- background: linear-gradient(180deg, rgba(75, 194, 176, 0.06), rgba(94, 207, 255, 0.04));
-}
-
-.short-box input {
- align-items: center;
- padding: 10px;
- font-size: 15px;
-}
-
-.btn-copy {
- border: none;
- padding: 10px 14px;
- border-radius: 8px;
- color: white;
- font-weight: 700;
- cursor: pointer;
-}
-
-.btn-share {
- background: #f2f5f5;
- border: none;
- padding: 10px 14px;
- border-radius: 8px;
- color: #0b2b2a;
- font-weight: 700;
- cursor: pointer;
- margin-left: 6px;
-}
-
-.meta-row {
- align-items: center;
- justify-content: center;
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 2px;
- padding: 16px;
- margin-top: 1px;
- align-items: top;
- color: black;
-}
-.result-body {
- margin-top: 30px;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
-}
-
-.qr-block {
- text-align: center;
- padding-top: 8px;
-}
-
-.qr-block img {
- height: 15rem;
- align-items: center;
- aspect-ratio: 1;
- box-shadow: 0 10px 20px rgba(10, 20, 30, 0.06);
- outline: 2px solid green;
- outline-offset: 4px;
-}
-
-.download-qr {
- display: inline-block;
- margin-top: 12px;
- text-decoration: none;
- color: var(--accent-1);
- font-weight: 700;
-}
-
-.action-row {
- display: flex;
- justify-content: right;
- align-items: right;
-}
-
-.action-secondary {
- background: #f6fbfb;
- border: 1px solid rgba(0, 0, 0, 0.03);
- border-radius: 10px;
- cursor: pointer;
- font-weight: 700;
-}
-
-/* Force Generate QR to stay on one line */
-.qr-inline {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- white-space: nowrap;
-}
-
-.qr-inline input {
- margin: 0;
-}
-
-/* Responsive */
-@media (max-width: 880px) {
- .input-row {
- flex-direction: column;
- }
-
- .cta {
- width: 100%;
- }
-
- .meta-row {
- grid-template-columns: 1fr;
- }
-}
-.result-title {
- font-weight: 700;
- color: #0e34f6;
-}
-
-.dark-theme .result-title {
- color: #150cff;
-}
-
-footer {
- min-height: auto;
-}
-
-.app-footer {
- background: white;
- color: #e5e7eb;
- padding: 8px 10px;
- margin-top: auto;
- position: relative;
-}
-.dark-theme .app-footer {
- background: linear-gradient(180deg, #0b1220, #050b14);
-}
-
-.footer-container {
- margin: auto;
- display: flex;
- gap: 60px;
- justify-content: space-between;
- flex-wrap: wrap;
-}
-
-.footer-brand {
- max-width: 420px;
-}
-
-.footer-logo {
- width: 42px;
- height: 42px;
- background: linear-gradient(135deg, #2563eb, #5ab9ff);
- border-radius: 14px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22px;
-}
-.dark-theme .footer-brand h3,
-.dark-theme .footer-brand p,
-.dark-theme .footer-col h4,
-.dark-theme .app-footer a,
-.dark-theme .footer-bottom {
- color: #f8fafc;
-}
-
-.footer-brand h3 {
- margin: 0;
- color: #000000;
- font-size: 22px;
- font-weight: 700;
-}
-
-.footer-brand p {
- margin-top: 8px;
- color: #000000;
- line-height: 1.6;
- font-size: 14px;
-}
-
-/* MAIN CONTENT */
-
-/* FOOTER */
-.app-footer {
- margin-top: auto;
-}
-
-/* GitHub button */
-.github-btn {
- display: inline-flex;
- align-items: center;
- gap: 2px;
- margin-top: 1px;
- padding: 10px 16px;
- border-radius: 8px;
- background: rgba(255, 255, 255, 0.06);
- color: #000000;
- text-decoration: none;
- font-weight: 600;
- transition: all 0.25s ease;
-}
-
-.github-btn:hover {
- background: black(11, 1, 1);
- transform: translateY(-2px);
-}
-
-.footer-links {
- display: flex;
- gap: 80px;
- flex-wrap: wrap;
-}
-
-.footer-col h4 {
- margin-bottom: 14px;
- font-size: 16px;
- color: #000000;
- font-weight: 700;
-}
-
-.footer-col a {
- display: block;
- text-decoration: none;
- color: #000000;
- margin-bottom: 10px;
- font-size: 14px;
- transition: color 0.2s ease;
-}
-
-.footer-col a:hover {
- text-decoration: underline;
-}
-
-/* Bottom */
-.footer-bottom {
- margin-top: 10px;
- border-top: 1px solid rgba(255, 255, 255, 0.153);
- padding-top: 8px;
- padding-bottom: 1px;
- text-align: center;
- font-size: 14px;
- color: #080808;
-}
-.footer-bottom a {
- color: #030000;
- font-weight: 600;
- text-decoration: none;
-}
-
-.footer-bottom a:hover {
- text-decoration: underline;
-}
-
-/* Responsive */
-@media (max-width: 768px) {
- .footer-container {
- flex-direction: column;
- gap: 40px;
- }
-
- .footer-links {
- gap: 40px;
- }
-}
-/* REMOVE white line above footer in dark mode */
-footer {
- margin-top: 0 !important;
-}
-.recent-table-wrapper {
- margin-top: 20px;
- width: 100%;
- overflow-x: auto;
-}
-
-.recent-table {
- width: 100%;
- border-collapse: collapse;
- border-radius: 12px;
- overflow: hidden;
-}
-
-.recent-table thead {
- background: rgb(0, 0, 0);
-}
-.recent-table th {
- color: rgb(0, 0, 0);
- padding: 8px 14px;
- text-align: left;
- font-size: 16px;
-}
-.short-code a {
- color: #2563eb;
- font-weight: 600;
- text-decoration: none;
-}
-
-.short-code a:hover {
- color: #1d4ed8;
- text-decoration: underline;
-}
-.recent-table td {
- color: rgb(34, 48, 77);
- padding: 10px 14px;
- text-align: left;
- font-size: 14px;
-}
-
-.created-time {
- font-size: 14px;
- color: #374151;
- white-space: nowrap;
-}
-
-.time-ago {
- color: #374151;
- font-size: 13px;
- margin-left: 2px;
-}
-.recent-table th {
- font-weight: 700;
-}
-
-.recent-table tbody tr,
-th {
- background: rgb(255, 255, 255);
- border-bottom: 1px solid rgb(0, 0, 0);
-}
-.dark-theme.recent-table tbody tr,
-td {
- background: rgba(255, 255, 255, 0.04);
- border-bottom: 1px solid rgb(0, 0, 0);
-}
-.recent-table tbody tr:hover {
- background: rgb(196, 196, 196);
-}
-
-/* Short code */
-.short-code {
- font-weight: 700;
-}
-
-.original-url {
- color: #22c55e;
- word-break: break-all;
-}
-
-/* Action buttons */
-.action-col {
- display: flex;
- gap: 10px;
-}
-
-.action-btn {
- width: 36px;
- height: 36px;
- border-radius: 10px;
- display: flex;
- align-items: center;
- justify-content: center;
- text-decoration: none;
- font-size: 16px;
- transition: 0.2s ease;
-}
-
-.open-btn {
- background: #3b82f6;
- color: #fff;
-}
-
-.delete-btn {
- background: #ef4444;
- color: #fff;
-}
-
-.recent-table-wrapper {
- margin-bottom: 20px;
-}
-/* =========================
- Coming Soon Page
-========================= */
-
-.coming-soon-page {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 120px 20px 60px;
-}
-
-.coming-soon-card {
- max-width: 520px;
- width: 100%;
- background: var(--card);
- border-radius: 16px;
- padding: 50px 40px;
- text-align: center;
- box-shadow: 0 20px 50px rgba(0, 0, 0, 0.08);
-}
-
-.coming-icon {
- font-size: 48px;
- margin-bottom: 18px;
-}
-
-.coming-soon-card h1 {
- font-size: 34px;
- margin-bottom: 14px;
- color: #000;
-}
-
-.dark-theme .coming-soon-card h1 {
- color: #fff;
-}
-
-.coming-soon-card p {
- font-size: 15px;
- color: var(--muted);
- line-height: 1.6;
- margin-bottom: 28px;
-}
-
-.coming-btn {
- display: inline-block;
- padding: 12px 22px;
- border-radius: 10px;
- background: var(--accent-grad);
- color: #fff;
- font-weight: 700;
- text-decoration: none;
- transition: 0.25s ease;
-}
-
-.coming-btn:hover {
- transform: scale(1.05);
- box-shadow: 0 12px 28px rgba(77, 163, 185, 0.25);
-}
-.info-box {
- margin-bottom: 15px;
- padding: 10px;
- color: #0e34f6;
- border-radius: 8px;
- font-weight: 700;
-}
+html,
+body {
+ height: 100%;
+ margin: 0;
+ font-family: Arial;
+ padding: 0;
+ font-family: "Poppins", system-ui, Arial, sans-serif;
+ background: var(--bg);
+ background-size: cover;
+ background-position: center;
+ background-size: cover;
+ background-position: center;
+}
+
+input {
+ width: 70%;
+ margin-top: 2px;
+ margin-bottom: 2px;
+ font-size: 16px;
+}
+
+.admin-box {
+ margin: 120px auto 60px;
+ /* space from header + footer */
+}
+
+.app-layout {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ margin-top: var(--header-height);
+}
+
+button {
+ padding: 8px;
+ margin: 5px;
+}
+
+.error-box {
+ margin-bottom: 15px;
+ padding: 10px;
+ color: #ff4d4d;
+ border-radius: 8px;
+ font-weight: 600;
+}
+
+.dark-theme h1 {
+ background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4);
+}
+
+.dark-theme p {
+ background: linear-gradient(90deg, #ffffff, #dddddd, #ffffff);
+ -webkit-background-clip: text;
+ background-clip: text;
+ -webkit-text-fill-color: transparent;
+ text-shadow: 0px 0px 10px rgba(255, 255, 255, 0.4);
+}
+
+.dark-theme {
+ --bg-overlay: rgba(0, 0, 0, 0.75);
+ --glass-bg: rgba(0, 0, 0, 0.4);
+ --text-color: #fff;
+ --input-bg: rgba(50, 50, 50, 0.8);
+ --input-text-color: #fff;
+}
+
+@keyframes pop {
+ 0% {
+ transform: scale(0.7);
+ opacity: 0;
+ }
+
+ 100% {
+ transform: scale(1);
+ opacity: 1;
+ }
+}
+
+/* INPUT CONTAINER */
+.input-field {
+ flex: 1 1 700px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ border-radius: 12px;
+ border: 2px solid rgb(6, 0, 0);
+ background: transparent;
+ /* IMPORTANT */
+ padding: 12px 12px;
+}
+
+.dark-theme .input-field {
+ border-color: #ffffff;
+}
+
+/* INPUT ITSELF */
+.input-field input[type="text"] {
+ width: 100%;
+ border: none;
+ outline: none;
+ background-color: transparent !important;
+ background-image: none !important;
+ box-shadow: none !important;
+ font-size: 23px;
+}
+
+.input-field input {
+ color: #000 !important;
+}
+
+.dark-theme .input-field input {
+ color: #fff !important;
+}
+
+.input-field input:-webkit-autofill,
+.input-field input:-webkit-autofill:hover,
+.input-field input:-webkit-autofill:focus,
+.input-field input:-webkit-autofill:active {
+ -webkit-box-shadow: 0 0 0 1000px transparent inset !important;
+ box-shadow: 0 0 0 1000px transparent inset !important;
+ background-color: transparent !important;
+ background-image: none !important;
+ transition: background-color 9999s ease-in-out 0s;
+}
+
+.input-field input:-webkit-autofill {
+ -webkit-text-fill-color: #000 !important;
+}
+
+.dark-theme .input-field input:-webkit-autofill {
+ -webkit-text-fill-color: #fff !important;
+}
+
+.input-field input::selection,
+.input-field input::-moz-selection {
+ background: transparent;
+ color: inherit;
+}
+
+.short-code {
+ color: #0a0000;
+ /* blue like links */
+ font-weight: 700;
+}
+
+footer {
+ margin-top: 0;
+}
+
+body.dark-theme,
+body.dark-theme .page,
+body.dark-theme main,
+body.dark-theme section {
+ background: #0f1720 !important;
+}
+
+/*:root {
+ --header-height: 55px;
+ --bg: #eefaf8;
+ --card: rgba(255, 255, 255, 0.95);
+ --muted: #7b8b8a;
+ --accent-1: #5ab9ff;
+ --accent-2: #4cb39f;
+ --accent-grad: linear-gradient(90deg, #4cb39f, #5ab9ff);
+ --success: #2fb06e;
+ --glass: rgba(255, 255, 255, 0.85);
+}*/
+
+:root {
+ /* Background */
+ --bg: #eefaf8;
+ --card: rgba(255, 255, 255, 0.85);
+
+ /* Text */
+ --text-color: #1f2937;
+ --text-muted: #6b7280;
+
+ /* Borders */
+ --glass-border: rgba(0, 0, 0, 0.08);
+
+ /* Accent */
+ --accent-1: #5ab9ff;
+ --accent-2: #4cb39f;
+
+ /* Shadow */
+ --card-shadow: 0 20px 60px rgba(0, 0, 0, 0.12);
+ --text-primary: var(--text-color);
+ --text-secondary: var(--text-muted);
+ --accent: var(--accent-1);
+}
+
+.dark-theme {
+ --bg-overlay: rgba(0, 0, 0, 0.75);
+ --glass-bg: rgba(0, 0, 0, 0.4);
+ --text-color: #f3f3f3;
+ --input-bg: rgba(11, 10, 10, 0.8);
+ --button-bg: linear-gradient(90deg, #4444ff, #2266ff);
+ --recent-bg: rgba(255, 255, 255, 0.1);
+}
+
+/* Preserve your dark theme variables too */
+body.dark-theme {
+ --bg: #0f1720;
+ --card: rgba(20, 25, 30, 0.75);
+
+ --text-color: #e5e7eb;
+ --text-muted: #9aa7a6;
+
+ --glass-border: rgba(255, 255, 255, 0.08);
+
+ --card-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
+}
+
+.page {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
+ padding: 2rem;
+ min-height: 80vh;
+}
+
+.theme-toggle {
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ padding: 8px;
+ border-radius: 8px;
+ font-weight: 700;
+ background: var(--card);
+}
+
+/* Hero */
+.hero {
+ width: 100%;
+ max-width: 1100px;
+ background: transparent;
+ text-align: center;
+ padding: 10px;
+}
+
+.hero h1 {
+ margin: 10px 0 14px;
+ font-size: 36px;
+ line-height: 1.05;
+ color: #000606;
+}
+
+.hero p {
+ margin: var(--bg-overlay);
+ color: var(--muted);
+ max-width: 820px;
+ margin-left: auto;
+ margin-right: auto;
+ color: #000606;
+}
+
+/* Main card & input */
+.card {
+ width: 100%;
+ max-width: 1100px;
+ background: var(--card);
+ border-radius: 14px;
+ padding: 15px;
+ box-shadow: 0 18px 50px rgba(8, 24, 24, 0.06);
+}
+
+.cta {
+ min-width: 220px;
+ padding: 14px 22px;
+ border-radius: 12px;
+ border: none;
+ color: rgb(12, 1, 1);
+ font-weight: 700;
+ cursor: pointer;
+ background: var(--accent-grad);
+ box-shadow: 0 12px 28px rgba(77, 163, 185, 0.12);
+}
+
+.small-action {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--muted);
+ margin-top: 10px;
+}
+
+.result {
+ margin-top: 26px;
+ background: white;
+ border-radius: 12px;
+ padding: 20px;
+ border: 1px solid rgba(22, 60, 55, 0.03);
+ box-shadow: 0 8px 28px rgba(7, 20, 20, 0.03);
+}
+
+.result-header {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.result-header .dot {
+ width: 30px;
+ height: 30px;
+ background: var(--success);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-weight: 700;
+}
+
+.short-actions {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 14px;
+ border-radius: 12px;
+ background: linear-gradient(180deg, rgba(75, 194, 176, 0.06), rgba(94, 207, 255, 0.04));
+}
+
+.short-box input {
+ align-items: center;
+ padding: 10px;
+ font-size: 15px;
+}
+
+.btn-copy {
+ border: none;
+ padding: 10px 14px;
+ border-radius: 8px;
+ color: white;
+ font-weight: 700;
+ cursor: pointer;
+}
+
+.btn-share {
+ background: #f2f5f5;
+ border: none;
+ padding: 10px 14px;
+ border-radius: 8px;
+ color: #0b2b2a;
+ font-weight: 700;
+ cursor: pointer;
+ margin-left: 6px;
+}
+
+.meta-row {
+ align-items: center;
+ justify-content: center;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 2px;
+ padding: 16px;
+ margin-top: 1px;
+ align-items: top;
+ color: black;
+}
+
+.result-body {
+ margin-top: 30px;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+}
+
+.qr-block {
+ text-align: center;
+ padding-top: 8px;
+}
+
+.qr-block img {
+ height: 15rem;
+ align-items: center;
+ aspect-ratio: 1;
+ box-shadow: 0 10px 20px rgba(10, 20, 30, 0.06);
+ outline: 2px solid green;
+ outline-offset: 4px;
+}
+
+.download-qr {
+ display: inline-block;
+ margin-top: 12px;
+ text-decoration: none;
+ color: var(--accent-1);
+ font-weight: 700;
+}
+
+.action-row {
+ display: flex;
+ justify-content: right;
+ align-items: right;
+}
+
+.action-secondary {
+ background: #f6fbfb;
+ border: 1px solid rgba(0, 0, 0, 0.03);
+ border-radius: 10px;
+ cursor: pointer;
+ font-weight: 700;
+}
+
+/* Force Generate QR to stay on one line */
+.qr-inline {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ white-space: nowrap;
+}
+
+.qr-inline input {
+ margin: 0;
+}
+
+/* Responsive */
+@media (max-width: 880px) {
+ .input-row {
+ flex-direction: column;
+ }
+
+ .cta {
+ width: 100%;
+ }
+
+ .meta-row {
+ grid-template-columns: 1fr;
+ }
+}
+
+.result-title {
+ font-weight: 700;
+ color: #0e34f6;
+}
+
+.dark-theme .result-title {
+ color: #150cff;
+}
+
+footer {
+ min-height: auto;
+}
+
+/* /* ===============================
+ MODERN UI STYLE FOOTER (VISIT PAGE)
+================================= */
+
+.app-footer {
+ background: rgba(255, 255, 255, 0.02);
+ backdrop-filter: blur(16px);
+ border-top: 1px solid var(--glass-border);
+ padding: 2.5rem 1rem 1.2rem;
+ /* reduced space */
+ margin-top: 40px;
+}
+
+/* Container */
+.footer-container {
+ max-width: 1100px;
+ margin: 0 auto;
+
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr 1fr;
+ gap: 1.8rem;
+}
+
+/* Footer columns */
+.footer-col h4 {
+ font-size: 14px;
+ font-weight: 700;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ margin-bottom: 16px;
+ color: var(--text-primary);
+}
+
+.footer-col p {
+ color: var(--text-secondary);
+ font-size: 14px;
+ line-height: 1.6;
+}
+
+.footer-col ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.footer-col ul li {
+ margin-bottom: 10px;
+}
+
+.footer-col ul li a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-size: 14px;
+ transition: 0.2s ease;
+}
+
+.footer-col ul li a:hover {
+ color: var(--accent);
+}
+
+/* Footer bottom */
+.footer-bottom {
+ /* margin: 3rem auto 0;
+ padding-top: 20px;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ border-top: 1px solid var(--glass-border);
+ font-size: 14px;
+ color: var(--text-secondary); */
+
+ max-width: 1200px;
+ margin: 2rem auto 0;
+ padding-top: 1rem;
+ border-top: 1px solid var(--glass-border);
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
+ color: var(--text-secondary);
+ font-size: 0.8rem;
+
+}
+
+/* Version + GitHub area */
+.footer-bottom a {
+ color: var(--accent);
+ text-decoration: none;
+ transition: 0.2s ease;
+}
+
+.footer-bottom a:hover {
+ opacity: 0.8;
+}
+
+/* ===============================
+ DARK MODE SUPPORT
+================================= */
+
+.dark-theme .app-footer {
+ background: #0a0a0c;
+ backdrop-filter: blur(16px);
+ border-top: 1px solid rgba(255, 255, 255, 0.06);
+}
+
+.dark-theme .footer-col h4 {
+ color: #f3f4f6;
+}
+
+.dark-theme .footer-col p,
+.dark-theme .footer-col ul li a,
+.dark-theme .footer-bottom {
+ color: #cbd5e1;
+}
+
+.dark-theme .footer-col ul li a:hover,
+.dark-theme .footer-bottom a {
+ color: var(--accent-2);
+}
+
+/* ===============================
+ MOBILE RESPONSIVE
+================================= */
+
+@media (max-width: 900px) {
+ .footer-container {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .footer-container {
+ grid-template-columns: 1fr;
+ }
+
+ .footer-bottom {
+ flex-direction: column;
+ gap: 10px;
+ text-align: center;
+ }
+}
+
+/* REMOVE white line above footer in dark mode */
+footer {
+ margin-top: 0 !important;
+}
+
+*/
+/* =====================================
+ BIG FOOTER STYLE (FOR FIRST PAGE)
+ Using existing class names
+===================================== */
+
+/*Footer wrapper
+.app-footer {
+ background: rgba(255, 255, 255, 0.01);
+ border-top: 1px solid var(--glass-border);
+ padding: 4rem 1rem 2rem;
+ margin-top: 4rem;
+}
+
+/* Grid container */
+.footer-container {
+ max-width: 1200px;
+ margin: 0 auto;
+
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr 1fr;
+ gap: 2rem;
+}
+
+/* Brand column (first column) */
+.footer-container>div:first-child h4 {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+}
+
+.footer-container>div:first-child p {
+ color: var(--text-secondary);
+ line-height: 1.6;
+ max-width: 320px;
+}
+
+.footer-brand h3 {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+}
+
+.footer-brand p {
+ color: var(--text-secondary);
+ line-height: 1.6;
+ max-width: 320px;
+}
+
+/* Other footer columns */
+.footer-col h4 {
+ font-size: 0.85rem;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ margin-bottom: 1rem;
+ color: var(--text-primary);
+}
+
+.footer-col ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.footer-col ul li {
+ margin-bottom: 0.8rem;
+}
+
+.footer-col ul li a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-size: 0.9rem;
+ transition: color 0.2s ease;
+}
+
+.footer-col ul li a:hover {
+ color: var(--accent);
+}
+
+/* Bottom row */
+.footer-bottom {
+ max-width: 1200px;
+ margin: 2rem auto 0;
+ padding-top: 1rem;
+ border-top: 1px solid var(--glass-border);
+
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
+
+ color: var(--text-secondary);
+ font-size: 0.8rem;
+}
+
+/* Footer links inside bottom */
+.footer-bottom a {
+ color: inherit;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.footer-bottom a:hover {
+ color: var(--accent);
+}
+
+/* Responsive */
+@media (max-width: 900px) {
+ .footer-container {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+@media (max-width: 600px) {
+ .footer-container {
+ grid-template-columns: 1fr;
+ }
+
+ .footer-bottom {
+ flex-direction: column;
+ gap: 1rem;
+ text-align: center;
+ }
+}
+
+*/
+
+/* Footer */
+.big-footer {
+ background: rgba(255, 255, 255, 0.01);
+ border-top: 1px solid var(--glass-border);
+ padding: 4rem 1rem 2rem;
+ margin-top: 4rem;
+}
+
+.footer-grid {
+ max-width: 1200px;
+ margin: 0 auto;
+ display: grid;
+ grid-template-columns: 2fr 1fr 1fr 1fr;
+ gap: 2rem;
+}
+
+.footer-brand h3 {
+ font-size: 1.5rem;
+ margin-bottom: 1rem;
+}
+
+.footer-brand p {
+ color: var(--text-secondary);
+ line-height: 1.6;
+ max-width: 320px;
+}
+
+.footer-col h4 {
+ font-size: 0.85rem;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ margin-bottom: 1rem;
+ color: var(--text-primary);
+}
+
+.footer-col ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+.footer-col ul li {
+ margin-bottom: 0.8rem;
+}
+
+.footer-col ul li a {
+ color: var(--text-secondary);
+ text-decoration: none;
+ font-size: 0.9rem;
+ transition: color 0.2s;
+}
+
+.footer-col ul li a:hover {
+ color: var(--accent);
+}
+
+.footer-bottom {
+ max-width: 1200px;
+ margin: 2rem auto 0;
+ padding-top: 1rem;
+ border-top: 1px solid var(--glass-border);
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ align-items: center;
+ color: var(--text-secondary);
+ font-size: 0.8rem;
+}
+
+.footer-meta {
+ display: flex;
+ gap: 1rem;
+}
+
+.footer-meta a {
+ color: inherit;
+ text-decoration: none;
+}
+
+.footer-meta a:hover {
+ color: var(--accent);
+}
+
+/* Responsive adjustments */
+@media (max-width: 900px) {
+ .footer-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+}
+
+@media (max-width: 700px) {
+ .result-card {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .result-actions {
+ align-items: flex-start;
+ }
+
+ .recent-item {
+ min-width: 180px;
+ }
+}
+
+@media (max-width: 600px) {
+ .hero-input-card h1 {
+ font-size: 2rem;
+ }
+
+ .short-url a {
+ font-size: 1.2rem;
+ }
+
+ .footer-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .footer-bottom {
+ flex-direction: column;
+ gap: 1rem;
+ text-align: center;
+ }
+}
+
+/*===============================
+ MODERN GLASS RECENT TABLE
+================================ */
+
+.recent-page-container {
+ width: 100%;
+ max-width: 1100px;
+ margin: 30px auto;
+ padding: 28px;
+
+ background: var(--card);
+ backdrop-filter: blur(20px);
+
+ border: 1px solid var(--glass-border);
+ border-radius: 20px;
+
+ box-shadow: var(--card-shadow);
+ color: var(--text-color);
+
+ transition: background 0.3s ease, border 0.3s ease;
+}
+
+.recent-table-wrapper {
+ margin-top: 20px;
+ width: 100%;
+ overflow-x: auto;
+}
+
+/* Table */
+.recent-table {
+ width: 100%;
+ border-collapse: collapse;
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+/* Header */
+.recent-table thead {
+ background: var(--glass);
+}
+
+.recent-table th {
+ padding: 8px 14px;
+ text-align: left;
+ font-size: 13px;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ font-weight: 700;
+ color: var(--muted);
+ border-bottom: 1px solid var(--glass-border);
+}
+
+/* Body cells */
+.recent-table td {
+ padding: 14px;
+ font-size: 14px;
+ color: var(--text-primary);
+ border-bottom: 1px solid var(--glass-border);
+ transition: 0.25s ease;
+}
+
+/* Row hover */
+.recent-table tbody tr:hover {
+ background: rgba(255, 255, 255, 0.05);
+}
+
+/* Short link */
+.short-code a {
+ color: var(--accent);
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.short-code a:hover {
+ color: var(--accent-2);
+ text-decoration: underline;
+}
+
+/* Original URL */
+.original-url {
+ word-break: break-all;
+}
+
+.original-url a {
+ color: var(--text-secondary);
+ text-decoration: none;
+}
+
+.original-url a:hover {
+ color: var(--accent);
+}
+
+/* Created time */
+.created-time {
+ font-size: 13px;
+ color: var(--muted);
+ white-space: nowrap;
+}
+
+/* Visit count highlight */
+.recent-table td:nth-child(5) {
+ font-weight: 700;
+ color: var(--accent-2);
+}
+
+/* Dark mode adjustments */
+.dark-theme .recent-table th,
+.dark-theme .recent-table td {
+ color: #e5e7eb;
+ border-bottom: 1px solid var(--glass-border);
+}
+
+/* Action buttons */
+.action-col {
+ display: flex;
+ gap: 10px;
+}
+
+.action-btn {
+ width: 36px;
+ height: 36px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-decoration: none;
+ font-size: 16px;
+ transition: 0.2s ease;
+}
+
+.open-btn {
+ background: #3b82f6;
+ color: #fff;
+}
+
+.delete-btn {
+ background: #ef4444;
+ color: #fff;
+}
+
+.recent-table-wrapper {
+ margin-bottom: 20px;
+}
+
+
+/* =========================
+ Coming Soon Page
+========================= */
+
+.coming-soon-page {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 120px 20px 60px;
+}
+
+.coming-soon-card {
+ max-width: 520px;
+ width: 100%;
+ background: var(--card);
+ border-radius: 16px;
+ padding: 50px 40px;
+ text-align: center;
+ box-shadow: 0 20px 50px rgba(0, 0, 0, 0.08);
+}
+
+.coming-icon {
+ font-size: 48px;
+ margin-bottom: 18px;
+}
+
+.coming-soon-card h1 {
+ font-size: 34px;
+ margin-bottom: 14px;
+ color: #000;
+}
+
+.dark-theme .coming-soon-card h1 {
+ color: #fff;
+}
+
+.coming-soon-card p {
+ font-size: 15px;
+ color: var(--muted);
+ line-height: 1.6;
+ margin-bottom: 28px;
+}
+
+.coming-btn {
+ display: inline-block;
+ padding: 12px 22px;
+ border-radius: 10px;
+ background: var(--accent-grad);
+ color: #fff;
+ font-weight: 700;
+ text-decoration: none;
+ transition: 0.25s ease;
+}
+
+.coming-btn:hover {
+ transform: scale(1.05);
+ box-shadow: 0 12px 28px rgba(77, 163, 185, 0.25);
+}
+
+.info-box {
+ margin-bottom: 15px;
+ padding: 10px;
+ color: #0e34f6;
+ border-radius: 8px;
+ font-weight: 700;
+}
\ No newline at end of file
diff --git a/app/templates/404.html b/app/templates/404.html
new file mode 100644
index 0000000..0825534
--- /dev/null
+++ b/app/templates/404.html
@@ -0,0 +1,51 @@
+{% block content %}
+
+
+
+
+ Page Not Found
+
+
+
+
+
+
🚫 Page not found
+
The page you are looking for does not exist.
+
+
+ ← Go Back
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/templates/coming-soon.html b/app/templates/coming-soon.html
deleted file mode 100644
index 1a5e5eb..0000000
--- a/app/templates/coming-soon.html
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
-
-
-
-
- Coming-soon
-
-
-
-
-
-
-
-
-
-
-
-
🚧
-
Coming Soon
-
- This feature is under active development.
-
-
-
← Back to Home
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/templates/footer.html b/app/templates/footer.html
new file mode 100644
index 0000000..07569aa
--- /dev/null
+++ b/app/templates/footer.html
@@ -0,0 +1,42 @@
+
\ No newline at end of file
diff --git a/app/templates/header.html b/app/templates/header.html
index c7b19b1..0fa001c 100644
--- a/app/templates/header.html
+++ b/app/templates/header.html
@@ -1,11 +1,28 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Recent Shortened URLs
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/templates/recent.html b/app/templates/recent.html
index 5d89d19..fbe52c7 100644
--- a/app/templates/recent.html
+++ b/app/templates/recent.html
@@ -1,218 +1,77 @@
-
-
-
-