From 89469d680896cef5f3e00ab8283011cce7c522b7 Mon Sep 17 00:00:00 2001 From: Don Kingdon Date: Mon, 13 Apr 2026 01:15:06 +0000 Subject: [PATCH] Adopt OTB portal shell styling for OTB Cloud --- app/static/brand.js | 54 + app/static/css/brand.css | 576 ++++++++++ app/static/css/style.css | 1276 +++++++++++++++++++++ app/static/favicon.png | Bin 0 -> 13854 bytes app/templates/auth/handoff_error.html | 27 +- app/templates/auth/login_required.html | 31 +- app/templates/cloud/dashboard.html | 97 +- app/templates/includes/otb_footer.html | 2 + app/templates/includes/otb_statusbar.html | 14 + app/templates/includes/site_nav.html | 42 + app/templates/portal_base.html | 119 +- 11 files changed, 2089 insertions(+), 149 deletions(-) create mode 100644 app/static/brand.js create mode 100644 app/static/css/brand.css create mode 100644 app/static/css/style.css create mode 100644 app/static/favicon.png create mode 100644 app/templates/includes/otb_footer.html create mode 100644 app/templates/includes/otb_statusbar.html create mode 100644 app/templates/includes/site_nav.html 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 0000000000000000000000000000000000000000..4f0f6bf7cfbda9e5fff2d802dc372943d1630beb GIT binary patch literal 13854 zcmb7r^K&I#_itvxC$??dww;-YtrOeUiEUdG8xz}3PHfx8H}CxqZdY~h>Zgx5; z-r>rMQV6iPuwY7nkFmJ&C3G|~(dp)u z|1>q{)dEKw!O*)|fNCtqF+_ft3flLXk7i+^tQ*ik#WPYD|AA2y_7l_d>R;{npXPNh z^TVtyBDG81fwIz>6IHJc zI5ABXa~w(#g17xQ;;OUh{^oNyp)F(j+ol4%eKD}INs=Z+_3wW*r4G6wd#&6tkr&)M zTiYC&uFsZY;f&Y{Nr02kkEN>UQ;H98>lf*q+rOI-R^9#P%8*edn)qv z**P*kDh}fic4l#Uy4TUR?W337lC^U82en0nhf%esRYl-=RYDD$r6WwRv)y1M<$$6= z;_8zosnLee`(DD=>q*VT)a-U{LSJQ}<&A{ueJj2Kor9z4{q-r9*=vdwkz666x~~aC zmaY*~nnW0YY&wF$=YF_&U?QR5NXCh!%badEk*27phzXpWkcyoUu> z%-}-c?yNeWF2uwTth$5j3@ww1gA4}8IF@6I7;}pLmOR4wXwysvw0jbIK6`xLJnM|n z*;qr!Qp}15Ev1Q)Hc95IbkREzU4%E0DmdF(# zYlDgFE*;-l9Pz^V=?4MO?{lV_-B-KMz~NlK*bJ!JDLjx zo+WPr7kTV#*~H7^qmipFZ&kI@|8(=hc^YOg4Aw(|3^oC}vb$dBXuiq76=XZBo zk`YK*_y_KGN7+X#>#W;}%4t(8vO>fnc z#Y5a*Buw_lzDG3M+_4n>0Q~Xo$icJgO4Py-+u2Y*PLf5&WzS2Ngo{Qjs$U{z|8MQ! zGt^v7MROhiA0V=XTP`8Iq@|`$WPTWF=lCVoK)s>=Qe0iycsM((U&w3dUMmxwkNZ4} zRFm(s_7h&L;M#j_&Gw7!w@i3N5>wg_1Xuz_P7(-JX(gt&niDW@_Dr`_Cy+pF{=$)d zR&@mcARQ;Ys(`uu(y>5FOjqn-v~;%qIuHG_a%Jwgk}O1S!58XNwc$0E_OdRjqfUm5 z{ZlcEJ*+_~H`oxHT0LH#{AGNL(D(t!nuH4B2R7Z4fJ#zCRlz}x{$ogMyN-L)$UbSm zI2B8+z5CWlhNOx^yX9)99q%*r>q+QIc`J^)(p*5tl0~Skpa7SFUMIh->@T-<`~^4S zbSHBxuX;ga^isw|6pa4AWBBGP>XQ7fn2!Sv^ee`r9jerAVzB7Y%=>pU_C5?iQfL_j zGRy^zf!51V>VouzPqoP4a;+is#Tw?drm7ar9 z%!n#%b?ZL;pQ_D~!>WHTg9Kb?bzRTOI>lkbi5>r(31GmhbmL}}K|KsD?>|^c4NMa3 zIYz}3iY`3YVM$uMm=$S^Pd*;JO|Po9??eMqHsK!r%jCdwWwt#hCPg{h0y2j9qfsFo z<;Hf%>lqT7=lH&3`7SYuF&v6oRE1L|!1OS~S5AXzT18i&j|NYs5O}=+7Acpiwgxj6 zGd0^h`Jhob=a`ZGe{rfV=a86qtz(Z*lX3|qcAR6+N<)mtNkhO!|H6f~oBg%Op{$KZ zg`EC?<-L`b|Po*$m981F&qtD92MRKPo z#$iOV_|7qVKAy|O2Kb!J=KCvNz=laM6kTH`BD=q0>3;x~YGzme<<8|Li{_UraaXRX zPQ1o0si~+;@I``@+Z&=<%aIikUFk(xEsUuH@^{f6&c>3|q?|r^sAj8e1T{5T=;y6f z#{d2PMkb*h**f=?VgA?SNP7LI%(*e%#%%KGKp85;y(BcrxqnJnyw2y+r7;&HVOkJx z2rw7WY{C)3rx8h)Smi{rCzAehB;HT73Vx*p6yyY%v^S4a(7WaH@mBG$T}(0;zo&~Ej#H?C=Exw(JWwBmVG>9cRsGNiiPXl!M#?WX;tkzs;_j0^~- zQ$d$B&lad@jL_|kzshIQm zYJ5Y{r&76kHf1Zh#D=ZbYky=P0&pMCD~gQlIAmJ^t+5Y1-MYV}vX{ zA|ZWTvUdiNY|M4!zP0rkSr6YvCRO@lA#uR@79-FhJ{5t5q)27Bh@O zL4R*qM@Fa&y}InSuVD_Z=w-s^a*`|Z;#(g%(a3xGwA?>!kn>Rk5-?X|)!>q3L%0NT zo-PX;Z+(#l8fT;_I&w#@?|5OtQ7Q?dlTa-*u|*9Q2!t)bsW3&PWq>hv$3ZPl??pQr zW_OWA5v@ z_=A>WS^tY3qTmvntI;NbaRSY0L#&uuNzDkxc!;vmYm``3N}aEQ)E&S&=2V z{`nu$$;gHSR{K4K;$)a9rpVzLI`%hhFbD24GCMdXF_R#3?~px7gghS+M39aLV_O~h zTq=Ify>b-&=>#2{-*7|hd&)|KuUzv<;0}Y}L9D`aS9S}rz*;YJo_{zJGfET9D8D6PjN05rt#1 z$$Gs#Q$lVjASs6tsirjj;}F(NKqwWXqUH89YtZ}7s~eh*t%)XIMBKQe^H_}5%5p_t zZ?N#oGX(r5t$XL(hQGo+u)?k*n2`W>bSwfjmTUXeUZ~&cj+h;{sC1Mic~X-KX|L5m zEK^{mX3&Mzoq{q}kR$Wo=QTM;j6|QqY>XsMPCOYh^@Y(-a4)Ru!nGPQA*!IOjYllb z3wkMHdlhMhxStayS=5Nlu61M{+!nEAHy?&ob;xN81rnGt76r*vGXfvSKEtTE4b>6S z>_MLN_%tfKO)w}kg3z{~OQzT97h1)RJ=}sAbGs6Y0g>|ja&7lb&5bmnN-T=n==)k;_+uW%%yo8UGB+qNte0D(U94Ja&p(>Z z=>`3*w2o(RF{RTZLZ#%v<14AC*7h4|_IR$=6LC}*g4Wlq3sm6dnKJ~g9jL!$4v~Dd zDtEJ(i-N#ajn3p>jy27H4pexY7X|M66fr;7Zd5&skkeGzK00J{ygy~`FBU@p3#5ks z&KVht>pLRmZfXBooqn?%y(~%BjFyz5UT1c}dFi_i z`Iz#n@P`6nWkfugk0BZ4Lj!gFh}!BF-|$*#2)gw7Zf@e>Rh8ij^x5)n9a(-F&Gj)-! zA)`lI`sZWOfXwQzU!eMm)~vAzOKUQ45^_zJ-kAbhc%1dj@3B#$Uc=~ov6@pErGYjr z%9dbj=%`z~rQN2uWj}t^Xv3#}swGQv(O&I}ufEn?nAluJM{9MzlKLsMQF0~WxqkyX z*`0IDmR? zNU~bo%@{M?oSt2&`Q%-~6}J(}_iol3O3Tmpuqxo9$}Cl`1mdu?J_o7+)YGH}WW$$$ zKyi(ElMJZYFbS31YI$6>U=qkHM*YY=?zEEBsXI+z;v>b_w4aO66F+le`zx^DH>ack z6I_!GXaAx|=(PjNTmvCS`fNa?_6+oHe6mkNlN)Wlka1w0io`cm$UVLNrc`!xpR2N zL_){b|5d%6F+M%8M>N=^u4o0+d=Fee1^TPX^2JmnHW%vq`jlUCo#Jko^_ac}R`^xR zjng*mwXV8sNNzbK&y5SMmaSpwR<2>_R(stGDcNl`U8!B|a94kBZSfbZA+^dsVLOKF z{d0ID8IG4<4P`G$Qhf!v2~_eScE~>wdPM6zPes_v%d9Ou2|ica@@=bbbX_@r+@8rb zCUS0Wzu*tsU;wou!_InU>g0pw6>$e2*rs|CWIpfDGHK{X2ATJ%bZCC;ADPADu=Jp7 zpd=obJ}+u{je5ljeLz*uMPbin=IHVfI^w6e96M{0K&0xkMwdC4%PmiMSb1o1mXg;H zie(xKHNTN1;>=^6Q-$Xzd1UL#gbW|kPl;0nzS zm$ZX_AJ$eH{^44Ojxz}aMNJ%&2(?%Y3(}qu$UdIKZ4jrY%bfg}jEG&zPcl0X`*glc z3Ub642b~wa!nu-KA}m-6f0FLEN4&mrzm3tl-@deYcbGZBsInS5O?N64$R}o(zBF0Ms&T5_PATygUZvH)5_`xSH zEDmdPgtoLQe$uOI?JXmQ7kx`*ipp1aGAq#KexZx@a&38<$(-4KoF4zvK2tpElFfSW zaD^*bu|)2a$NrJ|UVDViTI7nl%4o1kkl05SK$?^}?o*2kAt+H1e{w6=I}+)SZI6Qv zs}bf=mSBL;#{~uAKqCL`$aoZiO7L8Uwx1)C6PCAp>J$MEXBn1+4=;;>0Ta~ynWUjF zVv6arge!wpauaP_Q!ag;js(A45Dw;_~^#$s9y3ud+7zFH;e=r-n)Cc&e4H8m4ISuIBxd_z)J-xB7#9UY5eao7bD+|&dTjPF-L(xv%s zj>-$TpyP2(6fFG-18^cTYGsfY?>|svrni`o)s;!4K0-QQW%ZZEfAHKq&qfid?7z&w zbOWyLo@n{-Em`84fE$Cw*2}foPeRKBeS<`|pJUN5bZ8V5t$L!_6_BDd-*&pBUefQhe25!0QUlaqM?D0|)IM9OGbWHs63#7f=XEZza;Qg?kn9gF% z%Vs8TK06odew6K|*Q(!fHPTaE(!WoWt!c-e7L=}`5%2F zNlUPN@%OB>^Oo`S<3;9AkMw#C*&sdbk()iBfhMN06y|KFF$v!o()HomI89Y${x^X^ z8Q;BE8)AJ4o?UL93|RG~t+4X2`E)#-)E>z=z?+%HNA2sIW$dfv-iF1bU+YOgYDQ&I zSRFw3>pH`vj3HX3KWUTtpUzJIT4MEmZ|GLb?x~=IWhKzX=BY>O_iQn~j?;rD5{&ch z@r=`*=!lJBqGXHPQHD!++zut+SQUO2ef;J(hpz<$fKKh5J%dN8@?(lDJ z*+ShA%%8&!9Yy>G%VAyopSS-ed_DwxZwqS@O|gj~j3ploL1%R3f8AbstK4pv$+`#P zeu`yiP@0&CkBp8M7>9oQ8uQ6`HG-M#uMia=8>5d?1@8_EETXE_!J!LN0F5*`KB_~e zd*A*U*T$$Z(#F&=3U~(oG1=+2c~@RtexU9qD5|htgR(?;{JEoco^2Y>*%tkimNeb0NsjWtgJiQrPC zr>h!KY6S%s43P~WJ9&1yi|FuK8MnRv?wqVVO}IFHNe(Nj8f-%IZcr?zT769*@mgY@ zwWPa*V~QzixYqu@8M8OQuO!EVv>)n$R7lPU;)4EtlgyQL^piI<_j4;~fomYMV;Y2V zV6x`$GHlFLXXeBss{z9<(YczKYFg)gpd~ITXxr!y+FF@7v-=X_Z5}Uld*X~D(jRD> zFJUe#OND0lT_n-lVo-16|Cn1NPvk7AZQJnzAxEdd^vIBFzRf~a(#s4p>h}4PRG+D; z{|>@LW$+f#a)&dRnCTLy4~fF4wVCIHdOV4M03tGx@)^y~fc=-;3*^8%*=)0MSGah> zb!bXWdtT~$^4NLtuwHFXjpm~(tBN(&Vkxo^7DMRtbnTm=)PDSBKsWhDWOKCnmb0B= zrz=4|=lt#_E~3}z3g`EJ#zvhUB8l?*KijX~!7PU{_vXvhVY%sPvz@u7%B}j`xd`s! z-<)r>Pq&+=KJOcJvy(<%v{A($cGpYt83I{XiRjA@l$2y1Jq~)rpNX0X^YV;S%&v zkCy#20UNFR^-kDq^y{|crIBXnXipPh)PGi6h?#GMA%KI(Xmg#OVNm|zwLdL+i zsTrJ(m0l#s%7_kH-NeWQJFQx*`M`dWYufPDLk$4pOg*oxWHzU#;!8a1_GJ`$&y|v~ z42l4fZ?&GJPI$abqwkw?jjj`+#3idvc?v^8V=^)axr^4 z(-ze-=XPQ|EUd98@*bEa85)qyR`>OgU@pSrtYp@az1qJI7)If<#I0x1cP}Q|sXfz4 zWdy=a^F7LIckX4;cQl$#l*z2ciEl_)nCzwUzV~OZ0ik`k!>w_h_-!T+4Wr zrDQB1k%V4JnfN(Ufzp^U%*bQlqe)QdC^DEX@&a$C|gij0s_h z>S;2$)i&5Ht$gO$eCd0`NQ2jrlfiK`QuKg~zn#(0jU^@30bGZS22*Epnhp6;7lUsO z1mK3A91E&6>#L5E(RdTZ`aH?`VsESl!bJ6Kg~0?@BvpY4abBI;mR}AXp>FttcTAIe zz^l*yFh++afVx;J7)+Y3++5A`&$6O!&n%tUo2_hX}ycrB*?J4QdsY}trSK+!pv z6V$&Qo7kFfaZ^V!nDf*qaY!d##2O$Y5G;dTMWaJXD%!g~>^tBWazeCfxiCI^{NRoe zmSQM+Jk4~u1Pe$r=24rtYA}>mEq0Lkj8>?T{+ohrUmko8Tsg-# zV7J(Q2rzYgvNX?$BLr#NW(_fHb==D(w{hZ)XjzRts@rdx3YqQ2;0ha@Lb!e2dNRJ9 zqHi53gbxd|!_8vf`_Il*IR5-LvmvcF+7taYxgsMx6{-@JWrojo$RSFQM@%TFqP{-_NT=_(cVlF7 zloT`4AmdL)%)=07#C}gH=|wtjXQdh(X&4&yU~~3hmd8}f3K7?=FeYbBr#n_qgeM{A z%p_F(UOWHCx=xqRTM#ildWLHo%jH|Y8@7-oSUg|=uW{=)Qze%o0>YfQ#mJ~u=v%it z(_3yo6Z%v8^-6R9H-AZ~(djRsAZoB4Yrw5lxqXrF%lWusn zKb#K?uzcX~G%_VpMwfV%a*7+NFv%#DU=|>kOJIc=SK-C3d1Z+Skoo=)030MpuUc<5 zXXd`&*U-P%V`)jl({P^+_`Y9E3;j3m+fB`p=~C5HQqYd628-W;D45J4Q{DP*9+f*2 zl-Vs1B=XUokeeBeQ^)*a={MK5bk&(J@i$sc<13p}lQf#F=sIG$xG_xfF3oe5T#?V^ z=Z$+{)0(XNdA@nt!>;45=x!^lI7EIY<4=#)EZ-sIE{6?@`>VxpfF$}jJJJ}wf>myo zr^Z&1wB1co!F5kU5Fz6Uz?x4+L4kqKE%o-_zt7$|H}xIKHGgJr46rx3@j9f26F&9^ zML}il>4~GmyI8-jv7xGB14#!0rgtitnF?(ZW8lMXLgci`-13fezYu0m?t)lAc@Px7 zR`8HqE7;FS{^*9ey){{jrRuFdb;gdMThBEsGLk^BLbJZOYW|PquY`0Qn*|wAw@29@ z)xW!W7$+5Y#ZBD?5~n=xH?6W>nPf5JR(4(EcS_x3rwYaA%W5H<@y`&cCkxT)g+6Aq&bc0e0D5s=AEOVXR_2Aeqc75$nqKm8NLE*_6G+?4t>G- z_e`e}1MIk(<#DWkIwHL;68lCMX!_|D{7cqZs>+qf(;YqtCbT;cKo6L(P=NnL|AWuo zFK8Hc1le^fv0H{b_LXLXu*<@neek-Dx>2#iQK>i8r%o5~((WE*^*kOKXm?>zM(_Ew zE$s0fok-!&H)5QocylVMxid8eUguucq$&kh9Qd;vzJDO5NzH*yKi?GGHR8)3Ye%A;(6nH*V zi%OD-hB)u9F?4O`f%E;J%K?NImAu}($EoO4aMr^lIgKl=E=Wl47c8549TBaW3LU+@ z80CKwP=4UB_F|~#jB=#rDy(sOn3xS^RDKMgFo}Y?D0G^;BXZxT_1!%_X|09CMBl0i zj`H0;CUE`kHX^$3|yC&r<;?OjcC*lM1w-49R6LY*&Quc(;n2xfrye^ zRAy)X$A(c1rnh$Uf`)NJ%A*QeFO&Xx_(RC(J&i;!F8JO#n|dG zl90}Hft!%YO?^|PUQ3XB-vFf2B(}(v_4MBJC0F;@?^MNNpUe)Ws^e$Yg@SCTPsc5^ z_pJ@4xLSda)uo@Nu(*v9{PE=6T1*iI3OT!fYZQ+9?vzNFm@+3S;pzL&wi9n_)Ow+n z-ba{T^rmp^X$nhRp^6-)(Oy9d< z^R}z}mHRWzk^ND!)@!=?;I*e|F|_hefpcEQCn4s@*p@$q2r|=;n(w;o(}<>xxZS={!ZR_zRuOC z87mByCQ{u$E|6AQo3c(@3;u2z9snH$*ST~5UHQh$Q^xLRSx~WfE*P}CoNb%R!|tS; z;0kIrgm(+EV5!T=CvQt_b;sCduX?Px!!lp<33O~m*UQ(Nb=g%rDudyeX^S0h@wiu9LvZT^>$yI<_Bi_FypB@_XMdJGHgcWJ&sY8fSS!kk65_1zrtF}7Q@XWJ@zY(d{{wB1e;n}$Pijm4t z%tutEt~!v;{5?V>$kTpkw4a8lqWIZAK$X#km4i#)??;L$2WJ6#-f!%FsF^Xhjo`eg zx>nDj>EnfB@H3F@U^0u2y1z6k{S5QwW#*em7-!>Mv75rxs$S$#&sB;j=V{s&{z3t< z+!kQ2ca-V*ld;IC{qrvpUW+(cmeW$Wd^T%55J-N9!K+9!%>X_djjT<5q@+m}+ME z9gXdK<=qaImH+0)Z_lk-~TE_n-k3Ej6<%`_eE17hJQR z>`*Rui=dn4pUlCl(M4JF{E%LsE{58Ua>Ok>5Rio4;o(v$&E84`*1h+f3+kj#heV4p zkzvb=b8*k5>fRM!Py?1o_fPXsTzfp`wj|^@xTzo}y_jIS5>Fc|egTp!s`_KvCuJDm z9JP{Q-tS1>pMItu%u^Ci;@$W?O*tp1&b8_OXxVUN;PNR9Xm9uAb+MKj^H*RXXSgwG zNU{*I%!Mn4pdfY`b8H0*tfNcXD&XHXi#-TB`tO$#b&kYXGVD+4&3O1pYuy$D`T^r# zRp3BU_FwOn(F;)hB8kXh+FxfU|2>Ji3zQW~LX6i_(~k(0eRLxZR={-NVVFF7kvC!; zEubosn*ImP%k95$n)}ccrY2IBkUftOevNbd*LU-( zkBIqOD~oRg`9F!+c-&6w!^6X!#Y}FU*B6-=IzDFsRW>60^wf-=Z3g|0&~7o<^Tz)W zx+qoGQ&W^v4ymkaxIFCnzMgfi>`~tnt>1+NM}Aa`EHFawJ#Y9yu|vzC?#O^0FhJ!? zG)((My5i16ESy-b!a={}Ae`iRFctT}9}VPM5S1=m6=`-s`*h#9eGKk;>OMFdTtI(; za^Q-Zh?3C=smlXSBooMLNs?4a)PmGu(HM!V`m*WmCQiG*M)Iz?3ATt3DXF3!Ji{P! zDI4qO#qHePH<_i(UkcAi&T3PJ)DM1OQE2f?ima#Sw zqx{6$IG4g`q#h9+k%SI9W-p|K_W;dhH=7kpZ^e(31@<>tml=aoM>T zg$jt-D79JV-D^HKKsN&*DPo9|RKv+=-gM~z@9mHvE3&ISnOXXoEqjj?gDn3v(ib!q zm}u1~+4>p!>-6JZ0!whX*Vqb*HPbVwou%7nDHO7`tE-Vd$MY4T9uAi0qliTV1+72! zMszQ6bFP?z_n^f4=P_~=yD^BfCn>YnVdXYvPKg>w)Dcn8Wr*2hN1YM@nvGGza&zdqad9@zwFykzqYEl=tfJqnu*|?% z@Uk!Sz#1>Q=M_g@&p1Bm4s2uV)l{#pb;pcBeN%mBNq9cz;hyg(6v4{O)XaW5d+X?w zj_Y>SD$?Ej1ef!Ix>p5dH%3ks=hsE$UVEmmM2+o5n&YlLr4r%r@lH^fB$-}8cCMlm zb#~_8G3kC9_HAzhoL25_0!B=4p^Lb>?L94k{&2~DFf6GyV9W9Gq5fV$Yi zR2S;us_zC3+ToPO7>Ca>E){1rYN1a%I&dP2dN>-dFPJ5$tR|Mtt=Eyz+ki6D^_Pkj zoV9lRFO(&_3X{F55dQ09tNYWW;VPlFfSdj|wYc0+43^HI7DU-m42xIZd*Am)ViX9r z4lJL;R6(f{db+A4H)6Lk0|8?e?k*;H=OQKuKNMm}QOklp;{IRG7sz|fYt;t}d&A#J zkAP8WM^l8Eji@_AhNDw`n(w1bmeY`f^?GBF2i%|A{fQUrl`bS3@lIUB?9n0B)HB<6 zfeqe6Hfv!j#twH}HLBQQW(1S%E7+PW%Kd>Mu6tkU_Yt*Y_7{UUs#0i+23~G3?a~6n z>Y6AquO2>BW$FOt&OcbH6f`5uZI`PS@;ol^LD2Vtl_?YYDO!*t&}sv$IQ+F-;E|pV z{Y^&=#~Vl&+ubIFiA+kHqvEgm4F=z>EGhPpz96lcvVO}Lc?|14LJfWz+f5GWBZb_atzVPw5Qsdbf@Iew9 zEV6&Mr@Zub?2sl(m`@!)+!&AgoRlcZ$bzKf#_?NOPEwLc-Vs86LC8Iu3qq00mrrZd zm=s?53=ptm&=~D=1J!!m5$;w8!2RNA#g~@$eZ7-vd#agcKG;{m_gjZi6mX!&siRK8 z*jIkTY@DM8Jq$R;;S~Ia)p%zoBA`cQlG2f8Zju{r=E3LkWu}tHpV%Koqn;S&IqsQG zDgAiU5~6;Go9Bc9t=;yodh>mD?@CaVHGuo-!~F})zSSYsS|?%lAE?t4Gb zW-0EqAp7SqY}ef%g(GBs@%UG{(aS<;71S9aZ|F3&GVHYTLdE7dAbji8@cEIa_1_1` zA3peZQuAId;6#f5X5acx%$@u zgie2NTK1nu%^=!KMmC~XwU%BFK@x<^^8FUlZE7B7Xj{0lAk=Ml{uk6ZZU)YN-dLU~ zk0@9AGG9WHEwk@R9aqE70TS!F#+Thr^kJbAW_PjqUqV8Te=Y>AKMjF{A_sI;@iFWm zXeTG?NM4vRZyd`%as8JqMenllw?G9xE~x<}#MYJ@u@hzU7OmRM zfWRXm6L{G$Gn!1Eg5J38>(LqPd0zQU8^O!ZD8NXmvf8kyV78ToE5=eKvEQnAh{qg> z+u1dqRtw?A2D-4{_dyPP{BUz?y=?eUbh+qLAh-%5p*d0IzxltIUux7837xq#mNAN~zO`Tu<4s~=Lci1j7dLGPSUoY`8NJ^V(zJf;>z=vmS{v-tpc z=N9#vB)vzPKFHe;%Bu9LgQ2zbldDF=|C|Th^5@P{aKmqF?;CFh*y|@qk{~8&$Hcm0 zr+ewk`Jk+rradSXup;J)ci*zxriK6Vc zKfW@IiNV107tH$Kpy;$74Zbu%jBzIZ;c_xL!3Vt^(zpFiE!GC|>`$RUeW*b^?aip~ Qvs+*?5{ly0q6UHg2R%YkvH$=8 literal 0 HcmV?d00001 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 %} -
-

Portal handoff failed

-

{{ message }}

-
- Please return to the OTB Billing portal and try launching OTB Cloud again. +{% block portal_content %} +
+
+

Portal handoff failed

+

+ OTB Cloud could not validate the portal launch request. +

+ +
+ +
{% endblock %} diff --git a/app/templates/auth/login_required.html b/app/templates/auth/login_required.html index a1cc79e..dcb89d5 100644 --- a/app/templates/auth/login_required.html +++ b/app/templates/auth/login_required.html @@ -1,15 +1,28 @@ {% extends "portal_base.html" %} -{% block title %}Portal Login Required{% endblock %} +{% block title %}Portal Login Required - OTB Cloud{% endblock %} -{% block content %} -
-

Portal login required

-

- OTB Cloud is available only through a signed handoff from the OTB Billing portal. -

-
- Please return to the OTB Billing portal and open OTB Cloud from your Services page. +{% block portal_content %} +
+
+

Portal login required

+

+ 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. +

+ +
+
+
{% endblock %} diff --git a/app/templates/cloud/dashboard.html b/app/templates/cloud/dashboard.html index 5cc42f1..a36e9b3 100644 --- a/app/templates/cloud/dashboard.html +++ b/app/templates/cloud/dashboard.html @@ -2,37 +2,76 @@ {% block title %}OTB Cloud Dashboard{% endblock %} -{% block content %} -
-

OTB Cloud Dashboard

-

Authenticated user: {{ user_email }}

-

Tenant slug: {{ tenant_slug }}

-
- -
-
-

Devices

- {% if devices %} -
    - {% for device in devices %} -
  • - {{ device.device_name }} - ({{ device.device_type }})
    - {{ device.relative_path }} -
  • - {% endfor %} -
- {% else %} -

No devices have been created yet.

- {% endif %} +{% block portal_content %} +
+
+

OTB Cloud Dashboard

+

{{ user_email }}

+

+ Secure backup and storage dashboard for your account. +

-
-

Current scope

-

- 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. -

+
+ + +
+
Logged in as: + {{ user_email }}
+
Tenant slug: + {{ tenant_slug }}
+
+ +
+
+
+
+

Devices

+

Registered source locations for uploaded data.

+
+
+ Active +
+
+ +
+ {% if devices %} +
    + {% for device in devices %} +
  • + {{ device.device_name }} + ({{ device.device_type }})
    + {{ device.relative_path }} +
  • + {% endfor %} +
+ {% else %} +

No devices have been created yet.

+ {% endif %} +
+
+ +
+
+
+

Current scope

+

OTB Cloud is now operating inside the branded OTB portal shell.

+
+
+ In Progress +
+
+ +
+

+ Next steps are the searchable file library, bulk upload endpoints, zip export, and media processing jobs. +

+
+
+
{% endblock %} diff --git a/app/templates/includes/otb_footer.html b/app/templates/includes/otb_footer.html new file mode 100644 index 0000000..5858f0b --- /dev/null +++ b/app/templates/includes/otb_footer.html @@ -0,0 +1,2 @@ +{% include "includes/otb_statusbar.html" %} + diff --git a/app/templates/includes/otb_statusbar.html b/app/templates/includes/otb_statusbar.html new file mode 100644 index 0000000..c13121d --- /dev/null +++ b/app/templates/includes/otb_statusbar.html @@ -0,0 +1,14 @@ +
+
+ All billing is calculated in 🇨🇦 CAD + + Crypto conversions use the OTB Oracle + + + Methods: + Credit Card (via Square), + e-Transfer, + and enabled crypto assets + +
+
diff --git a/app/templates/includes/site_nav.html b/app/templates/includes/site_nav.html new file mode 100644 index 0000000..6eb4baa --- /dev/null +++ b/app/templates/includes/site_nav.html @@ -0,0 +1,42 @@ + diff --git a/app/templates/portal_base.html b/app/templates/portal_base.html index 9214f13..0ec24bb 100644 --- a/app/templates/portal_base.html +++ b/app/templates/portal_base.html @@ -1,114 +1,25 @@ - + - - - {% block title %}OTB Cloud{% endblock %} - + + + {% block title %}Portal - OutsideTheBox{% endblock %} + + + + {% block head_extra %}{% endblock %} -
-