rs_maps/frontend/style.css

472 lines
14 KiB
CSS

/* frontend/style.css */
/* ─────────────────────────────────────────────────────────────
rs_maps — chrome for a map, not a dashboard.
Palette taken from Ordnance Survey sheet conventions:
rights-of-way green, road-fill magenta, contour bistre.
───────────────────────────────────────────────────────────── */
:root {
--ink: #0F1A17; /* deep bottle-green black */
--panel: #16231F;
--panel-2: #1E2E29;
--hair: #2C433C; /* hairline rules */
--paper: #E7EDE7; /* cool pale sage */
--paper-dim: #9DB0A8;
--moss: #4E9C6B; /* rights of way — primary action */
--moss-lift: #5FB47D;
--accent: #D2467F; /* road fill — shared / accent */
--sun: #E2A93C; /* live location, warnings */
--alarm: #D3574B;
--r: 3px; /* map furniture is squared off, not pill-shaped */
--shadow: 0 8px 28px rgba(0,0,0,.38);
--sans: ui-sans-serif, "Inter", "Segoe UI", Roboto, system-ui, sans-serif;
--mono: ui-monospace, "SFMono-Regular", "JetBrains Mono", "Consolas", monospace;
}
* { box-sizing: border-box; }
html, body {
margin: 0; height: 100%;
font-family: var(--sans);
background: var(--ink);
color: var(--paper);
-webkit-text-size-adjust: 100%;
}
[hidden] { display: none !important; }
:focus-visible {
outline: 2px solid var(--sun);
outline-offset: 2px;
}
/* ── trig point: a small pyramid glyph, the app's only ornament ── */
.trig {
display: block;
width: 34px; height: 30px;
background: var(--moss);
clip-path: polygon(50% 0, 100% 100%, 0 100%);
margin: 0 auto 14px;
}
.trig.small { width: 20px; height: 17px; margin: 0 4px 0 0; flex: none; }
/* ───────────────────────── auth ───────────────────────── */
.auth {
min-height: 100%;
display: grid;
place-items: center;
padding: 24px;
/* faint contour rings, drawn not imported */
background:
radial-gradient(circle at 22% 30%, transparent 0 78px, rgba(78,156,107,.10) 78px 79px, transparent 79px),
radial-gradient(circle at 22% 30%, transparent 0 128px, rgba(78,156,107,.08) 128px 129px, transparent 129px),
radial-gradient(circle at 22% 30%, transparent 0 186px, rgba(78,156,107,.06) 186px 187px, transparent 187px),
radial-gradient(circle at 80% 74%, transparent 0 96px, rgba(210,70,127,.09) 96px 97px, transparent 97px),
radial-gradient(circle at 80% 74%, transparent 0 154px, rgba(210,70,127,.06) 154px 155px, transparent 155px),
var(--ink);
}
.auth-card {
width: min(420px, 100%);
background: var(--panel);
border: 1px solid var(--hair);
border-radius: var(--r);
padding: 34px 30px 30px;
box-shadow: var(--shadow);
}
.auth-head { text-align: center; margin-bottom: 26px; }
.auth-head h1 {
margin: 0;
font-size: 30px;
font-weight: 600;
letter-spacing: .16em;
text-transform: uppercase;
}
.auth-sub {
margin: 8px 0 0;
color: var(--paper-dim);
font-size: 13px;
}
/* ───────────────────────── forms ───────────────────────── */
label {
display: block;
margin-bottom: 15px;
font-size: 12px;
letter-spacing: .1em;
text-transform: uppercase;
color: var(--paper-dim);
}
label .opt {
text-transform: none;
letter-spacing: 0;
font-size: 11px;
color: var(--paper-dim);
opacity: .8;
}
input[type=text], input[type=password], input[type=email],
input[type=search], input:not([type]), select, textarea {
display: block;
width: 100%;
margin-top: 6px;
padding: 10px 11px;
background: var(--ink);
border: 1px solid var(--hair);
border-radius: var(--r);
color: var(--paper);
font: 15px/1.3 var(--sans);
text-transform: none;
letter-spacing: 0;
}
textarea { resize: vertical; }
input:focus, select:focus, textarea:focus {
border-color: var(--moss);
outline: none;
}
label.check {
display: flex;
align-items: center;
gap: 9px;
text-transform: none;
letter-spacing: 0;
font-size: 14px;
color: var(--paper);
}
label.check input { width: auto; margin: 0; }
button {
font: 500 14px var(--sans);
border-radius: var(--r);
border: 1px solid transparent;
padding: 10px 15px;
cursor: pointer;
color: var(--paper);
background: var(--panel-2);
transition: background .12s ease, border-color .12s ease;
}
button:hover { background: var(--hair); }
button[disabled] { opacity: .55; cursor: progress; }
button.primary { background: var(--moss); color: #07140D; font-weight: 600; }
button.primary:hover { background: var(--moss-lift); }
button.danger { background: transparent; border-color: var(--alarm); color: var(--alarm); }
button.danger:hover { background: rgba(211,87,75,.14); }
button.ghost { background: transparent; border-color: var(--hair); }
button.ghost:hover { background: var(--panel-2); }
button.ghost[aria-pressed=true] { border-color: var(--sun); color: var(--sun); }
button.small { padding: 7px 11px; font-size: 13px; }
button.link {
background: none; border: none; padding: 4px;
color: var(--paper-dim); font-size: 13px;
}
button.link:hover { color: var(--paper); background: none; }
.pane .primary { width: 100%; margin-top: 6px; }
.switch {
display: flex;
justify-content: space-between;
gap: 12px;
margin: 16px 0 0;
font-size: 13px;
}
.switch a { color: var(--paper-dim); text-decoration: none; }
.switch a:hover { color: var(--moss-lift); text-decoration: underline; }
.note { margin: 10px 0 0; font-size: 13px; min-height: 1em; color: var(--paper-dim); }
.note.bad { color: var(--alarm); }
.note.good { color: var(--moss-lift); }
/* ───────────────────────── app shell ───────────────────────── */
.app { position: fixed; inset: 0; }
#map { position: absolute; inset: 0; background: var(--ink); }
.bar {
position: absolute;
top: 0; left: 0; right: 0;
/* Above .rail (500) so the account menu isn't painted behind the
Places/Routes tabs. .bar sets a stacking context, so raising .menu alone
would have no effect — the parent is what has to come up. */
z-index: 700;
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
background: linear-gradient(var(--ink) 55%, rgba(15,26,23,0));
padding-bottom: 22px;
}
/* The bar's gradient extends 22px past its contents. Now that it sits above
the rail, that transparent strip would intercept clicks meant for the tabs
beneath it, so the bar ignores pointer events and its children take them back. */
.bar { pointer-events: none; }
.bar > * { pointer-events: auto; }
.search { display: flex; gap: 6px; flex: 1 1 auto; max-width: 420px; }
.search input { margin-top: 0; background: var(--panel); }
.search button { flex: none; background: var(--panel); border-color: var(--hair); }
.account { position: relative; margin-left: auto; }
.menu {
position: absolute;
right: 0; top: calc(100% + 6px);
min-width: 180px;
background: var(--panel);
border: 1px solid var(--hair);
border-radius: var(--r);
box-shadow: var(--shadow);
padding: 5px;
display: flex;
flex-direction: column;
}
.menu button {
background: none; border: none; text-align: left;
padding: 9px 10px; font-size: 14px;
}
.menu button:hover { background: var(--panel-2); }
/* signature: map-margin grid readout */
.readout {
position: absolute;
left: 12px; bottom: 12px;
z-index: 500;
display: flex;
align-items: baseline;
gap: 8px;
padding: 6px 11px;
background: rgba(15,26,23,.86);
border: 1px solid var(--hair);
border-left: 3px solid var(--moss);
border-radius: var(--r);
font-family: var(--mono);
font-size: 12px;
backdrop-filter: blur(3px);
}
.readout-label { color: var(--paper-dim); font-size: 10px; letter-spacing: .14em; }
.readout-val { color: var(--paper); }
.rail {
position: absolute;
top: 62px; right: 12px;
z-index: 500;
display: flex;
gap: 6px;
}
.tab {
background: var(--panel);
border: 1px solid var(--hair);
font-size: 13px;
padding: 8px 13px;
}
.tab[aria-pressed=true] { border-color: var(--moss); color: var(--moss-lift); }
/* ───────────────────────── drawers & sheets ───────────────────────── */
.drawer {
position: absolute;
top: 104px; right: 12px; bottom: 12px;
width: min(330px, calc(100vw - 24px));
z-index: 500;
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px;
overflow-y: auto;
background: rgba(22,35,31,.96);
border: 1px solid var(--hair);
border-radius: var(--r);
box-shadow: var(--shadow);
backdrop-filter: blur(6px);
}
.drawer-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.drawer-head h2 {
margin: 0; font-size: 13px; font-weight: 600;
letter-spacing: .14em; text-transform: uppercase;
}
.sub {
margin: 6px 0 0; font-size: 11px; font-weight: 600;
letter-spacing: .14em; text-transform: uppercase; color: var(--paper-dim);
}
.hint, .empty { margin: 0; font-size: 13px; color: var(--paper-dim); }
.list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.list li {
display: flex;
align-items: center;
gap: 9px;
padding: 9px 4px;
border-bottom: 1px solid var(--hair);
font-size: 14px;
}
.list li:last-child { border-bottom: none; }
.list .dot {
width: 9px; height: 9px; border-radius: 50%;
background: var(--moss); flex: none;
}
.list .grow { flex: 1 1 auto; min-width: 0; }
.list .name { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.list .meta { display: block; font-family: var(--mono); font-size: 11px; color: var(--paper-dim); }
.badge {
font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
color: var(--accent); border: 1px solid var(--accent);
border-radius: var(--r); padding: 1px 5px; flex: none;
}
.row { display: flex; gap: 8px; flex-wrap: wrap; }
.filebtn {
margin: 0; padding: 10px 15px;
background: var(--panel-2); border: 1px solid var(--hair);
border-radius: var(--r); cursor: pointer;
font-size: 13px; letter-spacing: 0; text-transform: none;
color: var(--paper);
}
.filebtn:hover { background: var(--hair); }
.local-actions {
display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
padding: 9px; border: 1px dashed var(--hair); border-radius: var(--r);
}
.mono { font-family: var(--mono); font-size: 12px; }
.scrim {
position: absolute; inset: 0; z-index: 900;
background: rgba(8,14,12,.55);
}
.sheet {
position: absolute;
z-index: 1000;
top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: min(430px, calc(100vw - 24px));
max-height: calc(100vh - 48px);
overflow-y: auto;
padding: 18px;
background: var(--panel);
border: 1px solid var(--hair);
border-radius: var(--r);
box-shadow: var(--shadow);
}
.sheet form { margin-top: 14px; }
.block + .block { margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--hair); }
.current { margin: 8px 0 14px; font-size: 13px; color: var(--paper-dim); }
.coords {
margin: 0 0 14px; padding: 7px 9px;
background: var(--ink); border-left: 3px solid var(--moss);
border-radius: var(--r); color: var(--paper-dim);
}
.actions { display: flex; gap: 9px; margin-top: 6px; }
.actions .primary { flex: 1 1 auto; }
.swatches { display: flex; gap: 7px; margin-top: 7px; }
.swatches button {
width: 26px; height: 26px; padding: 0;
border-radius: 50%; border: 2px solid transparent;
}
.swatches button[aria-pressed=true] { border-color: var(--paper); }
.toast {
position: absolute;
left: 50%; bottom: 22px;
transform: translateX(-50%);
z-index: 1100;
padding: 10px 16px;
background: var(--panel);
border: 1px solid var(--hair);
border-left: 3px solid var(--moss);
border-radius: var(--r);
font-size: 14px;
box-shadow: var(--shadow);
}
.toast.bad { border-left-color: var(--alarm); }
/* ───────────────────────── leaflet overrides ───────────────────────── */
.leaflet-container { background: var(--ink); font-family: var(--sans); }
.leaflet-control-attribution {
background: rgba(15,26,23,.82) !important;
color: var(--paper-dim) !important;
font-size: 10px !important;
}
.leaflet-control-attribution a { color: var(--paper-dim) !important; }
.leaflet-bar a {
background: var(--panel) !important;
color: var(--paper) !important;
border-bottom-color: var(--hair) !important;
}
.leaflet-bar a:hover { background: var(--panel-2) !important; }
.leaflet-popup-content-wrapper, .leaflet-popup-tip {
background: var(--panel);
color: var(--paper);
border-radius: var(--r);
}
.leaflet-popup-content { margin: 11px 13px; font-size: 14px; }
.leaflet-popup-content b { display: block; margin-bottom: 3px; }
.leaflet-popup-content .mono { color: var(--paper-dim); }
.pin {
width: 15px; height: 15px;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg);
border: 2px solid rgba(0,0,0,.42);
}
.here {
width: 15px; height: 15px; border-radius: 50%;
background: var(--sun);
box-shadow: 0 0 0 5px rgba(226,169,60,.26);
}
/* ───────────────────────── responsive ───────────────────────── */
@media (max-width: 640px) {
.bar { flex-wrap: wrap; }
.search { order: 3; flex-basis: 100%; max-width: none; }
.drawer { top: auto; left: 12px; right: 12px; bottom: 12px; width: auto; max-height: 52vh; }
.readout { display: none; }
.rail { top: 108px; }
}
@media (prefers-reduced-motion: reduce) {
* { transition: none !important; animation: none !important; }
}