Files

1081 lines
19 KiB
CSS

:root {
color-scheme: light;
--bg: #f3efe3;
--panel: rgba(253, 249, 240, 0.75);
--panel-border: rgba(92, 73, 49, 0.18);
--ink: #1f2a2e;
--muted: #5f6b6f;
--accent: #1f7a62;
--accent-strong: #125441;
--shadow: 0 24px 60px rgba(52, 43, 28, 0.12);
--map-tool-size: 42px;
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
min-height: 100%;
font-family: "Aptos", "Segoe UI", sans-serif;
background:
radial-gradient(circle at top left, rgba(255, 255, 255, 0.55), transparent 32%),
linear-gradient(135deg, #e9dec4 0%, #f7f4ec 52%, #d8eadf 100%);
color: var(--ink);
}
body {
min-height: 100vh;
}
.message-region {
position: fixed;
z-index: 30;
top: 24px;
left: 50%;
display: grid;
width: min(420px, calc(100vw - 32px));
gap: 10px;
transform: translateX(-50%);
pointer-events: none;
}
.message {
--message-background: #1f7a62;
--message-progress: #125441;
position: relative;
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: start;
gap: 12px;
overflow: hidden;
padding: 14px 12px 16px 16px;
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 16px;
background: var(--message-background);
color: #fff;
box-shadow: 0 16px 36px rgba(31, 42, 46, 0.26);
pointer-events: auto;
animation: message-enter 180ms ease-out;
}
.message--warning {
--message-background: #c76a13;
--message-progress: #914605;
}
.message--error {
--message-background: #b9382e;
--message-progress: #7f201a;
}
.message.is-leaving {
animation: message-leave 180ms ease-in forwards;
}
.message-text {
margin: 0;
font-weight: 650;
line-height: 1.35;
}
.message-dismiss {
width: 28px;
height: 28px;
min-width: 28px;
padding: 0;
border: 1px solid rgba(255, 255, 255, 0.45);
border-radius: 50%;
background: transparent;
color: inherit;
box-shadow: none;
font-size: 1.35rem;
line-height: 1;
}
.message-dismiss:hover,
.message-dismiss:focus-visible {
background: rgba(255, 255, 255, 0.18);
outline: 2px solid #fff;
outline-offset: 2px;
}
.message-progress {
position: absolute;
right: 0;
bottom: 0;
left: 0;
height: 4px;
background: var(--message-progress);
transform-origin: right;
animation: message-countdown var(--message-duration) linear forwards;
}
@keyframes message-enter {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes message-leave {
to { opacity: 0; transform: translateY(-8px); }
}
@keyframes message-countdown {
to { transform: scaleX(0); }
}
@media (prefers-reduced-motion: reduce) {
.message,
.message.is-leaving,
.message-progress {
animation-duration: 1ms;
}
}
.app-shell {
position: relative;
min-height: 100vh;
padding: 24px;
}
.sidebar,
.map-wrap {
min-height: 0;
}
.sidebar {
position: fixed;
z-index: 10;
top: 24px;
bottom: 24px;
left: 24px;
width: min(266px, calc(100vw - 48px));
display: flex;
flex-direction: column;
overflow: hidden;
padding: 8px;
transition: transform 240ms ease;
backdrop-filter: blur(3px);
}
.sidebar-content {
display: flex;
min-height: 0;
flex: 1;
flex-direction: column;
gap: 16px;
overflow-y: auto;
overscroll-behavior: contain;
scrollbar-gutter: stable;
}
.sidebar-toggle {
position: fixed;
z-index: 11;
display: grid;
width: 42px;
height: 42px;
padding: 0;
place-items: center;
border: 1px solid var(--panel-border);
border-radius: 14px;
background: var(--panel);
color: var(--ink);
font-size: 1.65rem;
font-weight: 500;
line-height: 1;
box-shadow: var(--shadow);
}
.sidebar-toggle:hover {
transform: translateY(-1px);
}
.sidebar-open {
top: 32px;
left: 32px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 180ms ease;
}
.sidebar-close {
position: absolute;
top: 14px;
right: 14px;
}
.sidebar-close span[aria-hidden="true"] {
transform: translateY(-1px);
}
.app-shell.sidebar-is-collapsed .sidebar {
transform: translateX(calc(-100% - 24px));
}
.app-shell.sidebar-is-collapsed .sidebar-open {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.brand,
.panel {
background: var(--panel);
backdrop-filter: blur(16px);
border: 1px solid var(--panel-border);
border-radius: 24px;
box-shadow: var(--shadow);
}
.brand {
padding: 24px 64px 24px 24px;
}
.brand-logo {
display: block;
width: min(100%, 178px);
height: auto;
}
.brand-footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 8px;
/* Extend through the brand's reserved close-button clearance so the gear
and sidebar close controls share the same right-hand axis. */
margin-right: -60px;
}
.brand-subtitle {
color: var(--muted);
font-size: 1rem;
font-weight: 400;
letter-spacing: 0.04em;
}
.settings-open {
display: grid;
width: 42px;
height: 42px;
min-width: 42px;
padding: 0;
place-items: center;
border: 1px solid var(--panel-border);
border-radius: 50%;
background: rgba(255, 255, 255, 0.55);
color: var(--ink);
box-shadow: none;
}
.settings-open:hover,
.settings-open:focus-visible {
background: rgba(31, 122, 98, 0.1);
outline: 2px solid rgba(31, 122, 98, 0.25);
outline-offset: 2px;
}
.settings-open .material-symbols-outlined {
font-size: 1.3rem;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
h1,
h2,
p {
margin: 0;
}
h1 {
font-family: "Aptos Display", "Segoe UI", sans-serif;
font-size: clamp(2rem, 4vw, 2.7rem);
line-height: 0.98;
}
.panel {
padding: 20px;
}
form.panel {
display: flex;
flex-direction: column;
gap: 14px;
}
.parcel-search-form {
display: grid;
gap: 12px;
margin-top: 16px;
}
.parcel-search-form button {
width: 100%;
}
.background-selector {
margin-top: 16px;
}
label {
display: flex;
flex-direction: column;
gap: 8px;
font-size: 0.95rem;
}
label span {
color: var(--muted);
font-weight: 600;
}
input,
select {
width: 100%;
padding: 12px 14px;
border-radius: 14px;
border: 1px solid rgba(73, 84, 79, 0.18);
background: rgba(255, 255, 255, 0.72);
color: var(--ink);
font: inherit;
}
input:focus,
select:focus {
outline: 2px solid rgba(31, 122, 98, 0.18);
border-color: var(--accent);
}
button {
border: 0;
border-radius: 14px;
padding: 13px 16px;
background: linear-gradient(135deg, var(--accent) 0%, #2b9a79 100%);
color: #fffefb;
font: inherit;
font-weight: 700;
cursor: pointer;
transition: transform 180ms ease, box-shadow 180ms ease, opacity 180ms ease;
box-shadow: 0 18px 36px rgba(31, 122, 98, 0.22);
}
button:hover {
transform: translateY(-1px);
}
button:disabled {
opacity: 0.72;
cursor: wait;
}
.panel-heading {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.badge {
min-width: 2rem;
padding: 5px 10px;
border-radius: 999px;
background: rgba(31, 122, 98, 0.12);
color: var(--accent-strong);
text-align: center;
font-weight: 700;
}
.status {
margin-top: 12px;
color: var(--muted);
}
.status:empty {
display: none;
}
.status.error {
color: #9d2e21;
}
.status.success {
color: var(--accent-strong);
}
.layer-list {
margin-top: 16px;
display: grid;
gap: 10px;
}
.layer-list.empty {
color: var(--muted);
line-height: 1.5;
}
.layer-item {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 12px;
padding: 12px;
border-radius: 16px;
background: rgba(255, 255, 255, 0.66);
border: 1px solid rgba(73, 84, 79, 0.12);
}
.layer-item input {
width: auto;
margin-top: 3px;
}
.layer-title {
font-weight: 700;
}
.map-wrap {
position: fixed;
z-index: 0;
inset: 24px;
overflow: hidden;
border-radius: 32px;
box-shadow: var(--shadow);
}
#map {
position: absolute;
inset: 0;
}
.feature-modal[hidden] {
display: none;
}
.settings-modal[hidden] {
display: none;
}
.feature-modal,
.settings-modal {
position: fixed;
z-index: 20;
inset: 0;
display: grid;
padding: 24px;
place-items: center;
}
.feature-modal-backdrop,
.settings-modal-backdrop {
position: absolute;
inset: 0;
background: rgba(31, 42, 46, 0.35);
backdrop-filter: blur(1px);
}
.settings-modal-dialog {
position: relative;
width: min(100%, 380px);
padding: 28px;
border: 1px solid var(--panel-border);
border-radius: 24px;
background: var(--panel);
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.settings-modal-close {
position: absolute;
top: 12px;
right: 12px;
display: grid;
width: 36px;
height: 36px;
padding: 0;
place-items: center;
border: 1px solid var(--panel-border);
border-radius: 50%;
background: transparent;
color: var(--ink);
box-shadow: none;
font-size: 1.5rem;
line-height: 1;
}
.settings-modal-dialog h2 {
padding-right: 44px;
}
.settings-form {
display: grid;
gap: 20px;
margin-top: 24px;
}
.feature-modal-dialog {
position: relative;
width: min(100%, 360px);
padding: 28px;
border: 1px solid var(--panel-border);
border-radius: 24px;
background: var(--panel);
backdrop-filter: blur(3px);
box-shadow: var(--shadow);
cursor: grab;
touch-action: none;
}
.feature-modal-dialog.is-dragging {
cursor: grabbing;
user-select: none;
}
.feature-modal-close {
position: absolute;
top: 12px;
right: 12px;
display: grid;
width: 36px;
height: 36px;
padding: 0;
place-items: center;
border: 1px solid var(--panel-border);
border-radius: 50%;
background: transparent;
color: var(--ink);
box-shadow: none;
font-size: 1.5rem;
line-height: 1;
}
.feature-modal-kind {
margin-bottom: 6px;
color: var(--accent-strong);
font-size: 0.85rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.feature-details {
margin: 20px 0 0;
}
.feature-details div {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 16px;
padding: 14px 0;
border-top: 1px solid var(--panel-border);
}
.feature-details dt {
color: var(--muted);
}
.feature-details dd {
min-width: 0;
margin: 0;
font-size: 1.1rem;
font-weight: 700;
overflow-wrap: anywhere;
text-align: right;
}
.feature-inhabited {
margin: 14px 0 0;
color: #c62828;
font-weight: 700;
}
.feature-navigation,
.feature-association {
width: 100%;
margin-top: 16px;
}
.feature-associations {
margin-top: 20px;
border-top: 1px solid var(--panel-border);
padding-top: 14px;
}
.feature-associations h3 {
margin: 0;
font-size: 1rem;
}
.feature-associations ul {
display: grid;
gap: 10px;
margin: 12px 0 0;
padding: 0;
list-style: none;
}
.feature-association {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
gap: 4px 12px;
margin: 0;
padding: 12px 0;
border: 0;
border-radius: 0;
border-bottom: 1px solid var(--panel-border);
background: transparent;
color: var(--ink);
box-shadow: none;
text-align: left;
}
.feature-association:hover,
.feature-association:focus-visible {
background: rgba(31, 122, 98, 0.08);
}
.feature-association-label {
min-width: 0;
font-weight: 700;
overflow-wrap: anywhere;
}
.feature-association-area {
color: var(--muted);
font-size: 0.9rem;
white-space: nowrap;
}
.feature-association-inhabited {
grid-column: 1 / -1;
color: #c62828;
font-size: 0.9rem;
font-weight: 700;
}
.feature-modal-actions {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
.feature-modal-share {
display: grid;
width: 42px;
height: 42px;
padding: 0;
place-items: center;
border-radius: 50%;
}
.feature-modal-share .material-symbols-outlined {
font-size: 1.25rem;
line-height: 1;
}
.map-wrap .ol-zoom {
top: 16px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-zoom button,
.map-wrap .ol-visible-layers-extent button,
.map-wrap .ol-share button {
width: var(--map-tool-size);
height: var(--map-tool-size);
margin: 0;
background: linear-gradient(135deg, var(--accent) 0%, #2b9a79 100%);
color: #fffefb;
font-size: 1rem;
line-height: 1;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.24);
}
.map-wrap .ol-zoom button + button {
border-top: 1px solid rgba(255, 255, 255, 0.35);
}
.map-wrap .ol-zoom button:hover,
.map-wrap .ol-zoom button:focus,
.map-wrap .ol-visible-layers-extent button:hover,
.map-wrap .ol-visible-layers-extent button:focus,
.map-wrap .ol-share button:hover,
.map-wrap .ol-share button:focus {
background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
color: #fff;
outline: 2px solid rgba(31, 122, 98, 0.35);
outline-offset: -2px;
}
.map-wrap .ol-visible-layers-extent {
top: 108px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-visible-layers-extent button {
font-size: 1.3rem;
}
.map-wrap .ol-area-zoom {
top: 158px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-area-zoom button {
display: grid;
width: var(--map-tool-size);
height: var(--map-tool-size);
margin: 0;
padding: 0;
place-items: center;
background: linear-gradient(135deg, var(--accent) 0%, #2b9a79 100%);
color: #fffefb;
font-size: 1.3rem;
line-height: 1;
}
.map-wrap .ol-area-zoom button:hover,
.map-wrap .ol-area-zoom button:focus,
.map-wrap .ol-area-zoom button[aria-pressed="true"] {
background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
color: #fff;
outline: 2px solid rgba(31, 122, 98, 0.35);
outline-offset: -2px;
}
.map-wrap .ol-share {
top: 208px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
/* When available, keep Share after the device compass in the tool stack. */
.map-wrap .ol-device-compass ~ .ol-share {
top: 258px;
}
.map-wrap .ol-share button {
display: grid;
place-items: center;
}
.map-wrap .ol-share .material-symbols-outlined {
font-size: 1.25rem;
line-height: 1;
}
.area-zoom-icon {
display: block;
width: 1.2em;
height: 1.2em;
filter: brightness(0) invert(1);
}
.map-wrap .ol-viewport.area-zoom-is-active {
cursor: crosshair;
}
.map-wrap .ol-area-zoom-box {
border-color: var(--accent);
background: rgba(31, 122, 98, 0.16);
}
.map-wrap .ol-device-compass {
top: 208px;
right: 16px;
left: auto;
overflow: hidden;
background: var(--panel);
border: 1px solid var(--panel-border);
border-radius: 14px;
backdrop-filter: blur(16px);
box-shadow: var(--shadow);
}
.map-wrap .ol-device-compass button {
display: grid;
width: var(--map-tool-size);
height: var(--map-tool-size);
margin: 0;
padding: 0;
place-items: center;
background: linear-gradient(135deg, var(--accent) 0%, #2b9a79 100%);
color: #fffefb;
font-size: 1.3rem;
line-height: 1;
}
.map-wrap .ol-device-compass button:hover,
.map-wrap .ol-device-compass button:focus {
background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
color: #fff;
outline: 2px solid rgba(31, 122, 98, 0.35);
outline-offset: -2px;
}
/* Compass orientation stays visibly engaged after the pointer leaves the
control. The inset high-contrast ring and explicit label do not rely on
the green active colour alone. */
.map-wrap .ol-device-compass button[aria-pressed="true"] {
position: relative;
border: 3px solid #fffefb;
background: linear-gradient(135deg, var(--accent-strong) 0%, var(--accent) 100%);
color: #fff;
outline: 2px solid #1f2a2e;
outline-offset: -5px;
}
.map-wrap .ol-device-compass button[aria-pressed="true"]::after {
position: absolute;
top: 2px;
right: 2px;
padding: 1px 2px;
border-radius: 3px;
background: #fffefb;
color: #1f2a2e;
content: "ON";
font-size: 0.45rem;
font-weight: 800;
letter-spacing: 0.02em;
line-height: 1;
}
.device-compass-icon {
display: block;
width: 1.2em;
height: 1.2em;
}
/* OpenLayers hides this control while the view is north-up. */
.map-wrap .ol-rotate {
top: auto;
right: 16px;
bottom: 16px;
left: auto;
padding: 0;
background: transparent;
}
.map-wrap .ol-rotate button {
display: grid;
width: 42px;
height: 42px;
margin: 0;
padding: 0;
place-items: center;
border-radius: 50%;
background: rgba(255, 255, 255, 0.9);
line-height: 1;
box-shadow: 0 10px 24px rgba(52, 43, 28, 0.2);
}
.north-pointer-icon {
display: block;
width: 26px;
height: 26px;
transform-origin: center;
}
.map-wrap .ol-rotate button:hover {
background: #fff;
transform: translateY(-1px);
}
@media (max-width: 920px) {
.app-shell {
padding: 16px;
}
.map-wrap {
inset: 16px;
border-radius: 24px;
}
.map-wrap .ol-zoom {
top: 12px;
right: 12px;
}
.map-wrap .ol-visible-layers-extent {
top: 104px;
right: 12px;
}
.map-wrap .ol-area-zoom {
top: 154px;
right: 12px;
}
.map-wrap .ol-share {
top: 204px;
right: 12px;
}
.map-wrap .ol-device-compass ~ .ol-share {
top: 254px;
}
.map-wrap .ol-device-compass {
top: 204px;
right: 12px;
}
.map-wrap .ol-rotate {
right: 12px;
bottom: 12px;
}
.sidebar {
top: 16px;
bottom: 16px;
left: 16px;
width: min(266px, calc(100vw - 32px));
}
.sidebar-content {
gap: 10px;
padding-right: 2px;
}
.sidebar-close {
top: 12px;
right: 12px;
}
.brand {
padding: 16px 58px 14px 16px;
border-radius: 18px;
}
.brand-logo {
width: min(100%, 136px);
}
.brand-subtitle {
margin-top: 4px;
font-size: 0.85rem;
}
.panel {
padding: 14px;
border-radius: 18px;
}
.panel-heading {
gap: 8px;
}
.parcel-search-form,
.background-selector,
.layer-list {
margin-top: 10px;
}
.parcel-search-form {
gap: 8px;
}
.status {
margin-top: 8px;
font-size: 0.9rem;
}
input,
select {
padding: 10px 12px;
}
button {
padding: 11px 14px;
}
.layer-list {
gap: 8px;
}
.layer-item {
gap: 10px;
padding: 10px;
border-radius: 14px;
}
.sidebar-open {
top: 24px;
left: 24px;
}
.app-shell.sidebar-is-collapsed .sidebar {
transform: translateX(calc(-100% - 16px));
}
}
@media (max-width: 920px) and (max-height: 700px) {
.sidebar-content {
gap: 8px;
}
.brand {
padding-top: 12px;
padding-bottom: 10px;
}
.brand-logo {
width: min(100%, 112px);
}
.brand-subtitle {
display: none;
}
.panel {
padding: 12px;
}
.panel h2 {
font-size: 1.1rem;
}
}