diff --git a/internal/server/web/app.css b/internal/server/web/app.css index 2a5fff8..ef16a97 100644 --- a/internal/server/web/app.css +++ b/internal/server/web/app.css @@ -1,333 +1,258 @@ -/* - * apointless.css — reconstructed from the description in claude.md. - * - * If you have the canonical apointless.css, drop it in over this file: the - * markup below only uses the components the spec lists (cards, stat cards, - * badges, pills, buttons, inputs, tables, alerts, code blocks, spinners), so a - * real copy should be a straight swap. - * - * Fonts are declared as stacks with system fallbacks rather than pulled from a - * CDN: ipswap runs on laptops sitting on customer networks with no route to - * the internet, and a webfont that 404s would leave the editor unstyled. - */ - +/* Design tokens - kept as CSS custom properties under these exact names so a + real apointless.css can drop in later without touching any template. */ :root { --bg: #0b0d11; --surface-1: #12151b; --surface-2: #1a1e28; --surface-3: #232838; - - --text: #e6e9ef; - --text-muted: #9aa3b2; - --text-dim: #6b7382; - + --border: #2a3040; + --text-primary: #e6e9ef; + --text-secondary: #9aa3b2; --accent: #3b82f6; - --accent-hover: #60a5fa; - --accent-dim: rgba(59, 130, 246, 0.15); - - --ok: #22c55e; - --warn: #f59e0b; + --accent-hover: #5b95f7; + --success: #22c55e; --danger: #ef4444; - --info: #38bdf8; - - --border: #232838; - --border-strong: #2f3648; - - --mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", "Consolas", monospace; - --sans: "DM Sans", ui-sans-serif, system-ui, "Segoe UI", sans-serif; - + --warning: #f59e0b; + --font-body: "DM Sans", system-ui, -apple-system, sans-serif; + --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, monospace; --radius: 10px; - --radius-sm: 6px; - --gap: 16px; } html.light { - --bg: #f6f7f9; + --bg: #f5f6f8; --surface-1: #ffffff; - --surface-2: #f0f2f5; - --surface-3: #e4e7ec; - - --text: #12151b; - --text-muted: #4b5563; - --text-dim: #6b7382; - - --accent-dim: rgba(59, 130, 246, 0.12); - --border: #dfe3e9; - --border-strong: #c8ced8; + --surface-2: #f0f1f4; + --surface-3: #e5e7eb; + --border: #d7dae0; + --text-primary: #14161a; + --text-secondary: #565d6b; + --accent: #2563eb; + --accent-hover: #1d4ed8; } * { box-sizing: border-box; } -body { +html, body { margin: 0; + padding: 0; background: var(--bg); - color: var(--text); - font-family: var(--sans); - font-size: 15px; - line-height: 1.55; - min-height: 100vh; - position: relative; + color: var(--text-primary); + font-family: var(--font-body); + min-height: 100%; } -/* Subtle blue grid behind everything. */ -body::before { - content: ""; - position: fixed; - inset: 0; - pointer-events: none; - z-index: 0; - background-image: - linear-gradient(to right, rgba(59, 130, 246, 0.05) 1px, transparent 1px), - linear-gradient(to bottom, rgba(59, 130, 246, 0.05) 1px, transparent 1px); - background-size: 40px 40px; -} +a { color: var(--accent); text-decoration: none; } +a:hover { color: var(--accent-hover); } -.wrap { - position: relative; - z-index: 1; - max-width: 1100px; - margin: 0 auto; - padding: 28px 20px 64px; -} +.muted { color: var(--text-secondary); } -/* --- typography --- */ - -h1, h2, h3 { font-weight: 600; line-height: 1.25; margin: 0 0 8px; } -h1 { font-size: 24px; letter-spacing: -0.02em; } -h2 { font-size: 18px; } -h3 { font-size: 15px; color: var(--text-muted); } - -.mono { font-family: var(--mono); } -.muted { color: var(--text-muted); } -.dim { color: var(--text-dim); font-size: 13px; } - -header.top { +.topbar { display: flex; align-items: center; justify-content: space-between; - gap: var(--gap); - margin-bottom: 24px; - flex-wrap: wrap; + padding: 0.9rem 1.5rem; + background: var(--surface-1); + border-bottom: 1px solid var(--border); } -/* --- cards --- */ +.topbar-nav { display: flex; align-items: center; gap: 1.25rem; } +.topbar-nav a { color: var(--text-secondary); font-weight: 500; } +.topbar-nav a:hover { color: var(--text-primary); } +.brand { color: var(--text-primary) !important; font-weight: 700; } + +.icon-btn { + background: var(--surface-2); + border: 1px solid var(--border); + color: var(--text-primary); + border-radius: 50%; + width: 2.2rem; + height: 2.2rem; + cursor: pointer; + font-size: 1rem; +} +.icon-btn:hover { background: var(--surface-3); } + +.page { + max-width: 900px; + margin: 0 auto; + padding: 2rem 1.5rem 4rem; +} + +h1 { margin-top: 0; } .card { background: var(--surface-1); border: 1px solid var(--border); border-radius: var(--radius); - padding: 18px; - margin-bottom: var(--gap); + padding: 1.25rem 1.5rem; + margin-bottom: 1.25rem; } -.card > h2 { margin-bottom: 12px; } - -.stat-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 12px; - margin-bottom: var(--gap); -} - -.stat-card { - background: var(--surface-1); - border: 1px solid var(--border); +.alert { + padding: 0.8rem 1rem; border-radius: var(--radius); - padding: 14px 16px; + margin-bottom: 1.25rem; + border: 1px solid transparent; +} +.alert-danger { background: rgba(239, 68, 68, 0.12); border-color: var(--danger); color: var(--danger); } +.alert-warning { background: rgba(245, 158, 11, 0.12); border-color: var(--warning); color: var(--warning); } +.alert-success { background: rgba(34, 197, 94, 0.12); border-color: var(--success); color: var(--success); } +.alert ul { margin: 0.4rem 0 0; padding-left: 1.2rem; } + +.btn { + display: inline-block; + padding: 0.6rem 1.1rem; + border-radius: var(--radius); + border: 1px solid var(--border); + background: var(--surface-2); + color: var(--text-primary); + font-family: inherit; + font-size: 0.95rem; + cursor: pointer; +} +.btn:hover { background: var(--surface-3); } +.btn:disabled { opacity: 0.5; cursor: not-allowed; } +.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; } +.btn-primary:hover { background: var(--accent-hover); } +.btn-success { background: var(--success); border-color: var(--success); color: #06210f; } +.btn-danger { background: var(--danger); border-color: var(--danger); color: #2a0a0a; } +.btn-secondary { background: var(--surface-2); } + +.upload-form { display: flex; flex-direction: column; gap: 1rem; align-items: flex-start; } +.upload-drop { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 10rem; + border: 2px dashed var(--border); + border-radius: var(--radius); + color: var(--text-secondary); + cursor: pointer; + text-align: center; + padding: 1rem; +} +.upload-drop:hover { border-color: var(--accent); color: var(--text-primary); } +.upload-drop input { display: none; } + +.tabs { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-bottom: 1.25rem; + border-bottom: 1px solid var(--border); + padding-bottom: 0.75rem; +} +.tab { + padding: 0.4rem 0.9rem; + border-radius: 999px; + color: var(--text-secondary); + font-size: 0.9rem; +} +.tab:hover { background: var(--surface-2); color: var(--text-primary); } +.tab-active { background: var(--accent); color: #fff; } +.tab-active:hover { background: var(--accent-hover); color: #fff; } + +.flight-list { display: flex; flex-direction: column; gap: 0.6rem; } +.flight-row { + display: grid; + grid-template-columns: auto auto auto 1fr auto; + align-items: center; + gap: 1rem; + color: var(--text-primary); +} +.flight-row:hover { border-color: var(--accent); } +.flight-route { font-family: var(--font-mono); } +.flight-number { font-family: var(--font-mono); color: var(--text-secondary); } + +.badge { + display: inline-block; + padding: 0.2rem 0.6rem; + border-radius: 999px; + font-size: 0.75rem; + font-weight: 500; + text-transform: capitalize; + background: var(--surface-3); + color: var(--text-secondary); +} +.badge-pending_review { background: rgba(59, 130, 246, 0.15); color: var(--accent); } +.badge-track_unavailable { background: rgba(245, 158, 11, 0.15); color: var(--warning); } +.badge-decode_failed { background: rgba(239, 68, 68, 0.15); color: var(--danger); } +.badge-approved { background: rgba(34, 197, 94, 0.15); color: var(--success); } +.badge-rejected { background: rgba(239, 68, 68, 0.15); color: var(--danger); } +.badge-queued { background: rgba(154, 163, 178, 0.15); color: var(--text-secondary); } + +.back-link { display: inline-block; margin-bottom: 1rem; color: var(--text-secondary); } + +.detail-header h1 { display: flex; align-items: center; gap: 0.75rem; font-size: 1.5rem; } + +.detail-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1.25rem; +} +.map-card { grid-column: 1 / -1; } + +@media (max-width: 720px) { + .detail-grid { grid-template-columns: 1fr; } + .flight-row { grid-template-columns: auto 1fr; grid-template-rows: auto auto; } } -.stat-card .label { - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.08em; - color: var(--text-dim); +.edit-form { display: flex; flex-direction: column; gap: 0.85rem; } +.edit-form label { + display: flex; + flex-direction: column; + gap: 0.3rem; + font-size: 0.85rem; + color: var(--text-secondary); } +.field-row { display: flex; gap: 1rem; } +.field-row label { flex: 1; } -.stat-card .value { - font-family: var(--mono); - font-size: 18px; - margin-top: 4px; +input { + font-family: var(--font-mono); + font-size: 0.95rem; + background: var(--surface-2); + border: 1px solid var(--border); + border-radius: 6px; + padding: 0.5rem 0.6rem; + color: var(--text-primary); +} +input:focus { outline: 2px solid var(--accent); outline-offset: 1px; } + +.meta-list { + margin: 1rem 0 0; + display: grid; + grid-template-columns: auto 1fr; + gap: 0.35rem 1rem; + font-size: 0.9rem; +} +.meta-list dt { color: var(--text-secondary); } +.meta-list dd { margin: 0; font-family: var(--font-mono); overflow-wrap: anywhere; } + +.bp-photo { width: 100%; border-radius: var(--radius); display: block; } +.raw-bcbp { margin-top: 1rem; } +.raw-bcbp pre { + font-family: var(--font-mono); + font-size: 0.8rem; + background: var(--surface-2); + padding: 0.75rem; + border-radius: 6px; + overflow-x: auto; + white-space: pre-wrap; word-break: break-all; } -/* --- badges and pills --- */ +#track-map { height: 360px; border-radius: var(--radius); overflow: hidden; } -.badge, .pill { - display: inline-block; - font-size: 12px; - font-family: var(--mono); - padding: 2px 8px; - border-radius: 999px; - border: 1px solid var(--border-strong); - color: var(--text-muted); - white-space: nowrap; -} +.action-bar { display: flex; gap: 0.75rem; flex-wrap: wrap; } -.badge.accent { background: var(--accent-dim); border-color: var(--accent); color: var(--accent-hover); } -.badge.ok { background: rgba(34, 197, 94, 0.12); border-color: var(--ok); color: var(--ok); } -.badge.warn { background: rgba(245, 158, 11, 0.12); border-color: var(--warn); color: var(--warn); } -.badge.danger { background: rgba(239, 68, 68, 0.12); border-color: var(--danger); color: var(--danger); } - -.pill { background: var(--surface-2); } - -/* --- buttons --- */ - -button, .btn { - font-family: var(--sans); - font-size: 14px; - font-weight: 500; - padding: 7px 14px; - border-radius: var(--radius-sm); - border: 1px solid var(--border-strong); - background: var(--surface-2); - color: var(--text); - cursor: pointer; - transition: background 0.12s ease, border-color 0.12s ease; -} - -button:hover, .btn:hover { background: var(--surface-3); border-color: var(--accent); } -button:disabled { opacity: 0.5; cursor: not-allowed; } -button:disabled:hover { background: var(--surface-2); border-color: var(--border-strong); } - -button.primary { background: var(--accent); border-color: var(--accent); color: #fff; } -button.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); } -button.danger { border-color: var(--danger); color: var(--danger); } -button.danger:hover { background: rgba(239, 68, 68, 0.12); } -button.ghost { background: transparent; border-color: transparent; color: var(--text-muted); } -button.ghost:hover { background: var(--surface-2); border-color: var(--border); } -button.small { font-size: 12px; padding: 4px 9px; } - -.btn-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; } - -/* --- inputs --- */ - -label { display: block; font-size: 13px; color: var(--text-muted); margin-bottom: 4px; } - -input, select, textarea { - width: 100%; - font-family: var(--mono); - font-size: 14px; - padding: 7px 10px; - border-radius: var(--radius-sm); - border: 1px solid var(--border-strong); - background: var(--surface-2); - color: var(--text); -} - -input:focus, select:focus, textarea:focus { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-dim); -} - -input[type="checkbox"] { width: auto; accent-color: var(--accent); } - -.field { margin-bottom: 12px; } -.field-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); - gap: 12px; -} - -.check { display: flex; align-items: center; gap: 8px; } -.check label { margin: 0; } - -/* --- tables --- */ - -table { width: 100%; border-collapse: collapse; font-size: 14px; } - -th { - text-align: left; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.06em; - color: var(--text-dim); - font-weight: 500; - padding: 8px 10px; - border-bottom: 1px solid var(--border-strong); -} - -td { padding: 9px 10px; border-bottom: 1px solid var(--border); vertical-align: middle; } -tbody tr:hover { background: var(--surface-2); } -td.mono, .table-scroll td.addr { font-family: var(--mono); font-size: 13px; } -.table-scroll { overflow-x: auto; } - -/* --- alerts --- */ - -.alert { - border-radius: var(--radius-sm); - border: 1px solid var(--border-strong); - background: var(--surface-2); - padding: 10px 14px; - margin-bottom: 12px; - font-size: 14px; -} - -.alert.ok { border-color: var(--ok); background: rgba(34, 197, 94, 0.10); } -.alert.warn { border-color: var(--warn); background: rgba(245, 158, 11, 0.10); } -.alert.danger { border-color: var(--danger); background: rgba(239, 68, 68, 0.10); } -.alert.info { border-color: var(--info); background: rgba(56, 189, 248, 0.10); } - -/* --- code --- */ - -pre, code { - font-family: var(--mono); - font-size: 13px; -} - -pre { - background: var(--surface-2); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 12px; - overflow-x: auto; -} - -/* --- spinner --- */ - -.spinner { - display: inline-block; - width: 14px; - height: 14px; - border: 2px solid var(--border-strong); - border-top-color: var(--accent); - border-radius: 50%; - animation: spin 0.7s linear infinite; - vertical-align: -2px; -} - -@keyframes spin { to { transform: rotate(360deg); } } - -/* --- layout odds and ends --- */ - -.group-head { +.settings-form { display: flex; flex-direction: column; gap: 1rem; max-width: 480px; } +.settings-form label { display: flex; - align-items: center; - gap: 10px; - margin: 20px 0 8px; + flex-direction: column; + gap: 0.3rem; + font-size: 0.9rem; + color: var(--text-secondary); } - -.group-head h2 { margin: 0; } - -.empty { - text-align: center; - color: var(--text-dim); - padding: 32px 16px; -} - -dialog { - background: var(--surface-1); - color: var(--text); - border: 1px solid var(--border-strong); - border-radius: var(--radius); - padding: 20px; - width: min(680px, 92vw); -} - -dialog::backdrop { background: rgba(0, 0, 0, 0.6); } - -.sec-list { display: flex; flex-direction: column; gap: 8px; } -.sec-row { display: flex; gap: 8px; align-items: center; } -.sec-row input { flex: 1; } - -.hidden { display: none !important; } +.settings-actions { display: flex; gap: 0.75rem; }