{{ message }}
+ +diff --git a/app/static/brand.js b/app/static/brand.js new file mode 100644 index 0000000..c971b62 --- /dev/null +++ b/app/static/brand.js @@ -0,0 +1,54 @@ +(function () { + const root = document.documentElement; + const KEY = "otb-theme"; + + function applyTheme(theme) { + const resolved = theme === "light" ? "light" : "dark"; + root.setAttribute("data-theme", resolved); + document.body.classList.toggle("otb-dark", resolved === "dark"); + document.body.classList.toggle("otb-light", resolved === "light"); + + const toggle = document.getElementById("otbThemeToggle") || document.getElementById("themeToggle"); + if (toggle) toggle.checked = resolved === "light"; + } + + function savedTheme() { + try { + return localStorage.getItem(KEY) || "dark"; + } catch (e) { + return "dark"; + } + } + + function saveTheme(theme) { + try { + localStorage.setItem(KEY, theme); + } catch (e) {} + } + + function setupMenu() { + const btn = document.getElementById("otbMenuToggle"); + const nav = document.getElementById("otbNavWrap"); + if (!btn || !nav) return; + + btn.addEventListener("click", function () { + const open = nav.classList.toggle("otb-nav-open"); + btn.setAttribute("aria-expanded", open ? "true" : "false"); + }); + } + + document.addEventListener("DOMContentLoaded", function () { + applyTheme(savedTheme()); + + const toggle = document.getElementById("otbThemeToggle") || document.getElementById("themeToggle"); + if (toggle) { + toggle.addEventListener("change", function () { + const next = toggle.checked ? "light" : "dark"; + saveTheme(next); + applyTheme(next); + }); + } + + setupMenu(); + }); +})(); diff --git a/app/static/css/brand.css b/app/static/css/brand.css new file mode 100644 index 0000000..2d1a46e --- /dev/null +++ b/app/static/css/brand.css @@ -0,0 +1,576 @@ +/* ===== OTB shared theme system ===== */ + +:root{ + --otb-bg-main:#050b19; + --otb-bg-grad-1:#1f2a57; + --otb-bg-grad-2:#08111f; + --otb-bg-grad-3:#143c2d; + --otb-panel:#0b1330; + --otb-text:#f1f5ff; + --otb-muted:#c5cde4; + --otb-link:#9fd2ff; + --otb-accent:#62f0cf; + --otb-border:rgba(255,255,255,0.08); + --otb-shadow:0 16px 40px rgba(0,0,0,0.28); +} + +html[data-theme="light"]{ + --otb-bg-main:#eef3fb; + --otb-bg-grad-1:#f7f9fd; + --otb-bg-grad-2:#edf3fb; + --otb-bg-grad-3:#e7f2ec; + --otb-panel:#ffffff; + --otb-text:#172033; + --otb-muted:#55627c; + --otb-link:#0b63ce; + --otb-accent:#0e8f73; + --otb-border:rgba(18,32,60,0.10); + --otb-shadow:0 10px 28px rgba(16,24,40,0.08); +} + +html, body{ + background: + linear-gradient(90deg, var(--otb-bg-grad-1) 0%, var(--otb-bg-grad-2) 48%, var(--otb-bg-grad-3) 100%), + var(--otb-bg-main) !important; + color:var(--otb-text) !important; +} + +a{ color:var(--otb-link); } + +.site-header{ background:transparent; } + +.site-brand strong, +.site-navlinks > a, +.dropdown-toggle, +.site-title strong, +.site-title span{ + color:var(--otb-text) !important; +} + +.site-title span, +.small, +.lead, +.sub, +.portal-sub, +.portal-note, +.muted{ + color:var(--otb-muted) !important; +} + +.card, +.heroCard, +.sideCard, +.feature, +.portal-card, +.detail-card, +.pay-card, +.snapshot-wrap, +.note, +.tableWrap{ + background:var(--otb-panel) !important; + border:1px solid var(--otb-border) !important; + color:var(--otb-text) !important; + box-shadow:var(--otb-shadow); +} + +.dropdown-menu{ + background:var(--otb-panel) !important; + border:1px solid var(--otb-border) !important; + box-shadow:var(--otb-shadow); +} + +.dropdown-menu a{ + color:var(--otb-text) !important; +} + +input, +select, +textarea{ + background:rgba(255,255,255,0.06) !important; + color:var(--otb-text) !important; + border:1px solid var(--otb-border) !important; +} + +html[data-theme="light"] input, +html[data-theme="light"] select, +html[data-theme="light"] textarea{ + background:#f6f8fc !important; +} + +::placeholder{ + color:var(--otb-muted); + opacity:0.85; +} + +.btn, +.portal-btn{ + border:1px solid var(--otb-border) !important; +} + +.otb-statusbar{ + background:#0b1326 !important; + border-top:1px solid rgba(255,255,255,0.06); +} + +.otb-statusbar-inner{ + max-width:1200px; + margin:0 auto; + padding:10px 18px; + color:#f3f7ff; + display:flex; + justify-content:center; + align-items:center; + gap:10px; + flex-wrap:wrap; + font-size:13px; + line-height:1.4; +} + +.otb-statusbar strong{ color:#f8fbff; } + +.otb-statusbar a{ + color:#62f0cf !important; + text-decoration:none; +} + +.otb-dot{ + width:4px; + height:4px; + border-radius:50%; + background:rgba(255,255,255,0.25); + display:inline-block; +} + +html[data-theme="light"] .otb-statusbar{ + background:#edf2fb !important; + border-top:1px solid rgba(18,32,60,0.08); +} + +html[data-theme="light"] .otb-statusbar-inner{ + color:#23314a; +} + +html[data-theme="light"] .otb-statusbar strong{ + color:#16233a; +} + +html[data-theme="light"] .otb-dot{ + background:rgba(23,32,51,0.22); +} + +.portal-shell, +.portal-wrap{ + max-width:1200px; + margin:0 auto; + padding:32px 20px 72px; +} + +.portal-card{ + max-width:760px; + margin:0 auto; +} + +@media (max-width: 1100px){ + .site-container, + .container{ + padding-left:16px !important; + padding-right:16px !important; + } + + .site-nav{ + display:flex; + flex-direction:column; + align-items:flex-start; + gap:14px; + } + + .site-nav-right{ + width:100%; + display:flex; + flex-direction:column; + align-items:flex-start; + gap:12px; + } + + .site-navlinks{ + width:100%; + display:flex; + flex-wrap:wrap; + gap:10px 16px; + align-items:center; + } + + .dropdown-menu{ + min-width:220px; + } + + .portal-shell, + .portal-wrap{ + padding:24px 16px 64px; + } +} + +@media (max-width: 640px){ + .site-brand img{ + width:48px; + height:48px; + } + + .site-title strong{ + font-size:20px; + } + + .site-title span{ + font-size:13px; + } + + .site-navlinks{ + gap:8px 14px; + } + + .otb-statusbar-inner{ + font-size:12px; + gap:8px; + padding:10px 12px; + } + + .portal-card{ + max-width:100%; + } +} + + + +/* ===== OTB hamburger nav ===== */ +.otb-menu-toggle{ + display:none !important; + margin-left:auto; + width:46px; + height:42px; + border:1px solid var(--otb-border); + background:rgba(255,255,255,0.04); + border-radius:12px; + padding:0; + align-items:center; + justify-content:center; + flex-direction:column; + gap:5px; + cursor:pointer; +} + +.otb-menu-toggle span{ + display:block; + width:20px; + height:2px; + background:var(--otb-text); + border-radius:2px; + transition:transform 0.2s ease, opacity 0.2s ease; +} + +.site-nav-right{ + display:flex; + align-items:center; + gap:16px; +} + +@media (max-width: 1100px){ + .otb-menu-toggle{ + display:flex !important; + } + + .site-nav{ + display:flex; + align-items:center; + justify-content:space-between; + gap:14px; + flex-wrap:wrap; + } + + .site-brand{ + min-width:0; + flex:1 1 auto; + } + + .site-nav-right{ + display:none; + width:100%; + flex-direction:column; + align-items:flex-start; + gap:14px; + padding-top:10px; + } + + .site-nav-right.otb-nav-open{ + display:flex; + } + + .site-navlinks{ + width:100%; + display:flex; + flex-direction:column; + align-items:flex-start; + gap:10px; + } + + .site-navlinks > a, + .dropdown{ + width:100%; + } + + .dropdown-toggle{ + display:inline-flex; + width:100%; + justify-content:flex-start; + } + + .dropdown-menu{ + position:static !important; + display:none; + min-width:0; + width:100%; + margin-top:8px; + } + + .dropdown:hover .dropdown-menu, + .dropdown:focus-within .dropdown-menu{ + display:block; + } +} + + + +/* ===== stronger mobile nav override ===== */ +@media (max-width: 1100px){ + .otb-menu-toggle{ + display:flex !important; + } + + .site-nav{ + display:flex !important; + align-items:center !important; + justify-content:space-between !important; + gap:14px !important; + flex-wrap:wrap !important; + } + + .site-nav-right{ + display:none !important; + width:100% !important; + flex-direction:column !important; + align-items:flex-start !important; + gap:14px !important; + padding-top:10px !important; + } + + .site-nav-right.otb-nav-open{ + display:flex !important; + } + + .site-navlinks{ + width:100% !important; + display:flex !important; + flex-direction:column !important; + align-items:flex-start !important; + gap:10px !important; + } + + .site-navlinks > a, + .dropdown{ + width:100% !important; + } + + .dropdown-toggle{ + width:100% !important; + justify-content:flex-start !important; + } + + .dropdown-menu{ + position:static !important; + min-width:0 !important; + width:100% !important; + margin-top:8px !important; + } +} + + +/* ===== desktop nav force ===== */ +@media (min-width: 1101px){ + .otb-menu-toggle{ + display:none !important; + } + + .site-nav-right{ + display:flex !important; + width:auto !important; + flex-direction:row !important; + align-items:center !important; + gap:16px !important; + padding-top:0 !important; + } + + .site-navlinks{ + display:flex !important; + flex-direction:row !important; + align-items:center !important; + width:auto !important; + gap:16px !important; + } +} + +/* ===== OTB hamburger hard fix ===== */ +.otb-menu-toggle{ + display:none !important; + appearance:none !important; + -webkit-appearance:none !important; + background:transparent !important; + border:0 !important; + box-shadow:none !important; + outline:none !important; + padding:0 !important; + margin:0 0 0 auto !important; + width:46px !important; + height:42px !important; + align-items:center !important; + justify-content:center !important; + flex-direction:column !important; + gap:5px !important; + cursor:pointer !important; +} + +.otb-menu-toggle::before, +.otb-menu-toggle::after{ + content:none !important; + display:none !important; +} + +.otb-menu-toggle span{ + display:block !important; + width:20px !important; + height:2px !important; + min-height:2px !important; + max-height:2px !important; + background:var(--otb-text) !important; + border-radius:2px !important; + margin:0 !important; + padding:0 !important; +} + +@media (min-width: 1101px){ + .otb-menu-toggle{ + display:none !important; + visibility:hidden !important; + pointer-events:none !important; + width:0 !important; + height:0 !important; + overflow:hidden !important; + } + + .site-nav-right{ + display:flex !important; + width:auto !important; + flex-direction:row !important; + align-items:center !important; + gap:16px !important; + padding-top:0 !important; + } + + .site-navlinks{ + display:flex !important; + flex-direction:row !important; + align-items:center !important; + width:auto !important; + gap:16px !important; + } +} + +@media (max-width: 1100px){ + .otb-menu-toggle{ + display:flex !important; + visibility:visible !important; + pointer-events:auto !important; + width:46px !important; + height:42px !important; + overflow:visible !important; + border:1px solid var(--otb-border) !important; + background:rgba(255,255,255,0.04) !important; + border-radius:12px !important; + } + + .site-nav{ + display:flex !important; + align-items:center !important; + justify-content:space-between !important; + gap:14px !important; + flex-wrap:wrap !important; + } + + .site-brand{ + min-width:0 !important; + flex:1 1 auto !important; + } + + .site-nav-right{ + display:none !important; + width:100% !important; + flex-direction:column !important; + align-items:flex-start !important; + gap:14px !important; + padding-top:10px !important; + } + + .site-nav-right.otb-nav-open{ + display:flex !important; + } + + .site-navlinks{ + width:100% !important; + display:flex !important; + flex-direction:column !important; + align-items:flex-start !important; + gap:10px !important; + } + + .site-navlinks > a, + .dropdown{ + width:100% !important; + } + + .dropdown-toggle{ + width:100% !important; + justify-content:flex-start !important; + } + + .dropdown-menu{ + position:static !important; + min-width:0 !important; + width:100% !important; + margin-top:8px !important; + } +} + +/* light theme button consistency fix */ +body.light-theme .portal-btn, +body.light-theme a.portal-btn, +body.light-theme button.portal-btn { + background: rgba(255,255,255,0.75); + color: var(--text, #10233f); + border: 1px solid rgba(40,70,120,0.14); + opacity: 1; +} + +body.light-theme .portal-btn.primary, +body.light-theme a.portal-btn.primary, +body.light-theme button.portal-btn.primary { + color: #081528; +} + +body.light-theme .portal-toolbar .portal-btn, +body.light-theme .portal-actions .portal-btn { + opacity: 1; +} + +body.light-theme .portal-btn[disabled], +body.light-theme button.portal-btn[disabled], +body.light-theme .portal-btn.disabled { + opacity: 0.55; +} diff --git a/app/static/css/style.css b/app/static/css/style.css new file mode 100644 index 0000000..9f05200 --- /dev/null +++ b/app/static/css/style.css @@ -0,0 +1,1276 @@ +:root{ + --bg:#0b0f14; + --card:#121825; + --card-soft:rgba(18,24,37,.78); + --text:#e8eefc; + --muted:#aab6d6; + --line:#24304a; + --accent:#7aa2ff; + --accent2:#62e6b7; + --success:#4ade80; + --warn:#fbbf24; + --danger:#f87171; + --radius:16px; + --shadow:0 16px 40px rgba(0,0,0,.35); +} + +*{box-sizing:border-box} +html,body{height:100%} + +body{ + margin:0; + font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial, "Apple Color Emoji","Segoe UI Emoji"; + background: + radial-gradient(1200px 600px at 20% 0%, rgba(122,162,255,.22), transparent 60%), + radial-gradient(900px 500px at 90% 20%, rgba(98,230,183,.15), transparent 60%), + linear-gradient(180deg, #081225 0%, #09172d 100%); + color:var(--text); + line-height:1.45; +} + +a{color:inherit} + +/* ===== Shared container ===== */ +.container{ + max-width:1100px; + margin:0 auto; + padding:20px 18px; +} + +/* ===== Header / branded nav ===== */ +.header{width:100%} + +.nav{ + display:flex; + align-items:center; + justify-content:space-between; + gap:18px; + padding:12px 0 22px 0; +} + +.brand{ + display:flex; + align-items:center; + gap:14px; + text-decoration:none; +} + +.brand img{ + height:60px; + width:auto; + display:block; + object-fit:contain; + background: rgba(255,255,255,0.92); + padding: 6px 12px; + border-radius: 999px; + box-shadow: 0 8px 24px rgba(0,0,0,0.35); +} + +.title{ + display:flex; + flex-direction:column; + line-height:1.1; +} + +.title strong{letter-spacing:.2px} +.title span{ + color:var(--muted); + font-size:13px; + margin-top:2px; +} + +.navlinks{ + display:flex; + gap:12px; + flex-wrap:wrap; + justify-content:flex-end; +} + +.navlinks a{ + text-decoration:none; + padding:8px 10px; + border-radius:12px; + color:var(--muted); + border:1px solid transparent; +} + +.navlinks a:hover{ + color:var(--text); + border-color:rgba(255,255,255,.08); + background:rgba(255,255,255,.03); +} + +.navlinks a.active{ + color:var(--text); + border-color:rgba(255,255,255,.10); + background:rgba(255,255,255,.04); +} + +/* ===== Generic portal shell ===== */ +.portal-shell{ + max-width:1100px; + margin:16px auto 28px auto; + padding:0 18px 20px 18px; +} + +.portal-card, +.detail-card, +.summary-card, +.pay-card{ + background: var(--card-soft); + border: 1px solid rgba(255,255,255,.07); + border-radius: var(--radius); + box-shadow: var(--shadow); +} + +.portal-card{ + max-width:760px; + margin:24px auto 12px auto; + padding:22px; +} + +.portal-page-header{ + display:flex; + align-items:flex-start; + justify-content:space-between; + gap:16px; + flex-wrap:wrap; + margin:6px 0 18px 0; +} + +.portal-page-title{ + margin:0; + font-size:24px; + line-height:1.1; +} + +.portal-page-subtitle{ + margin:8px 0 0 0; + color:var(--muted); +} + +.portal-client-name{ + margin:8px 0 0 0; + color:var(--text); + font-size:15px; +} + +.portal-toolbar{ + display:flex; + gap:10px; + flex-wrap:wrap; + align-items:center; +} + +.portal-btn, +.btn, +.pay-btn, +.quote-pick-btn{ + display:inline-flex; + align-items:center; + justify-content:center; + gap:8px; + min-height:42px; + padding:10px 14px; + border-radius:12px; + text-decoration:none; + border:1px solid rgba(255,255,255,.10); + background: rgba(255,255,255,.05); + color:var(--text); + font-weight:600; + cursor:pointer; +} + +.portal-btn:hover, +.btn:hover, +.pay-btn:hover, +.quote-pick-btn:hover{ + border-color:rgba(122,162,255,.45); + box-shadow:0 0 0 4px rgba(122,162,255,.12); + text-decoration:none; +} + +.portal-btn.primary, +.btn.primary{ + background: linear-gradient(135deg, rgba(122,162,255,.95), rgba(98,230,183,.85)); + border-color: transparent; + color:#071017; + font-weight:700; +} + +.portal-btn.primary:hover, +.btn.primary:hover{ + box-shadow:0 0 0 4px rgba(98,230,183,.18); +} + +/* ===== Login / forms ===== */ +.portal-sub{ + color:var(--muted); + margin:0 0 16px 0; +} + +.portal-form{ + display:grid; + gap:14px; +} + +.portal-form label{ + display:block; + font-weight:600; + margin-bottom:6px; +} + +.portal-form input, +.portal-form select, +.pay-selector{ + width:100%; + padding:12px 14px; + border-radius:12px; + border:1px solid rgba(255,255,255,.14); + background: rgba(255,255,255,.06); + color:var(--text); + box-sizing:border-box; + outline:none; +} + +.portal-form input:focus, +.portal-form select:focus, +.pay-selector:focus{ + border-color:rgba(122,162,255,.65); + box-shadow:0 0 0 4px rgba(122,162,255,.12); +} + +.portal-actions{ + display:flex; + gap:10px; + flex-wrap:wrap; + margin-top:4px; +} + +.portal-note{ + margin-top:16px; + color:var(--muted); + font-size:14px; + line-height:1.5; +} + +.portal-msg, +.error-box, +.success-box{ + margin-bottom:16px; + padding:12px 14px; + border-radius:12px; + border:1px solid rgba(255,255,255,.16); + background: rgba(255,255,255,.04); +} + +.error-box{ + border-color: rgba(239, 68, 68, 0.55); + background: rgba(127, 29, 29, 0.22); + color: #fecaca; +} + +.success-box{ + border-color: rgba(34, 197, 94, 0.55); + background: rgba(22, 101, 52, 0.18); + color: #dcfce7; +} + +/* ===== Dashboard ===== */ +.portal-wrap{ + max-width:1100px; + margin:0 auto; + padding:0; +} + +.summary-grid, +.detail-grid{ + display:grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap:14px; + margin: 0 0 18px 0; +} + +.summary-card, +.detail-card{ + padding:18px; +} + +.summary-card h3, +.detail-card h3{ + margin:0 0 8px 0; + font-size:14px; + color:var(--muted); +} + +.summary-card .summary-value, +.detail-card .detail-value{ + font-size:28px; + font-weight:800; + line-height:1.1; + color:var(--text); +} + +.summary-card .summary-sub{ + margin-top:6px; + font-size:12px; + color:var(--muted); +} + +.section-title{ + margin:18px 0 10px 0; + font-size:22px; +} + +.table-card{ + background: var(--card-soft); + border: 1px solid rgba(255,255,255,.07); + border-radius: var(--radius); + box-shadow: var(--shadow); + overflow:hidden; +} + +/* ===== Tables ===== */ +table, +.portal-table, +.quote-table{ + width:100%; + border-collapse: collapse; +} + +table.portal-table th, +table.portal-table td, +.quote-table th, +.quote-table td{ + padding: 0.9rem 0.85rem; + border-bottom: 1px solid rgba(255,255,255,0.10); + text-align: left; + vertical-align: middle; +} + +table.portal-table th, +.quote-table th{ + background: rgba(255,255,255,.06); + color: var(--text); + font-size:13px; + letter-spacing:.02em; +} + +table.portal-table tr:hover td, +.quote-table tr:hover td{ + background: rgba(255,255,255,.02); +} + +.invoice-link{ + color: var(--text); + text-decoration: none; + font-weight: 700; +} + +.invoice-link:hover{ + color: var(--accent); + text-decoration: underline; +} + +/* ===== Badges ===== */ +.status-badge, +.quote-badge{ + display: inline-block; + padding: 0.22rem 0.62rem; + border-radius: 999px; + font-size: 0.82rem; + font-weight: 700; +} + +.status-paid{ background: rgba(34, 197, 94, 0.18); color: var(--success); } +.status-pending{ background: rgba(245, 158, 11, 0.20); color: var(--warn); } +.status-overdue{ background: rgba(239, 68, 68, 0.18); color: var(--danger); } +.status-other{ background: rgba(148, 163, 184, 0.20); color: #cbd5e1; } + +.quote-live{ background: rgba(34, 197, 94, 0.18); color: var(--success); } +.quote-stale{ background: rgba(239, 68, 68, 0.18); color: var(--danger); } + +/* ===== Payments / invoice detail ===== */ +.pay-card{ + padding:18px; + margin-top: 1.25rem; +} + +.pay-selector-row{ + display:flex; + gap:0.75rem; + align-items:center; + flex-wrap:wrap; + margin-top:0.75rem; +} + +.pay-panel{ + margin-top: 1rem; + padding: 1rem; + border: 1px solid rgba(255,255,255,0.12); + border-radius: 12px; + background: rgba(255,255,255,0.02); +} + +.pay-panel.hidden{ display:none; } + +.pay-btn-square { background:#16a34a; border-color:transparent; color:#fff; } +.pay-btn-wallet { background:#2563eb; border-color:transparent; color:#fff; } +.pay-btn-mobile { background:#7c3aed; border-color:transparent; color:#fff; } +.pay-btn-copy { background:#374151; border-color:transparent; color:#fff; } + +.snapshot-wrap{ + position: relative; + margin-top: 1rem; + border: 1px solid rgba(255,255,255,0.14); + border-radius: 14px; + padding: 1rem; + background: rgba(255,255,255,0.02); +} + +.snapshot-header{ + display:flex; + justify-content:space-between; + gap:1rem; + align-items:flex-start; +} + +.snapshot-meta{ + flex: 1 1 auto; + min-width: 0; + line-height: 1.65; +} + +.snapshot-timer-box{ + width: 220px; + min-height: 132px; + border: 1px solid rgba(255,255,255,0.16); + border-radius: 14px; + background: rgba(0,0,0,0.18); + display:flex; + flex-direction:column; + justify-content:center; + align-items:center; + text-align:center; + padding: 0.9rem; +} + +.snapshot-timer-value{ + font-size: 2rem; + font-weight: 800; + line-height: 1.1; +} + +.snapshot-timer-label{ + margin-top: 0.55rem; + font-size: 0.95rem; + opacity: 0.95; +} + +.snapshot-timer-expired{ color: var(--danger); } + +.lock-box{ + margin-top: 1rem; + border: 1px solid rgba(34, 197, 94, 0.28); + background: rgba(22, 101, 52, 0.16); + border-radius: 12px; + padding: 1rem; +} + +.lock-box.expired{ + border-color: rgba(239, 68, 68, 0.55); + background: rgba(127, 29, 29, 0.22); +} + +.lock-grid{ + display:grid; + grid-template-columns: 1fr 220px; + gap:1rem; + align-items:start; +} + +.lock-code{ + display:block; + margin-top:0.35rem; + padding:0.65rem 0.8rem; + background: rgba(0,0,0,0.22); + border-radius: 8px; + overflow-wrap:anywhere; +} + +.wallet-actions{ + display:flex; + gap:0.75rem; + flex-wrap:wrap; + margin-top:0.9rem; + align-items:center; +} + +.wallet-help{ + margin-top: 0.85rem; + padding: 0.9rem 1rem; + border-radius: 10px; + background: rgba(255,255,255,0.04); + border: 1px solid rgba(255,255,255,0.10); +} + +.wallet-help h4{ + margin: 0 0 0.55rem 0; + font-size: 1rem; +} + +.wallet-help p{ margin: 0.35rem 0; } +.wallet-note{ opacity:0.9; margin-top:0.65rem; } +.mono{ font-family: monospace; } + +.copy-row{ + display:flex; + gap:0.5rem; + flex-wrap:wrap; + align-items:center; + margin-top:0.65rem; +} + +.copy-target{ + flex: 1 1 420px; + min-width: 220px; +} + +.copy-status{ + display:inline-block; + margin-left: 0.5rem; + opacity: 0.9; +} + +/* ===== Footer ===== */ +footer{ + margin:28px auto 20px auto; + max-width:1100px; + padding:0 18px; + color:var(--muted); + font-size:13px; +} + +/* ===== Responsive ===== */ +@media (max-width: 900px){ + .summary-grid, + .detail-grid{ + grid-template-columns:1fr; + } + + .nav{ + align-items:flex-start; + flex-direction:column; + } + + .navlinks{ + justify-content:flex-start; + } + + .brand img{ + height:54px; + } +} + +@media (max-width: 820px){ + .snapshot-header, + .lock-grid{ + grid-template-columns: 1fr; + display:block; + } + + .snapshot-timer-box{ + width: 100%; + margin-top: 1rem; + min-height: 110px; + } +} + + +/* ===== Fixed CAD / Oracle status bar ===== */ +body{ + padding-bottom: 56px; +} + +.otb-statusbar{ + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 9999; + display: flex; + align-items: center; + justify-content: center; + min-height: 42px; + padding: 8px 14px; + background: rgba(8, 16, 32, 0.94); + border-top: 1px solid rgba(255,255,255,.10); + backdrop-filter: blur(8px); + box-shadow: 0 -8px 24px rgba(0,0,0,.28); +} + +.otb-statusbar-inner{ + width: 100%; + max-width: 1100px; + display: flex; + gap: 10px; + align-items: center; + justify-content: center; + flex-wrap: wrap; + text-align: center; + color: var(--muted); + font-size: 12px; + line-height: 1.35; +} + +.otb-statusbar strong{ + color: var(--text); + font-weight: 700; +} + +.otb-statusbar a{ + color: var(--accent2); + text-decoration: none; + font-weight: 600; +} + +.otb-statusbar a:hover{ + text-decoration: underline; +} + +.otb-dot{ + width: 6px; + height: 6px; + border-radius: 999px; + display: inline-block; + background: rgba(255,255,255,.25); + flex: 0 0 auto; +} + +/* ===== Payment method chips ===== */ +.payment-method{ + display: inline-flex; + align-items: center; + gap: 7px; + margin-top: 7px; + padding: 4px 9px; + border-radius: 999px; + background: rgba(255,255,255,.05); + border: 1px solid rgba(255,255,255,.08); + color: var(--muted); + font-size: 11px; + font-weight: 700; + letter-spacing: .01em; +} + +.payment-method::before{ + content: ""; + width: 8px; + height: 8px; + border-radius: 999px; + display: inline-block; + background: #7aa2ff; + box-shadow: 0 0 0 3px rgba(255,255,255,.04); +} + +.payment-square::before{ background: #7dd3fc; } +.payment-etransfer::before{ background: #86efac; } +.payment-etho::before{ background: #b084ff; } +.payment-etica::before{ background: #4fd1c5; } +.payment-alt::before{ background: #fbbf24; } +.payment-cad::before{ background: #cbd5e1; } + +/* ===== Slightly stronger row hover ===== */ +table.portal-table tbody tr:hover td, +.quote-table tbody tr:hover td{ + background: rgba(255,255,255,.035); + transition: background .14s ease; +} + +@media (max-width: 700px){ + body{ + padding-bottom: 72px; + } + + .otb-statusbar-inner{ + font-size: 11px; + line-height: 1.25; + } +} + + +@media (max-width: 900px){ + .dropdown{ + width:100%; + } + + .dropdown-toggle{ + width:100%; + } + + .dropdown-menu{ + position:static; + right:auto; + top:auto; + min-width:100%; + margin-top:6px; + } +} + + +/* ===== Shared services dropdown ===== */ +.navlinks{ + display:flex; + gap:12px; + flex-wrap:wrap; + justify-content:flex-end; + align-items:center; +} + +.dropdown{ + position:relative; + display:inline-block; +} + +.dropdown-toggle{ + display:inline-block; + cursor:pointer; +} + +.dropdown-menu{ + position:absolute; + top:calc(100% + 8px); + right:0; + min-width:220px; + display:none; + padding:10px; + border-radius:14px; + background:rgba(18,24,37,.98); + border:1px solid rgba(255,255,255,.08); + box-shadow:0 16px 40px rgba(0,0,0,.35); + z-index:9999; +} + +.dropdown:hover .dropdown-menu, +.dropdown:focus-within .dropdown-menu{ + display:block; +} + +.dropdown-menu a{ + display:block; + padding:9px 10px; + border-radius:10px; + color:var(--muted); + text-decoration:none; + white-space:nowrap; + margin:0; +} + +.dropdown-menu a + a{ + margin-top:4px; +} + +.dropdown-menu a:hover{ + color:var(--text); + background:rgba(255,255,255,.04); +} + +@media (max-width: 900px){ + .dropdown{ + width:100%; + } + + .dropdown-toggle{ + width:100%; + } + + .dropdown-menu{ + position:static; + right:auto; + top:auto; + min-width:100%; + margin-top:6px; + } +} + + +/* ===== OTB shared branding ===== */ +html[data-theme="dark"]{ + --otb-bg:#081225; + --otb-bg2:#09172d; + --otb-text:#e8eefc; + --otb-muted:#aab6d6; + --otb-panel:rgba(18,24,37,.98); + --otb-panel-soft:rgba(18,24,37,.78); + --otb-line:rgba(255,255,255,.08); +} + +html[data-theme="light"]{ + --otb-bg:#f4f7fb; + --otb-bg2:#eef3f9; + --otb-text:#0f172a; + --otb-muted:#475569; + --otb-panel:rgba(255,255,255,.98); + --otb-panel-soft:rgba(255,255,255,.88); + --otb-line:rgba(15,23,42,.10); +} + +body{ + padding-bottom:56px; +} + +.site-container{ + max-width:1100px; + margin:0 auto; + padding:20px 18px 0 18px; +} + +.site-header{ + width:100%; +} + +.site-nav{ + display:flex; + align-items:center; + justify-content:space-between; + gap:18px; + padding:12px 0 22px 0; +} + +.site-brand{ + display:flex; + align-items:center; + gap:14px; + text-decoration:none; + color:inherit; +} + +.site-brand img{ + height:60px; + width:auto; + display:block; + object-fit:contain; + background:rgba(255,255,255,0.92); + padding:6px 12px; + border-radius:999px; + box-shadow:0 8px 24px rgba(0,0,0,0.35); +} + +.site-title{ + display:flex; + flex-direction:column; + line-height:1.1; +} + +.site-title strong{ + letter-spacing:.2px; + color:var(--otb-text); +} + +.site-title span{ + color:var(--otb-muted); + font-size:13px; + margin-top:2px; +} + +.site-nav-right{ + display:flex; + align-items:center; + gap:14px; + flex-wrap:wrap; + justify-content:flex-end; +} + +.site-navlinks{ + display:flex; + gap:12px; + flex-wrap:wrap; + justify-content:flex-end; + align-items:center; +} + +.site-navlinks > a, +.dropdown-toggle{ + text-decoration:none; + padding:8px 10px; + border-radius:12px; + color:var(--otb-muted); + border:1px solid transparent; +} + +.site-navlinks > a:hover, +.dropdown-toggle:hover{ + color:var(--otb-text); + border-color:var(--otb-line); + background:rgba(255,255,255,.03); +} + +.dropdown{ + position:relative; + display:inline-block; +} + +.dropdown-toggle{ + display:inline-block; + cursor:pointer; +} + +.dropdown-menu{ + position:absolute; + top:calc(100% + 8px); + right:0; + min-width:220px; + display:none; + padding:10px; + border-radius:14px; + background:var(--otb-panel); + border:1px solid var(--otb-line); + box-shadow:0 16px 40px rgba(0,0,0,.35); + z-index:9999; +} + +.dropdown:hover .dropdown-menu, +.dropdown:focus-within .dropdown-menu{ + display:block; +} + +.dropdown-menu a{ + display:block; + padding:9px 10px; + border-radius:10px; + color:var(--otb-muted); + text-decoration:none; + white-space:nowrap; + margin:0; +} + +.dropdown-menu a + a{ + margin-top:4px; +} + +.dropdown-menu a:hover{ + color:var(--otb-text); + background:rgba(255,255,255,.04); +} + +.otb-theme-switch{ + position:relative; + display:inline-block; + width:54px; + height:30px; + flex:0 0 auto; +} + +.otb-theme-switch input{ + opacity:0; + width:0; + height:0; +} + +.otb-theme-slider{ + position:absolute; + inset:0; + cursor:pointer; + background:rgba(255,255,255,.10); + border:1px solid var(--otb-line); + transition:.2s; + border-radius:999px; +} + +.otb-theme-slider:before{ + content:""; + position:absolute; + height:22px; + width:22px; + left:3px; + top:3px; + background:var(--otb-text); + transition:.2s; + border-radius:50%; +} + +.otb-theme-switch input:checked + .otb-theme-slider:before{ + transform:translateX(24px); +} + +.otb-statusbar{ + position:fixed; + left:0; + right:0; + bottom:0; + z-index:9999; + display:flex; + align-items:center; + justify-content:center; + min-height:42px; + padding:8px 14px; + background:var(--otb-panel); + border-top:1px solid var(--otb-line); + backdrop-filter:blur(8px); + box-shadow:0 -8px 24px rgba(0,0,0,.28); +} + +.otb-statusbar-inner{ + width:100%; + max-width:1100px; + display:flex; + gap:10px; + align-items:center; + justify-content:center; + flex-wrap:wrap; + text-align:center; + color:var(--otb-muted); + font-size:12px; + line-height:1.35; +} + +.otb-statusbar strong{ + color:var(--otb-text); + font-weight:700; +} + +.otb-statusbar a{ + color:#62e6b7; + text-decoration:none; + font-weight:600; +} + +.otb-statusbar a:hover{ + text-decoration:underline; +} + +.otb-dot{ + width:6px; + height:6px; + border-radius:999px; + display:inline-block; + background:rgba(255,255,255,.25); + flex:0 0 auto; +} + +@media (max-width: 900px){ + .site-nav{ + align-items:flex-start; + flex-direction:column; + } + + .site-nav-right{ + width:100%; + justify-content:space-between; + } + + .site-navlinks{ + justify-content:flex-start; + } + + .dropdown{ + width:100%; + } + + .dropdown-toggle{ + width:100%; + } + + .dropdown-menu{ + position:static; + right:auto; + top:auto; + min-width:100%; + margin-top:6px; + } + + .site-brand img{ + height:54px; + } +} + +@media (max-width: 700px){ + body{ + padding-bottom:72px; + } + + .otb-statusbar-inner{ + font-size:11px; + line-height:1.25; + } +}.pay-selector { + background: #0f172a !important; + color: #e8eefc !important; + border: 1px solid rgba(255,255,255,0.2) !important; +} + +.pay-selector option { + background: #0f172a; + color: #e8eefc; +} + +.pay-selector option:checked { + background: #2563eb; + color: #ffffff; +} + +/* ===== final portal light-theme button normalization ===== */ + +body.light-theme .portal-btn, +body.light-theme a.portal-btn, +body.light-theme button.portal-btn, +body.light-theme .btn, +body.light-theme a.btn, +body.light-theme button.btn { + background: rgba(255,255,255,0.88); + color: #10233f; + border: 1px solid rgba(40,70,120,0.14); + box-shadow: none; + opacity: 1 !important; +} + +body.light-theme .portal-btn:hover, +body.light-theme a.portal-btn:hover, +body.light-theme button.portal-btn:hover, +body.light-theme .btn:hover, +body.light-theme a.btn:hover, +body.light-theme button.btn:hover { + border-color: rgba(122,162,255,.45); + box-shadow: 0 0 0 4px rgba(122,162,255,.10); + text-decoration: none; +} + +body.light-theme .portal-btn.primary, +body.light-theme .btn.primary { + background: linear-gradient(135deg, rgba(122,162,255,.95), rgba(98,230,183,.85)); + border-color: transparent; + color: #071017; +} + +body.light-theme .portal-btn[disabled], +body.light-theme button.portal-btn[disabled], +body.light-theme .portal-btn.disabled, +body.light-theme .btn[disabled], +body.light-theme button.btn[disabled], +body.light-theme .btn.disabled { + opacity: 0.5 !important; +} + +/* ===== portal button fix for actual OTB light theme selectors ===== */ + +html[data-theme="light"] .portal-btn, +html[data-theme="light"] a.portal-btn, +html[data-theme="light"] button.portal-btn, +body.otb-light .portal-btn, +body.otb-light a.portal-btn, +body.otb-light button.portal-btn, +html[data-theme="light"] .btn, +html[data-theme="light"] a.btn, +html[data-theme="light"] button.btn, +body.otb-light .btn, +body.otb-light a.btn, +body.otb-light button.btn { + background: rgba(255,255,255,0.88) !important; + color: #10233f !important; + border: 1px solid rgba(40,70,120,0.14) !important; + box-shadow: none !important; + opacity: 1 !important; +} + +html[data-theme="light"] .portal-btn:hover, +html[data-theme="light"] a.portal-btn:hover, +html[data-theme="light"] button.portal-btn:hover, +body.otb-light .portal-btn:hover, +body.otb-light a.portal-btn:hover, +body.otb-light button.portal-btn:hover, +html[data-theme="light"] .btn:hover, +html[data-theme="light"] a.btn:hover, +html[data-theme="light"] button.btn:hover, +body.otb-light .btn:hover, +body.otb-light a.btn:hover, +body.otb-light button.btn:hover { + border-color: rgba(122,162,255,.45) !important; + box-shadow: 0 0 0 4px rgba(122,162,255,.10) !important; + text-decoration: none !important; +} + +html[data-theme="light"] .portal-btn.primary, +html[data-theme="light"] .btn.primary, +body.otb-light .portal-btn.primary, +body.otb-light .btn.primary { + background: linear-gradient(135deg, rgba(122,162,255,.95), rgba(98,230,183,.85)) !important; + border-color: transparent !important; + color: #071017 !important; +} + +html[data-theme="light"] .portal-btn[disabled], +html[data-theme="light"] button.portal-btn[disabled], +html[data-theme="light"] .portal-btn.disabled, +html[data-theme="light"] .btn[disabled], +html[data-theme="light"] button.btn[disabled], +html[data-theme="light"] .btn.disabled, +body.otb-light .portal-btn[disabled], +body.otb-light button.portal-btn[disabled], +body.otb-light .portal-btn.disabled, +body.otb-light .btn[disabled], +body.otb-light button.btn[disabled], +body.otb-light .btn.disabled { + opacity: 0.5 !important; +} + +/* ===== portal button fix for actual OTB light theme selectors ===== */ + +html[data-theme="light"] .portal-btn, +html[data-theme="light"] a.portal-btn, +html[data-theme="light"] button.portal-btn, +body.otb-light .portal-btn, +body.otb-light a.portal-btn, +body.otb-light button.portal-btn, +html[data-theme="light"] .btn, +html[data-theme="light"] a.btn, +html[data-theme="light"] button.btn, +body.otb-light .btn, +body.otb-light a.btn, +body.otb-light button.btn { + background: rgba(255,255,255,0.88) !important; + color: #10233f !important; + border: 1px solid rgba(40,70,120,0.14) !important; + box-shadow: none !important; + opacity: 1 !important; +} + +html[data-theme="light"] .portal-btn:hover, +html[data-theme="light"] a.portal-btn:hover, +html[data-theme="light"] button.portal-btn:hover, +body.otb-light .portal-btn:hover, +body.otb-light a.portal-btn:hover, +body.otb-light button.portal-btn:hover, +html[data-theme="light"] .btn:hover, +html[data-theme="light"] a.btn:hover, +html[data-theme="light"] button.btn:hover, +body.otb-light .btn:hover, +body.otb-light a.btn:hover, +body.otb-light button.btn:hover { + border-color: rgba(122,162,255,.45) !important; + box-shadow: 0 0 0 4px rgba(122,162,255,.10) !important; + text-decoration: none !important; +} + +html[data-theme="light"] .portal-btn.primary, +html[data-theme="light"] .btn.primary, +body.otb-light .portal-btn.primary, +body.otb-light .btn.primary { + background: linear-gradient(135deg, rgba(122,162,255,.95), rgba(98,230,183,.85)) !important; + border-color: transparent !important; + color: #071017 !important; +} + +html[data-theme="light"] .portal-btn[disabled], +html[data-theme="light"] button.portal-btn[disabled], +html[data-theme="light"] .portal-btn.disabled, +html[data-theme="light"] .btn[disabled], +html[data-theme="light"] button.btn[disabled], +html[data-theme="light"] .btn.disabled, +body.otb-light .portal-btn[disabled], +body.otb-light button.portal-btn[disabled], +body.otb-light .portal-btn.disabled, +body.otb-light .btn[disabled], +body.otb-light button.btn[disabled], +body.otb-light .btn.disabled { + opacity: 0.5 !important; +} diff --git a/app/static/favicon.png b/app/static/favicon.png new file mode 100644 index 0000000..4f0f6bf Binary files /dev/null and b/app/static/favicon.png differ diff --git a/app/templates/auth/handoff_error.html b/app/templates/auth/handoff_error.html index fbba73d..b13d247 100644 --- a/app/templates/auth/handoff_error.html +++ b/app/templates/auth/handoff_error.html @@ -1,13 +1,26 @@ {% extends "portal_base.html" %} -{% block title %}Portal Handoff Error{% endblock %} +{% block title %}Portal Handoff Error - OTB Cloud{% endblock %} -{% block content %} -
{{ message }}
-+ OTB Cloud could not validate the portal launch request. +
{{ message }}
+ +- OTB Cloud is available only through a signed handoff from the OTB Billing portal. -
-+ OTB Cloud is available only through a signed handoff from the OTB Billing portal. +
+ Please return to the OTB Billing portal and launch OTB Cloud from your Services page. +
+Authenticated user: {{ user_email }}
-Tenant slug: {{ tenant_slug }}
-No devices have been created yet.
- {% endif %} +{% block portal_content %} +{{ user_email }}
++ Secure backup and storage dashboard for your account. +
- OTB Cloud is now running as a portal-linked secure storage service. - Next steps are real OTB Billing handoff integration, file library pages, and upload endpoints. -
+Registered source locations for uploaded data.
+No devices have been created yet.
+ {% endif %} +OTB Cloud is now operating inside the branded OTB portal shell.
++ Next steps are the searchable file library, bulk upload endpoints, zip export, and media processing jobs. +
+