/* ======================================================================
   Plantplot — stylesheet
   Palette: warm off-white background, forest-green primary, sand accents
   ====================================================================== */

* { box-sizing: border-box; }

/* Force `hidden` attribute to always win over class-level display rules.
   Without this, `.plan-empty { display: flex }` and `.plan-view { flex:1 }`
   override the UA default `[hidden] { display: none }` and both panels
   render at once — making the upload appear to do nothing. */
[hidden] { display: none !important; }

:root {
  color-scheme: light;
  --bg: #fafaf7;
  --bg-panel: #ffffff;
  --bg-soft: #fafbf8;          /* dropzone, ci-body, catalog-filters */
  --bg-muted: #f5f7f3;         /* pane-header, summary-card */
  --bg-hover: #f0f5ec;         /* catalog-item hover */
  --bg-hover-border: #cde1c6;
  --bg-image-fallback: #eee;   /* photo placeholder before src loads */
  --plan-bg: #f0f0ea;          /* plan-view scroll backdrop */
  --hud-bg: rgba(255, 255, 255, .92);
  --dropzone-over: #eaf3e7;
  --month-cell: #ececec;       /* inactive cell in 12-month timeline */
  --fg: #2c2c2c;
  --fg-muted: #666;
  --primary: #2e5a2e;
  --primary-hover: #1f4220;
  --accent: #c08a3a;
  --border: #e4e4e0;
  --danger: #b03a3a;
  --danger-bg: #fde8e8;
  --danger-border: #e8b0b0;
  --ring: #4a7a4a;
  --shadow: 0 1px 3px rgba(0,0,0,.06);
  --radius: 10px;
  --sidebar-w: 280px;
  --report-w: 300px;
}

/* Dark theme — applied by src/theme.js based on stored preference or
   the OS prefers-color-scheme. Brand-green accents stay; surface colors
   flip to a warm dark gray so the aerial photos read well against the
   chrome. The header gradient (a hard-coded brand green) is intentionally
   kept identical in both themes. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #1a1d1a;
  --bg-panel: #21241f;
  --bg-soft: #1d2018;
  --bg-muted: #25281f;
  --bg-hover: #2a3326;
  --bg-hover-border: #3a5235;
  --bg-image-fallback: #333;
  --plan-bg: #14160f;
  --hud-bg: rgba(20, 22, 15, .88);
  --dropzone-over: #2a4225;
  --month-cell: #303330;
  --fg: #e7e9e3;
  --fg-muted: #9aa090;
  --primary: #6ab86b;
  --primary-hover: #8acc8a;
  --accent: #d6a662;
  --border: #353830;
  --danger: #e26a6a;
  --danger-bg: #3a1818;
  --danger-border: #6a2828;
  --ring: #6ab86b;
  --shadow: 0 1px 3px rgba(0, 0, 0, .4);
}

html, body { height: 100%; margin: 0; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--fg);
  display: flex; flex-direction: column;
  line-height: 1.5;
}

/* ---------- Header ---------- */
.app-header {
  background: linear-gradient(135deg, #4a7a4a 0%, #2e5a2e 100%);
  color: white;
  padding: 14px 22px;
  box-shadow: 0 2px 6px rgba(0,0,0,.08);
  display: flex; flex-direction: column; gap: 10px;
}
.app-header .brand {
  display: flex; align-items: baseline; gap: 10px;
}
.app-header h1 {
  margin: 0; font-size: 1.4em; font-weight: 700; letter-spacing: .3px;
}
.app-header .logo { font-size: 1.4em; }
.app-header .tagline {
  opacity: .85; font-size: .9em; margin-left: 4px;
}

.toolbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
}
/* Toolbar groups cluster related actions visually. The slightly tighter
   gap inside a group (4 px) vs. the gap-around-divider (6 px from the
   parent + the divider's own 4 px margin) makes related buttons feel
   "stuck together" so the eye groups them without reading the labels. */
.toolbar-group {
  display: inline-flex; flex-wrap: wrap; align-items: center; gap: 4px;
}
/* Spacer pushes the App group (Hilfe / Theme / Reset) to the right edge
   of the toolbar on wide layouts. On narrow layouts (.app-header wraps),
   it just collapses to zero and the App group sits inline. */
.toolbar-spacer { flex: 1 1 0; min-width: 0; }
.toolbar button {
  background: rgba(255,255,255,.12);
  color: white; border: 1px solid rgba(255,255,255,.25);
  padding: 6px 12px; border-radius: 6px;
  font-size: .9em; cursor: pointer;
  transition: background .15s;
}
.toolbar button:hover:not(:disabled) { background: rgba(255,255,255,.22); }
.toolbar button:disabled { opacity: .45; cursor: not-allowed; }
.toolbar button.danger { background: rgba(255,60,60,.18); }
.toolbar button.danger:hover:not(:disabled) { background: rgba(255,60,60,.32); }
.toolbar .divider {
  width: 1px; height: 22px; background: rgba(255,255,255,.3); margin: 0 4px;
}
.toolbar-field {
  display: flex; align-items: center; gap: 6px;
  font-size: .85em; opacity: .9;
}
.toolbar-field select {
  background: rgba(255,255,255,.12);
  color: white;
  border: 1px solid rgba(255,255,255,.25);
  border-radius: 6px;
  padding: 4px 6px;
  font-size: .9em;
  cursor: pointer;
}
.toolbar-field select:disabled { opacity: .45; cursor: not-allowed; }
.toolbar-field select option { color: var(--fg); background: white; }
/* ---------- Toast (snackbar) ---------- */
/* Floating notifications anchored to the bottom-center of the viewport.
   Successes auto-dismiss in 3.5 s, errors in 6 s; click any toast to
   dismiss early. Replaces the old inline #status-msg span — much harder
   to miss while still staying out of the way. */
.toast-host {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  z-index: 1000;
  pointer-events: none;             /* host is transparent; toasts opt back in */
  max-width: min(560px, calc(100vw - 32px));
}
.toast {
  pointer-events: auto;
  min-width: 240px;
  max-width: 100%;
  padding: 10px 16px;
  border-radius: 8px;
  font-size: .95em;
  line-height: 1.35;
  color: white;
  background: #2e5a2e;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .25), 0 1px 3px rgba(0, 0, 0, .15);
  cursor: pointer;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .18s ease-out, transform .18s ease-out;
}
.toast.visible { opacity: 1; transform: translateY(0); }
.toast.dismissing { opacity: 0; transform: translateY(8px); }
.toast-success { background: #2e5a2e; }
.toast-info    { background: #2c4a6a; }
.toast-warning { background: #8a5a2a; }
.toast-error   { background: #8a2a2a; }

/* A toast that asks a question: message left, one button right. */
.toast:has(.toast-action) { display: flex; align-items: center; gap: 14px; }
.toast-msg { flex: 1; }
.toast-action {
  flex: none;
  padding: 5px 12px;
  border: 1px solid rgba(255, 255, 255, .55);
  border-radius: 6px;
  background: rgba(255, 255, 255, .14);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.toast-action:hover { background: rgba(255, 255, 255, .26); }

/* ---------- Main layout ---------- */
.app-main {
  flex: 1; min-height: 0;
  display: grid;
  /* sidebar | splitter | plan | splitter | report. The splitter columns
     replace the previous 1px gap that simulated borders — they're 6 px
     drag handles that visually act as the divider too. */
  grid-template-columns:
    var(--sidebar-w) 6px 1fr 6px var(--report-w);
  grid-template-rows: 1fr;          /* single row must fill available height,
                                       otherwise .plan-view collapses to 0px
                                       and the SVG (height:100%) is invisible */
  background: var(--border);
}
.splitter {
  background: var(--border);
  cursor: col-resize;
  user-select: none;
  touch-action: none;
  transition: background .12s;
}
.splitter:hover, .splitter.dragging { background: var(--primary); }
.sidebar, .plan-pane {
  background: var(--bg-panel);
  overflow: hidden;
  display: flex; flex-direction: column; min-height: 0;
}
.pane-header {
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-muted);
}
.pane-header h2 {
  margin: 0 0 8px; color: var(--primary); font-size: 1.1em;
}
.pane-title-row {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
.pane-title-row h2 { margin: 0 0 8px; }
.pane-header-btn {
  font-size: 0.82em; padding: 3px 8px; white-space: nowrap;
  cursor: pointer; margin-bottom: 8px;
}

/* ---------- Catalog ---------- */
.catalog-pane input[type=search] {
  width: 100%; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 6px; font-size: .9em;
  background: var(--bg-panel); color: var(--fg);
}
.catalog-filters {
  padding: 8px 16px; display: flex; gap: 12px; flex-wrap: wrap;
  font-size: .85em; color: var(--fg-muted);
  border-bottom: 1px solid var(--border); background: var(--bg-soft);
}
.catalog-filters label { cursor: pointer; display: flex; gap: 4px; align-items: center; }
/* Filter — opens the full rail (facet-rail.js) below the quick toggles. */
.catalog-filter-btn {
  margin-left: auto; padding: 2px 8px; font-size: .95em; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--bg-panel); color: var(--fg);
}
.catalog-filter-btn:hover { background: var(--bg-muted); }
.catalog-filter-btn.active { border-color: var(--primary); color: var(--primary); }
.catalog-facets {
  padding: 10px 16px; border-bottom: 1px solid var(--border);
  background: var(--bg-soft); max-height: 45vh; overflow-y: auto;
}
.catalog-chips { padding: 8px 16px 0; }
.catalog-list {
  list-style: none; padding: 4px; margin: 0;
  flex: 1; overflow-y: auto;
}
.catalog-item {
  margin: 3px 0;
  border-radius: 6px; cursor: grab;
  border: 1px solid transparent;
  transition: background .12s, border-color .12s, box-shadow .12s;
  user-select: none;
  background: var(--bg-panel);
}
.catalog-item:hover {
  background: var(--bg-hover); border-color: var(--bg-hover-border);
}
.catalog-item.dragging { opacity: .5; }

.ci-details { }
.ci-summary {
  display: grid;
  grid-template-columns: 44px 1fr;
  gap: 8px;
  align-items: center;
  padding: 6px 10px;
  cursor: grab;          /* drag from summary */
  list-style: none;      /* hide default triangle */
}
.ci-summary::-webkit-details-marker { display: none; }
.ci-summary::before {
  content: "▸";
  font-size: .8em;
  color: var(--fg-muted);
  position: absolute; right: 8px; top: 10px;
  transition: transform .15s;
}
.ci-details[open] .ci-summary::before { transform: rotate(90deg); }
.ci-details { position: relative; }
/* Section headers splitting the list into ★ Favoriten / Alle Arten (only
   rendered while at least one favorite matches the current filter). */
.catalog-section {
  padding: 8px 10px 3px;
  font-size: .72em; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--fg-muted);
  border-bottom: 1px solid var(--border);
  user-select: none;
}
/* ★ favorite toggle — overlays the top-left corner of the 44px thumbnail.
   A circular backdrop keeps it readable over any photo. Hidden until the
   row is hovered (or the button focused) to keep the list calm; a set
   favorite stays visible so starred rows are recognizable at a glance. */
.ci-fav {
  position: absolute; left: 4px; top: 3px; z-index: 2;
  width: 20px; height: 20px; padding: 0;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(0, 0, 0, .35); color: #fff;
  font-size: 12px; line-height: 20px; text-align: center;
  opacity: 0; transition: opacity .12s;
}
.catalog-item:hover .ci-fav,
.ci-fav:focus-visible,
.ci-fav.is-fav { opacity: 1; }
.ci-fav:hover { background: rgba(0, 0, 0, .55); }
.ci-fav.is-fav { color: #ffc83d; }
.ci-thumb {
  width: 44px; height: 44px; object-fit: cover;
  border-radius: 6px; background: var(--bg-image-fallback);
  display: block;
}
/* Placeholder shown in the thumb slot when a plant has no photo, so the
   summary grid keeps its 44px column and the name stays in the wide one. */
/* A render is a drawing, not a photograph of the plant somebody is about to
   buy: a faint tint keeps the two distinguishable at a glance without making
   the list noisy. The title attribute says so in words. */
.ci-thumb-render, .bc-photo-render { background: var(--bg-muted); }
.ds-render-note, .pp-note {
  margin: 6px 0 0; font-size: .82em; color: var(--fg-muted); text-align: center;
}
.ci-thumb-render { outline: 1px dashed var(--border); outline-offset: -1px; }

.ci-thumb-empty {
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; color: var(--fg-muted);
}
.catalog-item .ci-head { min-width: 0; padding-right: 14px; }
.catalog-item .ci-name { font-weight: 600; color: var(--primary); display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.catalog-item .ci-lat  { font-style: italic; color: var(--fg-muted); font-size: .82em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
.catalog-item .ci-meta { font-size: .78em; color: var(--fg-muted); margin-top: 2px; }

.ci-body {
  padding: 8px 12px 10px 12px;
  border-top: 1px solid var(--border);
  background: var(--bg-soft);
}
/* Steckbrief: per-species fact grid — icon + label + value (+ optional
   sub-line, e.g. the Wurzelsystem description). One column in the narrow
   sidebar, two when the pane is widened via the splitter. */
.steckbrief {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 8px 12px;
  margin: 2px 0 6px;
}
.sb-row { display: flex; gap: 8px; align-items: flex-start; }
.sb-icon { flex: none; width: 1.4em; text-align: center; line-height: 1.35; }
.sb-txt { display: flex; flex-direction: column; min-width: 0; }
.sb-label { font-size: .78em; font-weight: 600; color: var(--fg); }
.sb-value { font-size: .8em; color: var(--fg-muted); line-height: 1.35; }
.sb-sub { font-size: .74em; color: var(--fg-muted); line-height: 1.35; opacity: .85; }
.ci-note { font-size: .82em; color: var(--fg); margin: 8px 0 4px; line-height: 1.4; }
.ci-gallery {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
  margin-top: 6px;
}
.ci-gallery img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 4px; background: var(--bg-image-fallback);
  cursor: zoom-in;
}
/* 12-month timeline showing leaf / bloom / leaf-fall presence per species.
   Grid: 1 label column + 12 equal month columns. The header row prints the
   month initials; each row below toggles a coloured cell when that month is
   active. Native <abbr title> tooltips give the full month name. */
.ci-monthbars {
  display: grid;
  grid-template-columns: 52px repeat(12, 1fr);
  gap: 2px 2px;
  align-items: center;
  margin-top: 8px;
  font-size: .72em;
}
.ci-monthbars .mb-lbl {
  color: var(--fg-muted);
  text-align: right;
  padding-right: 4px;
  font-size: .85em;
}
.ci-monthbars .mb-head {
  text-align: center;
  color: var(--fg-muted);
  font-weight: 600;
  font-size: .9em;
}
.ci-monthbars .mb-cell {
  height: 9px;
  background: var(--month-cell);
  border-radius: 2px;
  display: block;
}
.ci-monthbars .mb-cell.leaf.on  { background: #4a7a4a; }   /* same green as primary */
.ci-monthbars .mb-cell.bloom.on { background: #c08a3a; }   /* sand accent */
.ci-monthbars .mb-cell.fall.on  { background: #8a5a2a; }   /* warm earth (matches boundary) */
.catalog-item .badge {
  display: inline-block; padding: 1px 6px; border-radius: 9px;
  font-size: .7em; font-weight: 600; margin-right: 3px;
}
.badge.tree { background: #e8d8c0; color: #6b4a2b; }
.badge.shrub { background: #e0e8d0; color: #4a6a2e; }
.badge.evergreen { background: #d0ebd0; color: #2e5a2e; }
.badge.custom { background: #ddd6ee; color: #4a3a8a; }

.catalog-pane .hint,
.plan-empty .hint {
  padding: 6px 16px; font-size: .8em; color: var(--fg-muted); margin: 0;
}

/* ---------- Eigene Pflanzen (user-added species) ---------- */
.add-user-plant {
  margin: 6px 12px 0; padding: 6px 10px;
  border: 1px dashed var(--border); border-radius: 8px;
  background: var(--bg-soft); color: var(--fg-muted);
  font: inherit; font-size: .85em; cursor: pointer;
}
.add-user-plant:hover { background: var(--bg-muted); color: var(--fg); }
.ci-edit-user {
  margin-top: 8px; padding: 4px 10px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-soft); color: var(--fg);
  font: inherit; font-size: .82em; cursor: pointer;
}
.ci-edit-user:hover { background: var(--bg-muted); }
.upd-row { display: flex; gap: 12px; }
.upd-row > label { flex: 1; }
.upd-check { display: flex; align-items: center; gap: 6px; align-self: end; }
.upd-check input { margin: 0; }
.upd-months-fieldset {
  border: 1px solid var(--border); border-radius: 8px;
  margin: 10px 0; padding: 6px 10px 10px;
}
.upd-months-fieldset legend { font-size: .85em; color: var(--fg-muted); padding: 0 4px; }
.upd-month-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px 10px;
}
.upd-month {
  display: flex; align-items: center; gap: 4px;
  font-size: .78em; color: var(--fg-muted);
  margin: 0; white-space: nowrap;
}
.upd-month input { margin: 0; }
.upd-error {
  margin: 8px 0 0; padding: 6px 10px; border-radius: 6px;
  background: var(--danger-bg); color: var(--danger);
  font-size: .85em;
}
.pd-height-hint { display: block; margin-top: 3px; font-size: .85em; }

/* ---------- Plan pane ---------- */
.plan-pane { position: relative; }
.plan-empty {
  display: flex; align-items: center; justify-content: center;
  height: 100%;
  padding: 40px;
}
.dropzone {
  border: 3px dashed var(--border);
  border-radius: 14px;
  padding: 50px 40px; text-align: center;
  background: var(--bg-soft);
  max-width: 520px;
  transition: border-color .2s, background .2s;
}
.dropzone.drag-over { border-color: var(--primary); background: var(--dropzone-over); }
.dropzone-icon { font-size: 3em; margin-bottom: 10px; }
.link-btn {
  background: none; border: none; color: var(--primary); cursor: pointer;
  text-decoration: underline; font: inherit;
}
.plan-view {
  flex: 1; position: relative; overflow: auto; background: var(--plan-bg);
}
.plan-svg {
  display: block; width: 100%; height: 100%;
  cursor: grab;
}
.plan-svg.panning { cursor: grabbing; }
/* Rubber-band marquee (Shift + drag on empty background). Stroke stays a
   constant on-screen width regardless of zoom via non-scaling-stroke. */
.rubber-band {
  fill: rgba(74, 74, 194, 0.12);
  stroke: #4a4ac2;
  stroke-width: 1.5;
  stroke-dasharray: 5 3;
  pointer-events: none;
  vector-effect: non-scaling-stroke;
}

/* On-canvas zoom controls — bottom-right, vertical stack, like map UIs.
   Sits above the SVG; uses pointer-events: auto on each button so the
   surrounding empty space still lets pan-drags through. */
.zoom-controls {
  position: absolute; bottom: 12px; right: 12px;
  display: flex; flex-direction: column;
  background: var(--hud-bg); color: var(--fg);
  border-radius: 6px; box-shadow: var(--shadow);
  overflow: hidden; z-index: 5;
  pointer-events: none;
}
.zoom-controls button {
  pointer-events: auto;
  width: 32px; height: 32px; padding: 0;
  background: none; border: none;
  color: inherit;
  font-size: 1.1em; line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.zoom-controls button + button {
  border-top: 1px solid var(--border, rgba(0,0,0,.12));
}
.zoom-controls button:hover { background: rgba(0,0,0,.06); }
:root[data-theme="dark"] .zoom-controls button:hover { background: rgba(255,255,255,.08); }
.plan-hud {
  position: absolute; top: 10px; left: 10px;
  background: var(--hud-bg); color: var(--fg);
  padding: 4px 10px; border-radius: 6px;
  font-size: .85em; box-shadow: var(--shadow);
  pointer-events: none;
  transition: background .3s, box-shadow .3s, color .3s;
}
.plan-hud.flash {
  animation: hud-flash 1.2s ease-out;
}
@keyframes hud-flash {
  0%   { background: #2e5a2e; color: white; box-shadow: 0 0 0 6px rgba(46,90,46,.25); }
  60%  { background: #2e5a2e; color: white; box-shadow: 0 0 0 14px rgba(46,90,46,0); }
  100% { background: rgba(255,255,255,.92); color: inherit; box-shadow: var(--shadow); }
}
.scale-banner {
  position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
  background: var(--primary); color: white;
  padding: 8px 14px; border-radius: 8px;
  font-size: .9em; font-weight: 600;
  box-shadow: 0 2px 10px rgba(0,0,0,.2);
  display: flex; align-items: center; gap: 10px;
  z-index: 5;
}
.scale-banner-cancel {
  background: rgba(255,255,255,.15); color: white;
  border: 1px solid rgba(255,255,255,.35); border-radius: 4px;
  width: 22px; height: 22px; line-height: 1; padding: 0;
  font-size: 1.1em; cursor: pointer;
}
.scale-banner-cancel:hover { background: rgba(255,255,255,.28); }
.plan-svg.picking { cursor: crosshair; }
.scale-pick-dot {
  fill: #e05050; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
.scale-pick-line {
  stroke: #e05050; stroke-width: 2; stroke-dasharray: 4 3;
  fill: none; pointer-events: none;
}
/* Maßband overlay — blue to visually distinguish from the red scale pick. */
.measure-dot {
  fill: #1f6b8a; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
.measure-line {
  stroke: #1f6b8a; stroke-width: 2.5; fill: none;
  pointer-events: none;
}
.measure-line.ghost {
  stroke-dasharray: 5 4; opacity: .85;
}
/* The active (row-clicked) measurement pops; the in-progress draft is
   dashed until it's saved. */
.measure-line.active { stroke: #d98a1f; stroke-width: 3.5; }
.measure-line.draft { stroke-dasharray: 7 4; }
.measure-dot.draft { fill: #d98a1f; }
/* Hollow dot ON the cursor: the point the next left-click would place. */
.measure-dot.cursor {
  fill: rgba(255, 255, 255, .55); stroke: #d98a1f; stroke-width: 2;
  stroke-dasharray: 3 2; pointer-events: none;
}
/* ---- Pläne-Panel: switch between saved plans ---- */
.plan-panel { top: 56px; left: 12px; width: 300px; }
.plan-row { grid-template-columns: auto 1fr auto auto; align-items: center; }
.plan-row.active { outline: 2px solid #2f6f3e; outline-offset: -2px; border-radius: 4px; }
.plan-row:not(.active) { cursor: pointer; }
.plan-mark { color: #2f6f3e; font-size: 0.9em; width: 1em; text-align: center; }
.plan-when {
  color: var(--muted, #6b6b6b);
  font-size: 0.85em;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.plan-hint {
  margin: 0; padding: 6px 8px;
  border-top: 1px solid var(--border, rgba(0,0,0,.12));
  color: var(--muted, #6b6b6b); font-size: 0.85em;
}

/* ---- Anchor-Snapping: what the next draw click would latch onto ---- */
/* Magenta on purpose — no other overlay uses it, so the indicator can never
   be mistaken for the thing it points at. Sizes come from the element as
   attributes (screen-px derived), so nothing here may set stroke-width:
   author CSS would outrank the presentation attribute. */
.snap-overlay { pointer-events: none; }
.snap-overlay .snap-marker {
  fill: rgba(216, 27, 140, .2);
  stroke: #d81b8c;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .45));
}
.snap-overlay .snap-label {
  fill: #b0166f;
  stroke: white;
  paint-order: stroke;
  stroke-linejoin: round;
  font-weight: 600;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  user-select: none;
}

/* Editable vertices of a SAVED measurement (grabbable) + its insert handles. */
.measure-vertex {
  fill: #1f6b8a; stroke: #fff; stroke-width: 2; cursor: grab;
}
.measure-vertex:hover { fill: #14506a; }
.measure-vertex.delete-target { fill: #c43030; }
.measure-mid {
  fill: rgba(255, 255, 255, .8); stroke: #1f6b8a; stroke-width: 1.5;
  cursor: copy;
}
.measure-mid:hover { fill: #cfe6ef; }
/* Wide invisible grab band for body-dragging a whole measurement. */
.measure-hit { stroke: transparent; fill: none; cursor: move; }
.measure-label {
  fill: #1f6b8a; stroke: white; paint-order: stroke; stroke-linejoin: round;
  font-weight: 700;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  pointer-events: none; user-select: none;
}
.measure-label.ghost { opacity: .8; }
/* Multi-segment total ("Σ 15,4 m") at the last vertex. */
.measure-total {
  fill: #14506a; stroke: white; paint-order: stroke; stroke-linejoin: round;
  font-weight: 700;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  pointer-events: none; user-select: none;
}

/* Maßband banner — same position as scale banner but blue so the colour
   matches the overlay. .scale-banner provides the base layout. */
.measure-banner { background: #1f6b8a; }
/* Geo-Maßstab banner — purple so the user sees that this is a different
   calibration mode than the green ruler / blue Maßband. */
.georef-banner { background: #5a4ac2; }
/* Active state for the toolbar toggles (Maßband, Grenze, Raster) */
#btn-measure.active,
#btn-boundary.active,
#btn-boundary-edit.active,
#btn-grid-toggle.active {
  background: rgba(255,255,255,.32);
  border-color: rgba(255,255,255,.6);
}

/* Metric grid overlay — thin, subtle lines so they don't dominate the
   aerial photo. vector-effect=non-scaling-stroke (set on the path) keeps
   the line at 1 device-pixel regardless of viewBox zoom. */
.grid-overlay .grid-line {
  fill: none;
  stroke: rgba(46, 90, 46, .35);
  stroke-width: 1;
}

/* ---------- Multi-select bulk-action bar ---------- */
/* Same anchor + base layout as the scale/measure/boundary banners (top
   center), but a sand-accent background so the user sees at a glance
   that this is a different mode from the picking flows. */
.selection-bar { background: #c08a3a; }
.selection-bar .selection-bar-delete {
  width: auto; padding: 2px 10px; font-size: .85em; font-weight: 600;
}

/* ---------- Wizard onboarding hint ---------- */
/* Anchored to the BOTTOM of the plan-view so it never overlaps the
   scale / measure / boundary picking banners at the top. Friendly
   amber so it reads as a hint, not a mode. */
.wizard-banner {
  top: auto;
  bottom: 14px;
  background: #c08a3a;
  max-width: min(680px, 92%);
  flex-wrap: wrap;
}
.wizard-banner-actions {
  display: inline-flex; gap: 6px; flex-wrap: wrap;
}
.wizard-banner-action {
  background: rgba(255,255,255,.18);
  color: white;
  border: 1px solid rgba(255,255,255,.45);
  border-radius: 4px;
  padding: 3px 10px;
  font-size: .85em; font-weight: 600;
  cursor: pointer;
}
.wizard-banner-action:hover { background: rgba(255,255,255,.32); }

/* Pulse-highlight whichever toolbar button(s) the wizard is currently
   pointing at. The amber ring matches the banner's background so the
   user's eye connects "the orange banner says do this → the orange
   ring is the button to click". */
.toolbar .wizard-target {
  position: relative;
  animation: wizard-pulse 1.6s ease-in-out infinite;
  outline: 2px solid #c08a3a;
  outline-offset: 2px;
  border-radius: 4px;
}
@keyframes wizard-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(192,138,58, .55); }
  50%      { box-shadow: 0 0 0 8px rgba(192,138,58, 0);  }
}
@media (prefers-reduced-motion: reduce) {
  .toolbar .wizard-target { animation: none; }
}

/* ---------- Layer panel ---------- */
/* Floating panel anchored to the bottom-LEFT of the plan view (the
   zoom-controls live bottom-right, so the two corners stay independent).
   Lists every layer top-to-bottom in z-order: the topmost layer in the
   render stack is the topmost row in the panel, mirroring how Photoshop
   etc. display their layer trees. */
.layer-panel {
  position: absolute; bottom: 12px; left: 12px;
  background: var(--hud-bg); color: var(--fg);
  border-radius: 6px; box-shadow: var(--shadow);
  font-size: .85em;
  z-index: 41;   /* above the docked 3D view — see .area-panel below */
  width: min(320px, 90%);
  max-height: 70%;
  display: flex; flex-direction: column;
}

/* ---------- Area panel (Flächen & Gelände) ---------- */
/* Anchored top-right of the plan view so it doesn't fight the bottom-left
   layer panel or the bottom-right zoom controls. The HUD scale label sits
   top-left, so all four corners stay free of overlap. */
/* z-index sits ABOVE the docked 3D view (.s3d-dock, 40) so every floating
   panel stays reachable while the 3D scene covers the plan — you can change
   a Planie's Niveau / Modus and watch the terrain react. (A FLOATING 3D view
   is a modal <dialog> in the top layer, which nothing in the normal document
   can paint over; dock it to use the panels alongside it.) */
.area-panel {
  position: absolute; top: 56px; right: 12px;
  background: var(--hud-bg); color: var(--fg);
  border-radius: 6px; box-shadow: var(--shadow);
  font-size: .85em;
  z-index: 41;
  width: min(280px, 90%);
  max-height: 70%;
  display: flex; flex-direction: column;
}
/* Every panel wired through enablePanelDrag gets the grab affordance — the
   helper tags its handle, so no panel can silently miss the rule. */
.panel-drag-handle { cursor: move; user-select: none; }
.area-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border, rgba(0,0,0,.12));
}
.area-panel-head strong { font-size: 1em; }
.area-panel-close {
  background: none; border: none; color: inherit;
  font-size: 1.2em; line-height: 1; cursor: pointer;
  padding: 0 4px;
}
.area-list {
  list-style: none; margin: 0; padding: 4px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 4px;
}
.area-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 6px;
  padding: 4px 6px;
  border: 1px solid var(--border, rgba(0,0,0,.12));
  border-radius: 4px;
  background: var(--bg-soft, transparent);
}
.area-row .area-pick { margin: 0; cursor: pointer; }
/* A levelling area still appears in the Flächen list — a ⛰ marker says so and
   points at the Gelände panel, where it is actually edited. The extra cell
   means the Flächen rows carry FIVE columns, not the base four. */
.area-level-mark { font-size: .95em; opacity: .55; }
.area-level-mark.active {
  opacity: 1; background: #efe0cd; border-color: #8a5e2a; color: #6d4a20;
}
:root[data-theme="dark"] .area-level-mark.active {
  background: #4a3a24; border-color: #b98a4a; color: #f0dcc0;
}
/* An inactive measurement still shows its vertices — grabbing one activates
   the strecke AND drags in a single gesture. */
.measure-vertex.idle { fill: #4d8ba6; stroke-width: 1.5; opacity: .75; }
.measure-vertex.idle:hover { fill: #1f6b8a; opacity: 1; }


.area-iconbtn {
  background: none; border: 1px solid var(--border, rgba(0,0,0,.15));
  color: inherit; cursor: pointer;
  width: 30px; height: 30px; padding: 0;
  font-size: 1.05em; line-height: 1;
  border-radius: 4px;
}
.area-iconbtn:hover:not(:disabled) {
  background: var(--bg-panel, rgba(0,0,0,.05));
}
.area-iconbtn.danger:hover:not(:disabled) {
  background: rgba(220, 50, 50, .15);
  border-color: #c43030;
  color: #c43030;
}
.area-panel-foot {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px;
  border-top: 1px solid var(--border, rgba(0,0,0,.12));
  font-weight: 600;
}
.area-sum-value {
  font-variant-numeric: tabular-nums;
  color: #1f6b8a;
}
.layer-panel-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border, rgba(0,0,0,.12));
}
.layer-panel-head strong { font-size: 1em; }
.layer-panel-close {
  background: none; border: none; color: inherit;
  font-size: 1.2em; line-height: 1; cursor: pointer;
  padding: 0 4px;
}
.layer-list {
  list-style: none; margin: 0; padding: 4px;
  overflow-y: auto;
  display: flex; flex-direction: column; gap: 4px;
}
.layer-row {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 4px 6px;
  padding: 4px 6px;
  border: 1px solid var(--border, rgba(0,0,0,.12));
  border-radius: 4px;
  background: var(--bg-soft, transparent);
}
.layer-row.base {
  background: var(--bg-muted, rgba(0,0,0,.04));
}
.layer-row .layer-visible {
  grid-row: 1 / 3;
  margin: 0;
  cursor: pointer;
}
.layer-row .layer-name {
  grid-column: 2;
  font-size: .95em;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 3px;
  padding: 2px 4px;
  color: inherit;
  min-width: 0;
}
.layer-row .layer-name:hover,
.layer-row .layer-name:focus {
  border-color: var(--border, rgba(0,0,0,.2));
  background: var(--bg-panel, rgba(255,255,255,.5));
}
.layer-row .layer-opacity {
  grid-column: 2;
  width: 100%;
  margin: 0;
}
.layer-row .layer-actions {
  grid-column: 3; grid-row: 1 / 3;
  display: inline-flex; gap: 4px;
  align-items: center;
}
.layer-iconbtn {
  background: none; border: 1px solid var(--border, rgba(0,0,0,.15));
  color: inherit; cursor: pointer;
  width: 30px; height: 30px; padding: 0;
  font-size: 1.05em; line-height: 1;
  border-radius: 4px;
}
.layer-iconbtn:hover:not(:disabled) {
  background: var(--bg-panel, rgba(0,0,0,.05));
}
.layer-iconbtn:disabled {
  opacity: .35; cursor: default;
}
.layer-iconbtn.danger {
  width: 36px; height: 36px;
  font-size: 1.25em;
}
.layer-iconbtn.danger:hover:not(:disabled) {
  background: rgba(220, 50, 50, .15);
  border-color: #c43030;
  color: #c43030;
}
.layer-base-note {
  font-size: .75em; color: var(--muted, #777);
  letter-spacing: .03em; text-transform: uppercase;
}
.layer-empty {
  list-style: none; padding: 6px 8px;
  color: var(--muted, #777); font-style: italic; font-size: .9em;
}
.layer-panel-foot {
  padding: 6px 8px;
  border-top: 1px solid var(--border, rgba(0,0,0,.12));
}
.layer-add {
  width: 100%; padding: 4px 8px;
  background: var(--bg-panel); color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.18));
  border-radius: 4px; cursor: pointer;
  font-size: .9em;
}
.layer-add:hover:not(:disabled) {
  background: var(--bg-muted, rgba(0,0,0,.06));
}
.layer-add:disabled { opacity: .5; cursor: default; }
#btn-layers.active {
  background: rgba(255,255,255,.32);
  border-color: rgba(255,255,255,.6);
}

/* ---------- Property boundary ---------- */
.boundary-banner { background: #8a5a2a; }   /* warm earth so it differs from scale (green) and Maßband (blue) */
.boundary-poly {
  fill: rgba(192, 138, 58, 0.10);            /* soft sand fill, very translucent so the image stays visible */
  stroke: #8a5a2a;
  stroke-width: 2;
  stroke-dasharray: 6 4;
  pointer-events: none;
}
.boundary-draft-line {
  stroke: #8a5a2a; stroke-width: 2.5; fill: none;
  pointer-events: none;
}
.boundary-draft-line.ghost { stroke-dasharray: 5 4; opacity: .85; }
.boundary-draft-line.close { stroke-dasharray: 2 4; opacity: .6; }
.boundary-draft-line.close.active {           /* solid + bright while the cursor sits in the close zone */
  stroke: #2e5a2e;
  stroke-dasharray: none;
  stroke-width: 3;
  opacity: 1;
}
.boundary-dot {
  fill: #8a5a2a; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
.boundary-dot.first {                        /* highlight the start vertex so users see the closing target */
  fill: #c08a3a; stroke-width: 3;
}
.boundary-dot.close-target {                 /* hover state: visibly bigger, primary green, glowing */
  fill: #2e5a2e;
  stroke-width: 4;
  filter: drop-shadow(0 0 6px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}

/* boundary EDIT mode: drag-handles on each vertex, insert-handles on each edge midpoint */
.boundary-poly.editing { stroke-dasharray: 4 3; }
.boundary-vertex {
  fill: #c08a3a; stroke: white; stroke-width: 2;
  cursor: grab;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  transition: r .08s, filter .12s;
}
.boundary-vertex:hover {
  fill: #2e5a2e;
  filter: drop-shadow(0 0 5px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.boundary-vertex.dragging {
  cursor: grabbing;
  fill: #2e5a2e;
  stroke-width: 3;
}
.boundary-midpoint {
  fill: white; stroke: #8a5a2a; stroke-width: 2;
  opacity: .65;
  cursor: copy;
  transition: opacity .1s, fill .1s;
}
.boundary-midpoint:hover {
  opacity: 1;
  fill: #c08a3a;
  stroke: white;
}

/* ---------- Measurement areas (Flächen) ---------- */
/* A second polygon family alongside the property boundary. Yellow/teal
   accent so the user can tell at a glance that "the brown polygon is the
   property line, the teal polygons are user-named areas I'm measuring."
   Picked teal (#1f6b8a) — same hue family as Maßband, but the boundary
   is brown so there's no clash. */
.area-banner { background: #1f6b8a; }
.area-poly {
  fill: rgba(31, 107, 138, 0.16);
  stroke: #1f6b8a;
  stroke-width: 2;
  stroke-dasharray: 5 3;
  cursor: pointer;
  transition: fill .12s, stroke-width .12s;
}
.area-poly:hover { fill: rgba(31, 107, 138, 0.24); }
.area-poly.selected {
  fill: rgba(31, 107, 138, 0.32);
  stroke-width: 3;
  stroke-dasharray: none;
}
/* A LEVELING area reshapes the terrain under it — earth-brown, so the plan
   distinguishes it at a glance from a plain measurement polygon. */
.area-poly.leveling {
  fill: rgba(150, 106, 58, 0.20);
  stroke: #8a5e2a;
}
.area-poly.leveling:hover { fill: rgba(150, 106, 58, 0.30); }
.area-poly.leveling.selected { fill: rgba(150, 106, 58, 0.36); }
.area-label {
  fill: #1a1a1a;
  stroke: white;
  paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 600;
  user-select: none;
}
.area-label.selected { fill: #1f6b8a; }
.area-draft-line {
  stroke: #1f6b8a; stroke-width: 2.5; fill: none;
  pointer-events: none;
}
.area-draft-line.ghost { stroke-dasharray: 5 4; opacity: .85; }
.area-draft-line.close { stroke-dasharray: 2 4; opacity: .6; }
.area-draft-line.close.active {
  stroke: #2e5a2e;
  stroke-dasharray: none;
  stroke-width: 3;
  opacity: 1;
}
.area-dot {
  fill: #1f6b8a; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
.area-dot.first { fill: #4ca2c2; stroke-width: 3; }
/* Hovered placed draft point — the next click REMOVES it. Shared by every
   draw tool's dots. */
.boundary-dot.delete-target, .area-dot.delete-target,
.structure-dot.delete-target, .hedge-dot.delete-target {
  fill: #b03030;
  stroke-width: 3;
  filter: drop-shadow(0 0 6px rgba(176, 48, 48, .6))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}
.area-dot.close-target {
  fill: #2e5a2e;
  stroke-width: 4;
  filter: drop-shadow(0 0 6px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}
/* When a polygon is selected, the body becomes draggable. */
.area-poly.selected { cursor: move; }
.area-poly.dragging { cursor: grabbing; opacity: .85; }

/* ---------- structures (Objekt-Werkzeug: Haus / Garage / …) ---------- */
/* Buildings read as solid grey footprints — clearly distinct from the blue
   dashed measurement areas. The draft visuals reuse the area-draft look but
   in the structure grey so the mode is recognizable while drawing. */
.structure-poly {
  fill: rgba(90, 86, 80, 0.38);
  stroke: #5a5650;
  stroke-width: 2;
  cursor: pointer;
  transition: fill .12s, stroke-width .12s;
}
.structure-poly:hover { fill: rgba(90, 86, 80, 0.5); }
.structure-poly.active {
  stroke: #2f2c28;
  stroke-width: 3;
  fill: rgba(90, 86, 80, 0.5);
  cursor: move;
}
.structure-poly.dragging { cursor: grabbing; opacity: .85; }
/* Elevated objects (base_m > 0, e.g. a Balkon) — dashed + lighter, so the
   plan shows it floats above the ground / another footprint. */
.structure-poly.elevated {
  stroke-dasharray: 6 4;
  fill: rgba(90, 86, 80, 0.22);
}
.structure-label {
  fill: #f5f2ec;
  stroke: #3a3733;
  paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 600;
  user-select: none;
}
.structure-draft-line {
  stroke: #5a5650; stroke-width: 2.5; fill: none;
  pointer-events: none;
}
.structure-draft-line.ghost { stroke-dasharray: 5 4; opacity: .85; }
.structure-draft-line.close { stroke-dasharray: 2 4; opacity: .6; }
.structure-draft-line.close.active {
  stroke: #2e5a2e; stroke-dasharray: none; stroke-width: 3; opacity: 1;
}
.structure-dot {
  fill: #5a5650; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
.structure-dot.first { fill: #8a857d; stroke-width: 3; }
.structure-dot.close-target {
  fill: #2e5a2e;
  stroke-width: 4;
  filter: drop-shadow(0 0 6px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}

/* Terrain elevation points (Geländehöhen): amber dots + halo'd "N m" labels,
   drawn above the markers. The active point (row↔plan sync) gets a ring. */
.elevation-dot {
  fill: #d98a1f; stroke: #fff; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4));
  cursor: grab;
}
.elevation-dot:hover { fill: #b8720f; }
.elevation-dot:active { cursor: grabbing; }
.elevation-dot.active {
  stroke: #2e5a2e; stroke-width: 3.5;
  filter: drop-shadow(0 0 6px rgba(217, 138, 31, .6))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}
.elevation-label {
  fill: #f5f2ec; stroke: #3a3733; paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 700; user-select: none; pointer-events: none;
}
/* Calisthenics stations: posts (dots) + bars (lines). */
.station-bar {
  stroke-linecap: round; opacity: .9; cursor: pointer;
}
.station-bar.active { opacity: 1; }
.station-bar-label {
  fill: #f5f2ec; stroke: #3a3733; paint-order: stroke; text-anchor: middle;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 700; user-select: none; pointer-events: none;
}
.station-post {
  fill: #5b6169; stroke: #fff; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4));
}
.station-post:hover { fill: #3f444b; }
.station-post.active { cursor: grab; }
.station-post.active:active { cursor: grabbing; }
.station-post.picked { stroke: #d98a1f; stroke-width: 3.5; }
/* Draggable name PILL at the centroid of the active station — the
   discoverable way to move the whole rig (works even with no bars). A filled
   rounded rect keeps the name readable over any plan background. */
.station-move { cursor: move; }
.station-move-bg {
  fill: rgba(38, 42, 50, .92); stroke: #fff; stroke-width: 1.2;
}
.station-move-label {
  fill: #fff; text-anchor: middle; dominant-baseline: middle;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 700; user-select: none; pointer-events: none;
}
/* Round rotate grip above the move pill — drag to spin the whole station. */
.station-rotate { cursor: grab; }
.station-rotate:active { cursor: grabbing; }
.station-rotate-bg {
  fill: rgba(38, 42, 50, .92); stroke: #fff; stroke-width: 1.2;
}
.station-rotate-icon {
  fill: #fff; text-anchor: middle; dominant-baseline: central;
  user-select: none; pointer-events: none;
}
/* The Geländehöhen panel inherits .area-panel's top-right anchor, which put
   it EXACTLY on top of the Flächen & Gelände panel — and those two are the
   terrain pair you tend to have open together. Stagger it below the Flächen
   panel's typical height, the way the left column (Heckenliste 56 / Objekte
   120 / Gruppen 184) already is. Panels are variable-height, so a long list
   can still reach the next one — that's what dragging them is for. */
.elevation-panel { top: 190px; }
/* ---------- Messen panel ---------- */
/* Top-right column, below the Flächen and Geländehöhen panels. */
.measure-panel { top: 324px; }

/* ---------- Planien section (in the Gelände panel) ---------- */
.leveling-row { display: flex; flex-direction: column; align-items: stretch; gap: 4px; }
.leveling-row .panel-row-head .area-m2 { flex: 0 0 auto; white-space: nowrap; }
.leveling-row.active { outline: 2px solid #8a5e2a; outline-offset: -2px; border-radius: 4px; }
.leveling-edit-btn.active {
  background: #d7ecd7; border-color: #4a8f4a; color: #2c6e2c;
}
:root[data-theme="dark"] .leveling-edit-btn.active {
  background: #2c4a2c; border-color: #5aa15a; color: #cdeccd;
}
.leveling-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 5px 8px;
  padding: 4px 2px 2px; border-top: 1px dashed var(--border, #d8d2c6);
}
/* Niveau / Übergang fields + the Modus select + the cut/fill readout. */
.area-level-field { display: inline-flex; align-items: center; gap: 3px; }
.area-level-field.is-disabled { opacity: .55; }
.area-level-inp:disabled { cursor: not-allowed; }
.area-level-lbl { font-size: .8em; color: var(--muted, #777); }
.area-level-inp {
  width: 56px; padding: 2px 4px; font: inherit; font-size: .85em;
  border: 1px solid var(--border, #cfcabd); border-radius: 4px;
  background: var(--bg-soft, #fff); color: inherit;
}
.area-level-mode {
  padding: 2px 4px; font: inherit; font-size: .85em;
  border: 1px solid var(--border, #cfcabd); border-radius: 4px;
  background: var(--bg-soft, #fff); color: inherit;
}
/* "Abtrag bis 2,3 m (≈ 45 m³)" — what the setting actually moves. */
.area-level-stats {
  flex: 1 0 100%;
  font-size: .78em; color: var(--muted, #777);
  font-variant-numeric: tabular-nums;
}
.area-level-stats:empty { display: none; }
.leveling-empty-hint {
  margin: 0; padding: 4px 10px 8px;
  color: var(--muted, #777); font-size: .88em;
}
.leveling-section .level-new-btn {
  background: #8a5e2a; color: #fff;
  border: 1px solid #6d4a20; border-radius: 4px;
  font-size: .82em; font-weight: 600; line-height: 1;
  padding: 3px 8px; cursor: pointer;
}
.leveling-section .level-new-btn:hover:not(:disabled) { background: #6d4a20; }
.leveling-section .level-new-btn:disabled { opacity: .5; cursor: not-allowed; }
/* The Gelände panel now scrolls its three sections. */
.elevation-panel .area-panel-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
.measure-row, .measure-like-row {
  grid-template-columns: auto auto minmax(0, 1fr) auto auto auto;
}
/* The two row controls, distinct on purpose: 👁 draws it on the plan, Σ counts
   it in the total. A bare checkbox never said which of the two it meant. */
.row-eye { display: inline-flex; align-items: center; justify-content: center; }
.row-eye svg { width: 18px; height: 18px; fill: currentColor; }
.row-sum { font-weight: 700; }
.row-sum.active {
  background: #d8e8ef; border-color: #1f6b8a; color: #14506a;
}
:root[data-theme="dark"] .row-sum.active {
  background: #24414f; border-color: #57a3c0; color: #cfe9f4;
}
.measure-like-row.hidden-row { opacity: .5; }
.measure-like-row.hidden-row .area-name { text-decoration: line-through; }
.measure-row.active { outline: 2px solid #1f6b8a; outline-offset: -2px; border-radius: 4px; }
.measure-edit-btn.active {
  background: #d7ecd7; border-color: #4a8f4a; color: #2c6e2c;
}
:root[data-theme="dark"] .measure-edit-btn.active {
  background: #2c4a2c; border-color: #5aa15a; color: #cdeccd;
}
.measure-row.hidden-row { opacity: .5; }
.measure-row.hidden-row .area-name { text-decoration: line-through; }
.measure-eye { display: inline-flex; align-items: center; justify-content: center; }
.measure-eye svg { width: 18px; height: 18px; fill: currentColor; }
.measure-len { font-variant-numeric: tabular-nums; font-weight: 600; white-space: nowrap; }
.measure-empty-hint {
  margin: 0; padding: 6px 10px;
  color: var(--muted, #777); font-size: .88em;
}
.measure-panel .measure-new-btn {
  background: #1f6b8a; color: #fff;
  border: 1px solid #17516a; border-radius: 4px;
  font-size: .82em; font-weight: 600; line-height: 1;
  padding: 3px 8px; cursor: pointer;
}
.measure-panel .measure-new-btn:hover:not(:disabled) { background: #17516a; }
.measure-panel .measure-new-btn:disabled { opacity: .5; cursor: not-allowed; }

/* Geländehöhen rows are two-line cards: full-width name on top, then a
   controls strip (# · height · ✎ · 🗑) — so the name is never truncated. */
.elevation-row {
  display: flex;
  flex-direction: column;
  grid-template-columns: none;
  gap: 4px;
}
.elevation-controls {
  display: flex;
  align-items: center;
  gap: 8px;
}
.elevation-idx {
  min-width: 2.2em;
  font-variant-numeric: tabular-nums;
  font-size: .8em;
  color: var(--muted, #777);
}
.elevation-inp { width: 5em; }
.elevation-name { width: 100%; box-sizing: border-box; }
.elevation-empty-hint,
.elevation-foot-hint,
.elevation-subhead {
  margin: 6px 6px 2px;
  font-size: .75em;
  color: var(--muted, #777);
}
.elevation-subhead { font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }

/* ---------- Verlauf (history timeline) panel ---------- */
/* Reuses the .area-panel base; anchored top-right BELOW the Flächen panel's
   default spot so the two don't stack when both are open. Rows are buttons:
   newest step first, the current one highlighted green, undone (redo-able)
   steps de-emphasised. */
.history-panel { top: 120px; width: min(280px, 92%); }
.history-panel-head { cursor: move; user-select: none; }
.history-list { gap: 2px; }
.history-row {
  display: flex; align-items: baseline; gap: 8px;
  width: 100%; text-align: left;
  background: none; color: inherit; font: inherit;
  border: 1px solid transparent; border-radius: 4px;
  padding: 4px 6px; cursor: pointer;
}
.history-row:hover:not(:disabled) {
  background: var(--bg-panel, rgba(0,0,0,.05));
  border-color: var(--border, rgba(0,0,0,.12));
}
.history-row.current {
  border-color: #2e7d32;
  background: rgba(46, 125, 50, .12);
  font-weight: 600;
  cursor: default;
}
.history-row.undone { opacity: .55; }
.history-time {
  font-variant-numeric: tabular-nums;
  font-size: .85em;
  color: var(--muted, #777);
  white-space: nowrap;
}
.history-label { min-width: 0; overflow-wrap: anywhere; }
.history-empty-hint,
.history-foot-hint {
  margin: 6px 8px;
  font-size: .75em;
  color: var(--muted, #777);
}

/* Contour lines (Höhenlinien): amber polylines with a halo'd "N m" label,
   draggable vertices when active, and a wide invisible hit line for grabbing. */
.contour-line {
  fill: none; stroke: #d98a1f; stroke-width: 2.5;
  stroke-linejoin: round; stroke-linecap: round;
  pointer-events: none;
}
.contour-line.active { stroke: #b8720f; stroke-width: 3.5; }
.contour-hit {
  fill: none; stroke: transparent; stroke-width: 14;
  stroke-linejoin: round; stroke-linecap: round;
  cursor: grab;
}
.contour-label {
  fill: #f5f2ec; stroke: #3a3733; paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 700; user-select: none; pointer-events: none;
}
.contour-vertex {
  fill: #d98a1f; stroke: #fff; stroke-width: 2; cursor: grab;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, .4));
}
.contour-vertex:hover { fill: #b8720f; }
.contour-draft-line {
  fill: none; stroke: #d98a1f; stroke-width: 2.5;
  stroke-dasharray: 6 5; pointer-events: none;
}
.contour-draft-dot {
  fill: #d98a1f; stroke: #fff; stroke-width: 2; pointer-events: none;
}
/* Edit handles on the ACTIVE structure — same affordances as the selected
   area (drag vertex, press midpoint to insert), in the structure grey. */
.structure-vertex {
  fill: #8a857d; stroke: white; stroke-width: 2;
  cursor: grab;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  transition: r .08s, filter .12s;
}
.structure-vertex:hover {
  fill: #2e5a2e;
  filter: drop-shadow(0 0 5px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.structure-vertex.dragging {
  cursor: grabbing;
  fill: #2e5a2e;
  stroke-width: 3;
}
.structure-midpoint {
  fill: white; stroke: #5a5650; stroke-width: 2;
  opacity: .65;
  cursor: copy;
  transition: opacity .1s, fill .1s;
}
.structure-midpoint:hover {
  opacity: 1;
  fill: #8a857d;
  stroke: white;
}
/* Per-edge length labels (Google-Maps-style) — only rendered for selected
   areas. Halo is a white stroke under a dark fill so they stay readable on
   any background. */
.area-edge-label {
  fill: #1a1a1a;
  stroke: white;
  paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 600;
  pointer-events: none;
  user-select: none;
}
:root[data-theme="dark"] .area-edge-label { fill: #f0f0f0; stroke: #1a1a1a; }
/* Vertex + midpoint handles for the selected polygon — same visual
   language as the boundary editor's, but in the area's teal accent. */
.area-vertex {
  fill: #4ca2c2; stroke: white; stroke-width: 2;
  cursor: grab;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  transition: r .08s, filter .12s;
}
.area-vertex:hover {
  fill: #2e5a2e;
  filter: drop-shadow(0 0 5px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.area-vertex.dragging {
  cursor: grabbing;
  fill: #2e5a2e;
  stroke-width: 3;
}
.area-midpoint {
  fill: white; stroke: #1f6b8a; stroke-width: 2;
  opacity: .65;
  cursor: copy;
  transition: opacity .1s, fill .1s;
}
.area-midpoint:hover {
  opacity: 1;
  fill: #4ca2c2;
  stroke: white;
}

/* ---------- Hedge tool (Heckenwerkzeug) ---------- */
/* The Heckenliste panel reuses the .area-panel base but anchors top-LEFT
   (the area panel owns top-right, the layer panel bottom-left, the zoom
   controls bottom-right). Accent is a hedge green, distinct from the
   boundary's brown, the area's teal, and the layer-align violet. */
.hedge-panel { left: 12px; right: auto; width: min(300px, 92%); }
/* Header doubles as a drag handle (move the whole panel). */
.hedge-panel-head { cursor: move; user-select: none; }
.hedge-head-actions { display: inline-flex; align-items: center; gap: 6px; }
.hedge-new-btn {
  background: #3a7a3a; color: #fff;
  border: 1px solid #2f632f; border-radius: 4px;
  font-size: .82em; font-weight: 600; line-height: 1;
  padding: 3px 8px; cursor: pointer;
}
.hedge-new-btn:hover { background: #2f632f; }
.hedge-empty-hint {
  margin: 0; padding: 8px 10px;
  color: var(--muted, #777); font-size: .9em;
}
/* The ✎ button lights up green while its hedge's line editor (or its
   object's footprint editor) is open, so the pencil reads as an on/off
   toggle (click again to leave edit mode). */
.hedge-row .hedge-edit-btn.active,
.structure-row .structure-edit-btn.active,
.elevation-row .elevation-edit-btn.active {
  background: #3a7a3a; border-color: #2f632f; color: #fff;
}
.hedge-row .hedge-edit-btn.active:hover,
.structure-row .structure-edit-btn.active:hover,
.elevation-row .elevation-edit-btn.active:hover { background: #2f632f; }
/* A hedge row is a small card: name on top, then a controls strip
   (species · spacing · count · edit · delete). Overrides the area row's
   single-line grid. */
.hedge-row {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: stretch;
}
.hedge-row .hedge-name { width: 100%; }
.hedge-row-controls {
  display: flex;
  flex-direction: column;
  gap: 5px;
}
.hedge-row-top, .hedge-row-bottom {
  display: flex;
  align-items: center;
  gap: 6px;
}
.hedge-spacing-wrap {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex: 0 1 auto;
}
/* Bestand toggle in a hedge row: compact checkbox + label; the count is
   pushed to the far right via margin-left:auto. */
.hedge-bestand {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin: 0;
  font-size: .82em;
  color: var(--muted, #777);
  white-space: nowrap;
  cursor: pointer;
}
.hedge-bestand-chk { margin: 0; }
.hedge-row-bottom .hedge-count { margin-left: auto; }
.hedge-field-label, .hedge-field-unit {
  font-size: .82em;
  color: var(--muted, #777);
  white-space: nowrap;
}
.hedge-species-sel {
  flex: 1 1 auto;
  min-width: 0;
  font-size: .92em;
  padding: 2px 4px;
  border-radius: 3px;
  background: var(--bg-panel, transparent);
  color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.2));
}
.hedge-spacing-inp {
  flex: 0 0 52px;
  width: 52px;
  font-size: .92em;
  padding: 2px 4px;
  border-radius: 3px;
  background: var(--bg-panel, transparent);
  color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.2));
}
.hedge-count {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.hedge-banner { background: #3a7a3a; }
.hedge-edit-banner { background: #3a7a3a; }
.hedge-preview { font-variant-numeric: tabular-nums; }
/* Shown when the spacing was derived from the adult width rather than a
   curated table — an unvetted species ("Weitere Arten"). */
.hedge-hint {
  margin: -4px 0 0;
  padding: 6px 8px;
  border-left: 3px solid var(--accent);
  background: var(--bg-soft);
  border-radius: 0 4px 4px 0;
  font-size: 0.82rem;
  line-height: 1.35;
}

/* ---------- Pflanzengruppen (visibility panel) ---------- */
/* Reuses the Heckenliste chrome (same floating panel, same drag-by-header)
   but its rows are single-line: eye · name · count · "nur" · 🗑. */
/* Left column, below the Heckenliste (56) and the Objektliste (120) — all
   three are draggable and remember where the user put them; the defaults
   only need to not land on top of each other on a first open. */
.plant-group-panel { top: 184px; left: 12px; right: auto; width: min(320px, 92%); }
.plant-group-panel .pg-count {
  font-size: .82em; color: var(--muted, #777);
}
.plant-group-panel .pg-count.filtered { color: var(--accent); font-weight: 600; }
/* .area-row is a 4-column grid, .hedge-row a column flex — a group row is
   neither: one line of eye · name · count · Solo · 🗑. */
.pg-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 6px;
}
/* A hidden row stays readable (it's still a row you can click) but reads as
   switched off. */
.pg-row.hidden-row .pg-label,
.pg-row.hidden-row .pg-row-count { opacity: .45; }
.pg-row.isolated { box-shadow: inset 3px 0 0 var(--accent); }
/* The plant the user just picked in 2D / hovered in 3D — a ring so it reads
   as "this one", distinct from the hidden dim, the Solo left-bar and hover. */
.pg-row.pg-highlight {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  border-radius: 4px;
}
.pg-eye {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 24px; padding: 0;
  background: none; border: 1px solid transparent; border-radius: 4px;
  color: var(--fg); cursor: pointer;
}
.pg-eye:hover { background: var(--bg-muted); }
.pg-row.hidden-row .pg-eye { color: var(--muted, #777); }
.pg-eye .mi { width: 18px; height: 18px; fill: currentColor; }
.pg-label {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 5px;
  font-size: .9em; overflow: hidden;
}
/* text-overflow has no effect on a flex container, so the name lives in its
   own shrinkable span — otherwise a long Sichtlinien-Name is cut mid-word. */
.pg-text {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.pg-label.muted { color: var(--muted, #777); font-style: italic; }
/* Per-row caret to reveal the individual plants. The empty placeholder keeps
   every row's eye in the same column. */
.pg-expand {
  flex: 0 0 auto; width: 16px; padding: 0;
  background: none; border: none; color: var(--muted, #777);
  font-size: .8em; cursor: pointer;
}
.pg-expand:hover:not(.empty) { color: var(--fg); }
.pg-expand.empty { visibility: hidden; cursor: default; }
/* An expanded row's individual plants: indented, one eye each, a colour dot
   matching the plan marker, then name + number. */
.pg-child {
  padding-left: 22px;
  font-size: .95em;
}
.pg-child.hidden-row .pg-label { opacity: .45; }
.pg-dot {
  flex: 0 0 auto; width: 11px; height: 11px; border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, .25);
}
.pg-dot.existing { border-style: dashed; }
/* Section heading: the user's own groups vs. the ones the plan derives. */
.pg-section {
  display: flex; align-items: center; gap: 4px;
  padding: 6px 4px 2px;
  font-size: .78em; font-weight: 600; letter-spacing: .02em;
  color: var(--muted, #777); text-transform: uppercase;
}
.pg-section:not(:first-child) {
  margin-top: 4px;
  border-top: 1px solid var(--border, rgba(0, 0, 0, .12));
}
.pg-section .row-fold-caret {
  background: none; border: none; color: inherit; cursor: pointer;
}
.pg-section-title { flex: 1 1 auto; min-width: 0; cursor: default; }
.pg-section-count { font-variant-numeric: tabular-nums; opacity: .8; }
.pg-name { width: 100%; min-width: 0; }
.pg-row-count {
  flex: 0 0 auto;
  min-width: 1.6em; text-align: right;
  font-size: .82em; color: var(--muted, #777); font-variant-numeric: tabular-nums;
}
.pg-only {
  flex: 0 0 auto;
  padding: 2px 7px; border-radius: 4px;
  border: 1px solid var(--border); background: var(--bg-soft);
  color: var(--fg); font-size: .78em; cursor: pointer;
}
.pg-only:hover { background: var(--bg-muted); }
.pg-only.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.pg-foot {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 10px; border-top: 1px solid var(--border);
}
.pg-reset {
  padding: 3px 8px; border-radius: 4px;
  border: 1px solid var(--border); background: var(--bg-soft);
  color: var(--fg); font-size: .8em; cursor: pointer;
}
.pg-reset:disabled { opacity: .5; cursor: default; }
.pg-hint { font-size: .8em; color: var(--muted, #777); }
/* Group picker in the multi-select action bar. */
.selection-bar-group select {
  max-width: 11em;
  font-size: .85em;
}

/* ---------- Objektliste (structures panel) ---------- */
/* Reuses the .area-panel base + the hedge row layout classes; anchored
   top-left like the Heckenliste but offset below its default spot so the
   two don't stack when both are open. Accent grey matches the footprints. */
.structure-panel { left: 12px; right: auto; top: 120px; width: min(300px, 92%); }
.structure-panel-head { cursor: move; user-select: none; }
.structure-panel .structure-new-btn {
  background: #5a5650; color: #fff;
  border: 1px solid #46433e; border-radius: 4px;
  font-size: .82em; font-weight: 600; line-height: 1;
  padding: 3px 8px; cursor: pointer;
}
.structure-panel .structure-new-btn:hover { background: #46433e; }
.structure-empty-hint {
  margin: 0; padding: 8px 10px;
  color: var(--muted, #777); font-size: .9em;
}
.structure-row {
  display: flex;
  flex-direction: column;
  gap: 5px;
  align-items: stretch;
}
.structure-row .structure-name { width: 100%; }
.structure-row.active {
  outline: 2px solid #5a5650;
  outline-offset: -2px;
  border-radius: 4px;
}
/* A Balkon attached to a building ("gehört zu") lists indented under it,
   with a subtle left rule so the grouping reads at a glance. */
.structure-row.structure-child {
  margin-left: 14px;
  border-left: 2px solid rgba(122, 116, 106, .45);
  padding-left: 6px;
}

/* ---------- Combined "Objekte" panel: scrollable body + sections ---------- */
/* The panel stacks two foldable sections (Gebäude / Stationen) inside one
   scroll container, so a tall list scrolls instead of overflowing. */
.area-panel-body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column;
}
.panel-section + .panel-section,
.station-section-mount .panel-section {
  border-top: 1px solid var(--border, #d8d2c6);
}
.panel-section-head {
  display: flex; align-items: center; gap: 6px; padding: 7px 10px 3px;
}
.panel-section-head strong { font-size: .92em; }
.panel-section-head .section-fold-caret {
  flex: 0 0 auto; width: 18px; padding: 0 2px;
  font-size: .8em; line-height: 1.5;
}
/* The "+ Neu"/"+ Station" button sits at the right edge. */
.panel-section-head > button:not(.section-fold-caret) { margin-left: auto; }
.panel-section.folded .panel-section-body { display: none; }
.panel-section-head button {
  background: #5a5650; color: #fff;
  border: 1px solid #46433e; border-radius: 4px;
  font-size: .82em; font-weight: 600; line-height: 1;
  padding: 3px 8px; cursor: pointer;
}
.panel-section-head button:hover { background: #46433e; }
.panel-section-head button:disabled { opacity: .5; cursor: not-allowed; }
.station-empty-hint {
  margin: 0; padding: 4px 10px 8px;
  color: var(--muted, #777); font-size: .88em;
}
.station-row { display: flex; flex-direction: column; gap: 5px; align-items: stretch; }
.station-row.active { outline: 2px solid #6d6a63; outline-offset: -2px; border-radius: 4px; }
.station-row-controls { display: flex; flex-direction: column; gap: 6px; }
.station-row.folded .station-row-controls { display: none; }

/* Tool rows: "Pfosten [h]m [presets] [＋ setzen]" / "Stange [kind] [＋ spannen]"
   — kept on ONE line (nowrap): the "+ setzen"/"+ spannen" button must sit in
   the same row as its label, and the middle (presets / kind select) shrinks
   before the button ever wraps (Franz 2026-07-27). */
.station-tool-row {
  display: flex; align-items: center; gap: 5px; flex-wrap: nowrap;
}
.station-tool-label {
  flex: 0 0 auto;
  font-size: .82em; color: var(--muted, #777);
}
.station-height-inp {
  flex: 0 0 auto;
  width: 50px; padding: 2px 4px;
  font: inherit; font-size: .85em;
  border: 1px solid var(--border, #cfcabd); border-radius: 4px;
  background: var(--bg-soft, #fff); color: inherit;
}
.station-height-inp:disabled { opacity: .5; }
.station-unit { flex: 0 0 auto; font-size: .74em; margin-left: -2px; }
.station-kind-sel {
  flex: 1 1 auto; min-width: 0; padding: 2px 4px;
  font: inherit; font-size: .85em;
  border: 1px solid var(--border, #cfcabd); border-radius: 4px;
  background: var(--bg-soft, #fff); color: inherit;
}
.station-presets {
  display: inline-flex; gap: 4px;
  flex: 1 1 auto; min-width: 0; overflow: hidden;
}
.station-preset-btn {
  flex: 0 0 auto;
  padding: 2px 6px; font-size: .76em; line-height: 1.2;
  border: 1px solid var(--border, #cfcabd); border-radius: 10px;
  background: var(--bg-muted, #eee); color: inherit; cursor: pointer;
}
.station-preset-btn:hover { background: var(--bg-soft, #ddd); }
.station-tool-btn {
  flex: 0 0 auto; margin-left: auto;
  padding: 3px 8px; font-size: .8em; font-weight: 600; line-height: 1;
  border: 1px solid #3a5f92; border-radius: 4px;
  background: #4a76b8; color: #fff; cursor: pointer; white-space: nowrap;
}
.station-tool-btn:hover { background: #3a5f92; }
/* The "Drehen" row is just a label + equivalent angle chips — let it WRAP
   (unlike the label+input+button tool rows) so all five buttons stay usable
   on a narrow panel. */
.station-rotate-row { flex-wrap: wrap; }
.station-rot-btn { flex: 0 0 auto; }

/* Editable sub-lists (posts / bars). */
.station-sublist {
  border-top: 1px dashed var(--border, #d8d2c6);
  padding-top: 4px; display: flex; flex-direction: column; gap: 3px;
}
.station-sublist-head { font-size: .76em; color: var(--muted, #888); font-weight: 600; }
/* One post/bar per line: the ↔ spacing + ⬍ height fields and the delete
   button stay together on the row (Franz 2026-07-27). */
.station-subrow { display: flex; align-items: center; gap: 5px; flex-wrap: nowrap; }
.station-subidx {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: #5b6169; color: #fff; font-size: .72em; font-weight: 700;
}
.station-subfield { display: inline-flex; align-items: center; gap: 3px; }
.station-subfield-lbl { font-size: .9em; color: var(--muted, #777); }
.station-bar-chip {
  flex: 0 0 auto; padding: 1px 7px; border-radius: 10px;
  color: #fff; font-size: .74em; font-weight: 600;
}
.station-subdel { margin-left: auto; }
.station-row-hint { margin: 2px 0 0; font-size: .78em; color: var(--muted, #888); }

/* Foldable panel rows (Heckenliste + Objektliste): the caret collapses a
   row to one compact head line — caret + name + summary. The summary
   only shows while folded (expanded rows show the real controls). */
.panel-row-head {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}
.panel-row-head .area-name { flex: 1 1 auto; min-width: 0; }
/* ✎ / 🗑 sit in the head row (next to the caret) — keep them compact so the
   row stays a single tidy line. */
.panel-row-head > .area-iconbtn:not(.row-fold-caret) {
  flex: 0 0 auto; width: 26px; height: 24px; font-size: .95em;
}
/* Header "alle ein-/ausklappen" toggle. */
.panel-foldall-btn {
  width: 26px; height: 24px; font-size: .95em; line-height: 1;
}
.row-fold-caret {
  flex: 0 0 auto;
  width: 20px;
  padding: 0 2px;
  font-size: .8em;
  line-height: 1.6;
}
.row-fold-sum {
  display: none;
  flex: 0 1 auto;
  font-size: .82em;
  opacity: .75;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.area-row.folded .row-fold-sum { display: inline; }
.area-row.folded .hedge-row-controls { display: none; }

/* Floating value HUD while Alt+wheel sizes a plant on the 2D plan
   ("H 4,25 m" / "B 2,50 m") — HTML so it survives marker re-renders. */
.size-wheel-hud {
  position: absolute;
  z-index: 40;
  pointer-events: none;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: .85em;
  font-weight: 600;
  background: rgba(40, 40, 36, .85);
  color: #fff;
  white-space: nowrap;
}
.structure-kind-sel {
  flex: 1 1 auto;
  min-width: 0;
  font-size: .92em;
  padding: 2px 4px;
  border-radius: 3px;
  background: var(--bg-panel, transparent);
  color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.2));
}
.structure-height-inp {
  flex: 0 0 52px;
  width: 52px;
  font-size: .92em;
  padding: 2px 4px;
  border-radius: 3px;
  background: var(--bg-panel, transparent);
  color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.2));
}
.structure-m2 {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  white-space: nowrap;
}
.structure-notes-inp {
  width: 100%;
  font-size: .88em;
  padding: 2px 6px;
  border-radius: 3px;
  background: var(--bg-panel, transparent);
  color: inherit;
  border: 1px solid var(--border, rgba(0,0,0,.2));
}
/* Committed hedge guide line, drawn under the auto-placed markers. */
.hedge-line {
  fill: none;
  stroke: #3a7a3a;
  stroke-width: 2.5;
  stroke-dasharray: 7 4;
  opacity: .8;
  pointer-events: none;
}
.hedge-label {
  fill: #1a3a1a;
  stroke: white;
  paint-order: stroke;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-weight: 600;
  user-select: none;
}
:root[data-theme="dark"] .hedge-label { fill: #d6f0d6; stroke: #14240f; }
/* Draft polyline + dots while the user is placing hedge points. */
.hedge-draft-line {
  stroke: #3a7a3a; stroke-width: 2.5; fill: none;
  pointer-events: none;
}
.hedge-draft-line.ghost { stroke-dasharray: 5 4; opacity: .85; }
.hedge-dot {
  fill: #3a7a3a; stroke: white; stroke-width: 2;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
  pointer-events: none;
}
/* Line-edit overlay (painted above the markers). The highlighted line is
   non-interactive; the vertex handles are grabbable even where a plant
   marker sits on them. */
.hedge-edit-line {
  fill: none; stroke: #3a7a3a; stroke-width: 3;
  opacity: .9; pointer-events: none;
}
/* Wide invisible hit-line under the handles — drag it to move the whole
   hedge. pointer-events: stroke so only the (fat) stroke area is grabbable. */
.hedge-edit-hit {
  fill: none; stroke: transparent;
  pointer-events: stroke; cursor: move;
}
.hedge-edit-hit.dragging { cursor: grabbing; }
.hedge-vertex {
  fill: #6fbf6f; stroke: white; stroke-width: 2;
  cursor: grab;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.45));
  transition: r .08s, filter .12s;
}
.hedge-vertex:hover {
  fill: #3a7a3a;
  filter: drop-shadow(0 0 5px rgba(58, 122, 58, .6))
          drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.hedge-vertex.dragging { cursor: grabbing; fill: #3a7a3a; stroke-width: 3; }
/* Edge-midpoint "+" handles — click / drag to insert a vertex. */
.hedge-midpoint {
  fill: white; stroke: #3a7a3a; stroke-width: 2;
  opacity: .6; cursor: copy;
  transition: opacity .1s, fill .1s;
}
.hedge-midpoint:hover { opacity: 1; fill: #6fbf6f; stroke: white; }

/* ---------- Layer alignment overlay ---------- */
/* Banner / accent uses a violet-blue distinct from the boundary's brown
   and the area's teal. The body rect is transparent but cursor:move so
   the user can grab anywhere inside the bbox to translate. */
.layer-align-banner { background: #4a4ac2; }
.layer-align-body {
  fill: transparent;
  stroke: none;
  cursor: move;
}
.layer-align-body.dragging { cursor: grabbing; }
.layer-align-outline {
  fill: none;
  stroke: #4a4ac2;
  stroke-width: 2;
  stroke-dasharray: 6 4;
  pointer-events: none;
}
.layer-align-handle {
  fill: #6a6ae0;
  stroke: white; stroke-width: 2;
  cursor: nwse-resize;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.layer-align-handle.tr,
.layer-align-handle.bl { cursor: nesw-resize; }
.layer-align-handle:hover {
  fill: #2e5a2e;
  filter: drop-shadow(0 0 5px rgba(46, 90, 46, .55))
          drop-shadow(0 1px 2px rgba(0,0,0,.4));
}
.layer-align-handle.dragging {
  fill: #2e5a2e;
  stroke-width: 3;
}

/* Sichtschutz / sightline picking banner — purple to differentiate from
   scale (primary), measure (cyan), boundary (brown), area (teal),
   layer-align (violet-blue). */
.sightline-banner { background: #7e3ea3; }

/* Sichtkegel — top-down cone overlay drawn while the sightline dialog
   is open. The cone is a translucent purple fan from viewer toward
   target; each in-cone plant casts a darker shadow polygon outward.
   pointer-events:none so the overlay can't eat clicks meant for
   markers / drag-pan / etc. */
.sightline-overlay { pointer-events: none; }
.sightline-cone {
  fill: rgba(126, 62, 163, 0.10);
  stroke: rgba(126, 62, 163, 0.55);
  stroke-width: 1.2;
  stroke-dasharray: 4 3;
}
/* The Sichtkegel-Breite margin band (span mode): the widened corridor
   the slider adds, painted behind the primary cone. Fainter fill + a
   thin dotted boundary so it reads as "tolerance around the cone", not a
   second cone. The primary cone's fill stacks on top, so the overlap
   reads a touch darker and the margin shows as a lighter ring. */
.sightline-cone-margin {
  fill: rgba(126, 62, 163, 0.06);
  stroke: rgba(126, 62, 163, 0.40);
  stroke-width: 1.0;
  stroke-dasharray: 2 3;
}
.sightline-cone-margin.inactive { fill: rgba(126, 62, 163, 0.04); stroke: rgba(126, 62, 163, 0.30); }
/* Visible-but-not-active sightline: same purple family as the active
   cone so the user reads them as "the same kind of thing", but with
   solid (not dashed) thinner stroke + lighter fill so the active one
   still pops as the focus. Earlier values (0.04 fill / 0.25 stroke
   dashed) were too faint to spot on top of the plan image. */
.sightline-cone.inactive {
  fill: rgba(126, 62, 163, 0.08);
  stroke: rgba(126, 62, 163, 0.55);
  stroke-width: 1.0;
  stroke-dasharray: none;
}
.sightline-shadow {
  fill: rgba(0, 0, 0, 0.35);
  stroke: rgba(0, 0, 0, 0.55);
  stroke-width: 0.6;
}
.sightline-shadow.inactive {
  fill: rgba(0, 0, 0, 0.16);
  stroke: rgba(0, 0, 0, 0.30);
}
/* Narrower trunk shadow painted on top of the crown shadow so the user
   sees where the trunk silhouette falls inside the Sichtkegel. Brown
   to match the side-view trunk band and the strip's trunk cells. */
.sightline-shadow-trunk {
  fill: rgba(92, 58, 33, 0.55);
  stroke: rgba(92, 58, 33, 0.8);
  stroke-width: 0.6;
}
.sightline-shadow-trunk.inactive {
  fill: rgba(92, 58, 33, 0.28);
  stroke: rgba(92, 58, 33, 0.45);
}
/* Shadow (umbra) a blocking structure casts on the plan — steel-blue to
   match the structure bands in the charts, so "this building screens this
   area" reads at a glance. Painted like the plant crown shadow. */
.sightline-shadow-structure {
  fill: rgba(55, 71, 79, 0.34);
  stroke: rgba(55, 71, 79, 0.6);
  stroke-width: 0.6;
}
.sightline-shadow-structure.inactive {
  fill: rgba(55, 71, 79, 0.16);
  stroke: rgba(55, 71, 79, 0.30);
}
/* Building edge in span mode — thick orange bar from corner A to B
   on the plan. Drawn under the dots so they stay visible on top. */
.sightline-target-edge {
  stroke: #ef6c00;
  stroke-width: 4;
  stroke-linecap: round;
  opacity: 0.85;
}
.sightline-target-edge.inactive { opacity: 0.45; stroke-width: 3; }
/* Anchor dots opt back into pointer events so they can be dragged
   (the parent .sightline-overlay group is pointer-events:none). */
.sightline-viewer-dot,
.sightline-target-dot {
  pointer-events: auto;
  cursor: move;
  stroke: white; stroke-width: 1.5;
}
.sightline-viewer-dot { fill: #7e3ea3; }
.sightline-target-dot { fill: #ef6c00; }

/* Sichtschutz dialog + side-elevation chart */
/* Non-modal: position top-right of the viewport by default so the plan
   underneath stays visible; user can drag the header to reposition. */
.sightline-dialog {
  width: min(720px, 92vw);
  position: fixed; top: 64px; right: 16px;
  margin: 0; padding: 0;
  border: 1px solid var(--bg-muted, #ccc);
  box-shadow: 0 8px 32px rgba(0,0,0,.18);
  /* Flex column so the body scrolls inside a bounded height while the 8
     resize handles (direct children, below) hug the dialog's own edges.
     overflow:visible keeps the handles from being clipped — the scroll
     lives on .sl-dialog-body. Size persists in plantplot.sightline.pos.v1
     (see sightline-dock.js). The explicit max-width overrides the 480px
     cap on the base `dialog` rule.
     display:flex is gated on [open] so a closed dialog falls back to the
     UA `dialog:not([open]) { display:none }` — without the qualifier this
     class rule wins on specificity and the emptied dialog box would stay
     visible after the form is reparented into a sidebar (docked). */
  overflow: visible;
  min-width: 360px; min-height: 280px;
  max-width: 96vw; max-height: 94vh;
}
.sightline-dialog[open] { display: flex; flex-direction: column; }
.sl-dialog-body {
  flex: 1 1 auto; min-height: 0;
  overflow: auto;
  padding: 8px;
}
/* 8-way resize handles — thin invisible grab zones on every edge + corner,
   replacing the native bottom-right-only `resize: both` grip. */
.sl-rsz { position: absolute; z-index: 6; touch-action: none; }
.sl-rsz-n { top: -4px;    left: 10px; right: 10px; height: 9px; cursor: ns-resize; }
.sl-rsz-s { bottom: -4px; left: 10px; right: 10px; height: 9px; cursor: ns-resize; }
.sl-rsz-e { right: -4px;  top: 10px; bottom: 10px; width: 9px;  cursor: ew-resize; }
.sl-rsz-w { left: -4px;   top: 10px; bottom: 10px; width: 9px;  cursor: ew-resize; }
.sl-rsz-ne { top: -4px;    right: -4px; width: 16px; height: 16px; cursor: nesw-resize; }
.sl-rsz-nw { top: -4px;    left: -4px;  width: 16px; height: 16px; cursor: nwse-resize; }
.sl-rsz-se { bottom: -4px; right: -4px; width: 16px; height: 16px; cursor: nwse-resize; }
.sl-rsz-sw { bottom: -4px; left: -4px;  width: 16px; height: 16px; cursor: nesw-resize; }

/* ---------- Sichtschutz docking (Qt-style dock widget) ---------- */
/* When docked, the dialog's <form> is reparented into a sidebar's
   .sl-dock-slot; the sidebar's native content is wrapped in .pane-native
   and a tab strip switches between them. Floating mode leaves all of this
   hidden, so the sidebars look exactly as before. */
.pane-native {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
}
.sidebar-tabs {
  display: flex; gap: 2px;
  padding: 6px 6px 0;
  background: var(--bg-muted);
  border-bottom: 1px solid var(--border);
}
.sidebar-tab {
  padding: 6px 12px;
  border: 1px solid transparent; border-bottom: none;
  border-radius: 6px 6px 0 0;
  background: transparent; color: var(--fg-muted);
  font: inherit; font-size: .9em; cursor: pointer; white-space: nowrap;
}
.sidebar-tab:hover { background: var(--bg-soft); }
.sidebar-tab.active {
  background: var(--bg-panel); color: var(--primary); font-weight: 600;
  border-color: var(--border);
}
.sidebar-tab-sl { touch-action: none; }          /* enables the tear-off drag */
.sl-dock-slot {
  flex: 1 1 auto; min-height: 0;
  overflow: auto;
  padding: 10px 12px;
}
/* Docked header isn't a move handle (gated in JS) — drop the move cursor. */
.sl-dock-slot .sightline-dialog-header { cursor: default; }
/* Drop-zone highlight while dragging the floating header over a sidebar.
   pointer-events:none so it never intercepts the drag. */
.sl-dock-drophint {
  position: fixed; z-index: 1200;
  background: rgba(126, 62, 163, 0.14);
  border: 2px dashed rgba(126, 62, 163, 0.7);
  border-radius: 6px;
  pointer-events: none;
}
.sightline-dialog-header {
  cursor: move;
  user-select: none; -webkit-user-select: none;
  margin: -8px -8px 6px -8px; padding: 6px 12px;
  background: rgba(126, 62, 163, 0.08);
  border-radius: 6px 6px 0 0;
  display: flex; align-items: center; gap: 10px;
}
.sightline-dialog-header h3 { margin: 0; font-size: 1.05em; flex: 1; }
.sightline-dialog-header.dragging { cursor: grabbing; }
.sl-header-actions { display: flex; gap: 6px; }
.sl-corner-btn {
  font-size: 0.85em; padding: 2px 8px; border-radius: 4px;
  border: 1px solid rgba(126, 62, 163, 0.4);
  background: var(--bg-panel, #fff); color: #5e3084;
  cursor: pointer;
}
.sl-corner-btn:hover { background: rgba(126, 62, 163, 0.1); }
.sl-corner-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.sl-new-btn {
  font-size: 0.85em; padding: 2px 8px; border-radius: 4px;
  border: 1px solid rgba(126, 62, 163, 0.7);
  background: #7e3ea3; color: #fff;
  cursor: pointer;
}
.sl-new-btn:hover { background: #6b3489; }

/* Sightlines panel inside the dialog — list of saved presets. Mirrors
   the area / layer panel pattern so the dialog feels consistent with
   the floating panels: rows with visibility checkbox + name + type
   chip + delete, click to activate. */
.sightline-panel {
  border: 1px solid var(--border, #d0d0d0);
  border-radius: 6px;
  background: var(--bg-soft, #fafafa);
  padding: 6px;
}
.sightline-panel-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px; padding: 0 4px 4px 4px;
}
.sightline-panel-head h4 { margin: 0; font-size: 0.95em; }
.sightline-panel-hint { font-style: italic; }
.sightline-list { list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 3px;
  max-height: 180px; overflow-y: auto; }
.sightline-row {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px; border-radius: 4px;
  cursor: pointer;
  border: 1px solid transparent;
  background: var(--bg-panel, #fff);
}
.sightline-row:hover { background: rgba(126, 62, 163, 0.06); }
.sightline-row.active {
  background: rgba(126, 62, 163, 0.12);
  border-color: rgba(126, 62, 163, 0.45);
}
.sightline-row .sl-row-vis {
  flex: 0 0 auto; cursor: pointer;
  background: none; border: none; padding: 2px 4px;
  font-size: 1em; line-height: 1;
  border-radius: 3px;
  opacity: 0.95;
}
.sightline-row .sl-row-vis[aria-pressed="false"] { opacity: 0.45; }
.sightline-row .sl-row-vis:hover { background: rgba(126, 62, 163, 0.1); }
.sightline-row input.sl-row-name {
  flex: 1 1 auto; min-width: 80px;
  border: 1px solid transparent;
  background: transparent; padding: 2px 4px; border-radius: 3px;
  font: inherit; color: inherit;
}
.sightline-row input.sl-row-name:focus {
  border-color: rgba(126, 62, 163, 0.45);
  background: var(--bg-panel, #fff);
  outline: none;
}
.sightline-row .sl-row-type {
  flex: 0 0 auto; font-size: 0.75em; padding: 1px 6px;
  border-radius: 999px; background: rgba(126, 62, 163, 0.15);
  color: #5e3084; font-weight: 600;
}
.sightline-row .sl-row-type.span { background: rgba(179, 116, 42, 0.18); color: #6a4514; }
.sightline-row .sl-row-del {
  flex: 0 0 auto; background: none; border: none; cursor: pointer;
  color: #b3242a; padding: 2px 4px; border-radius: 3px;
  font-size: 0.95em;
}
.sightline-row .sl-row-del:hover { background: rgba(179, 36, 42, 0.1); }
.sightline-panel-empty { margin: 4px 0 0 4px; }
.sightline-dialog form { display: flex; flex-direction: column; gap: 10px; }
.sightline-controls { display: flex; gap: 14px; flex-wrap: wrap; }
.sightline-controls label { display: flex; flex-direction: column; gap: 2px; font-size: .9em; }
.sightline-controls input { width: 90px; }
.sightline-chart { width: 100%; }
.sightline-svg { width: 100%; height: auto; display: block;
  background: var(--bg-soft, #f6f7f4); border-radius: 6px; }
.sl-axes-bg { fill: transparent; }
/* The ground is a POLYLINE (the terrain profile; flat plots yield a
   straight line) — fill must stay off or the browser fills it black. */
.sl-ground { stroke: #444; stroke-width: 1.5; fill: none; }
/* Earth body under the terrain profile in the Seitenansicht. Translucent
   so the metric grid stays readable through a tall knoll. */
.sl-terrain { fill: rgba(139, 106, 74, .18); stroke: none; }
.sl-grid { stroke: rgba(0,0,0,.06); stroke-width: 1; }
.sl-tick { stroke: #444; stroke-width: 1; }
.sl-axis-label { font-size: 10px; fill: #555; }
.sl-axis-title { font-size: 11px; fill: #333; font-weight: 600; }
.sl-sightline { stroke: #7e3ea3; stroke-width: 2; stroke-dasharray: 6 4; }
.sl-eye { fill: #7e3ea3; stroke: white; stroke-width: 1.5; }
/* Target = the thing being screened. Vivid orange, deliberately clear of
   the trunk brown (#5c3a21 / #8b5a2b) it used to be confused with. Matches
   the orange target dot/edge on the plan. */
.sl-target { fill: #ef6c00; opacity: .9; }
.sl-plant.blocking { fill: #2e7d32; opacity: .85; }
.sl-plant.pass     { fill: #9e9e9e; opacity: .65; }
/* Season: a bare deciduous crown — outline only, see-through, no screening. */
.sl-plant.bare {
  fill: none; stroke: #8d6e63; stroke-width: 1.4;
  stroke-dasharray: 4 3; opacity: .8;
}
/* Marzeszenz: brown DEAD foliage that stays opaque (Hainbuchen-/Buchenhecke
   in winter). Solid, so it still reads as screening — just not green. Must
   follow .blocking so the fill wins while the green accent stroke stays. */
.sl-plant.dry { fill: #a1785a; stroke: #7a5a44; }
/* Thin brown column under the crown for species with crown_base > 0.
   Rendered separately from the leafy crown so the side-elevation chart
   shows a beech's trunk band where the sight line can sneak under. */
.sl-trunk { fill: #5c3a21; opacity: .75; }
/* Structures (Haus / Garage / Mauer / Balkon band) in both charts —
   STEEL-BLUE (not the old warm grey, which was hard to tell from the
   pass-plant grey #9e9e9e), so they read as "built, not planted". A
   blocking structure gets the green accent stroke to match the
   blocking-plant colour language. */
.sl-structure, .sf-structure { fill: #607d8b; opacity: .85; stroke: #37474f; stroke-width: 1; }
.sl-structure.blocking, .sf-structure.blocking { stroke: #2e7d32; stroke-width: 2; }
/* Name label centred on a structure band so a grey block reads as
   "Gartenhaus". White with a subtle halo for legibility over the fill. */
.sl-structure-label { font-size: 10px; fill: #fff; font-weight: 600;
  paint-order: stroke; stroke: rgba(0,0,0,.35); stroke-width: 2.5px;
  pointer-events: none; }
.sl-legend-eye      { color: #7e3ea3; font-weight: bold; }
.sl-legend-line     { color: #7e3ea3; font-weight: bold; letter-spacing: -1px; }
.sl-legend-target   { color: #ef6c00; }
.sl-legend-blocking { color: #2e7d32; }
.sl-legend-trunk    { color: #8b5a2b; }
.sl-legend-structure { color: #607d8b; }
.sl-legend-terrain  { color: #8b6a4a; }
.sl-legend-bare     { color: #8d6e63; }
.sl-legend-dry      { color: #a1785a; }
.sl-legend-pass     { color: #9e9e9e; }

/* Span-mode front strip — one cell per sample. Empty when in
   single-point mode. */
.sightline-strip { width: 100%; }
.sightline-strip:empty { display: none; }
.sightline-strip-svg { width: 100%; height: 28px; display: block;
  background: var(--bg-soft, #f6f7f4); border-radius: 6px; }
.sl-strip-cell.blocking { fill: #2e7d32; }
.sl-strip-cell.trunk    { fill: #8b5a2b; }   /* slightly lighter so the brown
                                                 strip peeking under a green
                                                 overlay still reads as "trunk". */
.sl-strip-cell.structure { fill: #607d8b; }  /* building blocks this sample */
.sl-strip-cell.terrain  { fill: #8b6a4a; }   /* the ground itself rises over
                                                 the sight line at this sample */
.sl-strip-cell.pass     { fill: #d0d0d0; }
.sl-strip-cell-hit      { fill: transparent; }

[data-theme="dark"] .sightline-svg { background: rgba(255,255,255,.04); }
[data-theme="dark"] .sl-grid { stroke: rgba(255,255,255,.08); }
[data-theme="dark"] .sl-axis-label { fill: #aaa; }
[data-theme="dark"] .sl-axis-title { fill: #ddd; }

/* Sichtfeld — first-person field-of-view chart. Same surface as the
   side-elevation chart so the two read as a pair. */
.sightline-sichtfeld { width: 100%; }
.sichtfeld-svg { width: 100%; height: auto; display: block;
  background: var(--bg-soft, #f6f7f4); border-radius: 6px; }
.sf-axes-bg { fill: transparent; }
.sf-axis-center { stroke: rgba(126, 62, 163, .35); stroke-width: 1;
  stroke-dasharray: 2 3; }
.sf-target { fill: #ef6c00; opacity: .9; }
.sf-plant.blocking { fill: #2e7d32; opacity: .85; }
.sf-plant.pass     { fill: #9e9e9e; opacity: .65; }
.sf-plant.bare {
  fill: none; stroke: #8d6e63; stroke-width: 1.4;
  stroke-dasharray: 4 3; opacity: .8;
}
.sf-plant.dry { fill: #a1785a; stroke: #7a5a44; }
/* Buried plant: a nearer crown is painted in front of it, so we stroke
   its silhouette on top (no fill) to keep its presence visible. Dashed
   so it reads as "behind", not as another solid plant. */
.sf-plant-occluded { fill: none; stroke: #fff; stroke-width: 1.4;
  stroke-dasharray: 4 3; opacity: .9; }
[data-theme="dark"] .sichtfeld-svg { background: rgba(255,255,255,.04); }
[data-theme="dark"] .sf-plant-occluded { stroke: #e8e8e8; }

/* Strip toggle in the dialog footer. */
.sl-toggle-strip { display: inline-flex; gap: 6px; align-items: center;
  margin-right: auto; font-size: .9em; user-select: none; cursor: pointer; }

/* Preset bar — dropdown + Save/Rename/Delete buttons just under the
   summary line. Compact so it doesn't crowd the controls row. */
.sightline-presets {
  display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
  padding: 4px 0; font-size: .9em;
}
.sightline-presets label {
  display: inline-flex; gap: 4px; align-items: center; margin: 0;
}
.sightline-presets select { min-width: 120px; }
.sightline-presets button { font-size: .9em; padding: 2px 8px; }
.sightline-presets button[disabled] { opacity: .4; cursor: not-allowed; }
[data-theme="dark"] .sl-ground, [data-theme="dark"] .sl-tick { stroke: #aaa; }

/* markers on the plan (SVG) */
.planting-marker { cursor: grab; touch-action: none; }
.planting-marker.dragging { cursor: grabbing; }
/* Adult crown circle behind the marker dot — translucent green so the
   aerial image stays legible and several overlapping crowns don't turn
   into mud. Dashed stroke reads as a "future projection" rather than
   a current footprint. pointer-events:none so clicks fall through to
   the dot. */
.planting-marker circle.crown {
  fill: rgba(46, 90, 46, .08);
  stroke: rgba(46, 90, 46, .45);
  stroke-width: 1.2;
  stroke-dasharray: 4 3;
  pointer-events: none;
}
.planting-marker circle.dot {
  stroke: white; stroke-width: 2;
  transition: r .1s, filter .12s;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.3));
}
/* Bestandspflanze (existing plant): de-emphasised dashed grey so it reads as
   context the plan reasons around, not part of the order. The grey fill
   overrides the inline species colour — a presentation attribute loses to any
   author CSS rule. Placed before .selected so the selection ring still wins. */
.planting-marker.existing circle.dot {
  fill: #9a9a9a;
  stroke: #5a5a5a;
  stroke-dasharray: 3 2;
  opacity: .9;
}
.planting-marker.existing circle.crown {
  fill: rgba(90, 90, 90, .07);
  stroke: rgba(90, 90, 90, .4);
}
.planting-marker:hover circle.dot { filter: drop-shadow(0 2px 4px rgba(0,0,0,.5)); }
.planting-marker.dragging circle.dot {
  filter: drop-shadow(0 4px 8px rgba(0,0,0,.6));
  opacity: .85;
}
/* Multi-select state: halo ring around the dot. paint-order on the
   stroke isn't needed because the dot itself isn't text. The accent
   colour matches the Maßband / boundary-vertex active state so it's
   visually consistent with "this is the thing you're acting on". */
.planting-marker.selected circle.dot {
  stroke: #c08a3a;
  stroke-width: 4;
  filter: drop-shadow(0 0 6px rgba(192, 138, 58, .7))
          drop-shadow(0 1px 2px rgba(0, 0, 0, .35));
}
/* Number inside the dot (large markers). White on the fill colour. */
.planting-marker text.marker-num {
  fill: white;
  font-weight: 700;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  pointer-events: none;
  user-select: none;
}
/* Label beside the marker — short plant name; the slot is picked per render
   so labels dodge each other. Lives in its own `g.marker-labels` layer ABOVE
   every marker (SVG has no z-index, so a label inside its marker's group is
   painted over by every marker drawn after it). paint-order:stroke draws the
   white halo first so the dark glyph stays readable against any image. */
.marker-labels { pointer-events: none; }
.marker-labels text.marker-label {
  fill: #1a1a1a;
  stroke: white;
  paint-order: stroke;
  stroke-linejoin: round;
  font-weight: 600;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  pointer-events: none;
  user-select: none;
}
/* Big enough dot: the name rides ON the marker instead of beside it (no halo
   needed on a solid fill). The fill is set inline per marker from
   readableTextColor — the palette isn't uniformly dark. */
.marker-labels text.marker-label.inside {
  stroke: none;
  font-weight: 700;
}
/* Bestandspflanzen read de-emphasised, like their dashed grey dot. */
.marker-labels text.marker-label.existing { fill: #4a4a4a; }
/* No free slot in a dense cluster: hidden rather than piled on top of its
   neighbours — but shown again as soon as the marker is hovered or picked
   (canvas.js mirrors both states onto the node; the label is not a child of
   the marker any more, so CSS can't cascade them). Zooming in frees real
   slots and brings it back for good. */
.marker-labels text.marker-label.crowded { opacity: 0; }
.marker-labels text.marker-label.crowded.hovered,
.marker-labels text.marker-label.crowded.selected { opacity: 1; }

/* ---------- Report pane ---------- */
.report-body {
  flex: 1; overflow-y: auto; padding: 12px 16px;
  font-size: .9em;
}
.report-body .empty-hint {
  color: var(--fg-muted); font-style: italic; text-align: center; margin-top: 30px;
}
/* Single-line property area shown above the summary cards when the
   boundary polygon AND a scale are both set. Sits flush at the top of
   the report so it reads as a header, not just another factoid. */
.report-property-area {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px;
  margin-bottom: 12px;
  padding: 6px 10px;
  background: var(--bg-muted);
  border-radius: 6px;
  border-left: 3px solid #8a5a2a;
}
.report-property-area .lbl {
  color: var(--fg-muted);
  font-size: .85em;
  letter-spacing: .02em;
}
.report-property-area .val {
  font-size: 1.15em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--primary);
}
.report-summary {
  display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px;
  margin-bottom: 16px;
}
/* Bestandspflanzen fold — sits under the summary cards, de-emphasised since
   these plants aren't ordered (dashed grey, mirroring the existing markers).
   The count line is the <summary>; expanding reveals a per-species table. */
.report-existing-fold { margin: -8px 0 16px; }
.report-existing-note {
  padding: 5px 10px;
  font-size: .82em;
  color: var(--fg-muted);
  background: var(--bg-muted);
  border-left: 3px dashed #8a8a8a;
  border-radius: 6px;
  cursor: pointer;
  user-select: none;
}
.report-existing-fold[open] .report-existing-note {
  border-radius: 6px 6px 0 0;
}
.report-existing-body {
  background: var(--bg-muted);
  border-left: 3px dashed #8a8a8a;
  border-radius: 0 0 6px 6px;
  padding: 0 10px 6px;
}
.report-existing-body .report-table { font-size: .82em; }
.report-existing-body .report-table td { color: var(--fg-muted); }
.summary-card {
  background: var(--bg-muted); padding: 10px; border-radius: 8px; text-align: center;
}
.summary-card .val { font-size: 1.6em; font-weight: 700; color: var(--primary); }
.summary-card .lbl { font-size: .8em; color: var(--fg-muted); margin-top: 2px; }
/* "davon X in Hecken" sub-line on a summary card — explains a big number
   at a glance without opening the fold below. */
.summary-card .sub { font-size: .72em; color: var(--fg-muted); margin-top: 3px; }
/* "Bedarf (zu kaufen)" heading over the summary cards — makes explicit that
   the numbers are the order quantity, as opposed to the Bestand fold. */
.report-block-head {
  margin: 0 0 8px; color: var(--primary); font-size: 1em;
  border-bottom: 1px solid var(--border); padding-bottom: 4px;
}
/* Hecken fold: same chrome as the Bestand fold but with a solid green
   accent — hedge plants ARE part of the order, not de-emphasised. */
.report-hedge-fold .report-existing-note,
.report-hedge-fold .report-existing-body {
  border-left-style: solid;
  border-left-color: #4a7a4a;
}

.report-section h3 {
  margin: 16px 0 8px; color: var(--primary); font-size: 1em;
  border-bottom: 1px solid var(--border); padding-bottom: 4px;
}
/* Bottom-of-report Jahreskalender: same .ci-monthbars layout as the
   catalog cards / dialog, but a touch taller so the sidebar's cells stay
   legible and tooltip-able. */
.report-calendar .ci-monthbars { font-size: .8em; }
.report-calendar .ci-monthbars .mb-cell { height: 12px; }
.report-table {
  width: 100%; border-collapse: collapse; font-size: .85em;
}
.report-table th, .report-table td {
  padding: 4px 6px; text-align: left; border-bottom: 1px dotted var(--border);
}
.report-table th { color: var(--fg-muted); font-weight: 500; }
.report-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Marker numbers ("Nr. 1–3"): a lookup key, not a quantity — kept quiet so
   the Stückzahl stays the number the eye lands on. */
.report-table td.marker-nrs { font-size: .92em; white-space: nowrap; }
/* Species colour dot in front of a table row — same colour as the marker on
   the plan, so a row can be found out there. */
.species-swatch {
  display: inline-block; width: 9px; height: 9px; border-radius: 50%;
  margin-right: 6px; vertical-align: baseline;
  border: 1px solid rgba(0, 0, 0, .25);
}
/* Bestand markers are grey AND dashed on the plan; the dot mirrors that so
   the two read as the same thing. */
.species-swatch[data-existing] { border-style: dashed; border-color: #5a5a5a; }
.report-table .row-total td {
  padding-top: 8px; font-weight: 600; color: var(--primary);
  border-top: 2px solid var(--border); border-bottom: none;
}

/* ---------- Dialogs ---------- */
dialog {
  border: none; border-radius: 12px; padding: 20px 24px;
  box-shadow: 0 8px 30px rgba(0,0,0,.2);
  max-width: 480px; width: 94%;
  background: var(--bg-panel); color: var(--fg);
}
dialog::backdrop { background: rgba(0,0,0,.3); }
dialog h3 { margin: 0 0 8px; color: var(--primary); }
/* The planting/hedge dialog title doubles as a drag handle (see app.js). */
#pd-title { cursor: move; user-select: none; }
#sc-title { cursor: move; user-select: none; }

/* ---- shortcut / gesture help card ---- */
.shortcuts-dialog { max-width: 720px; }
.sc-body { column-count: 2; column-gap: 28px; margin-top: 4px; }
.sc-section { break-inside: avoid; margin: 0 0 12px; }
.sc-title { margin: 0 0 4px; font-size: .9em; color: var(--primary); }
.sc-row {
  display: grid; grid-template-columns: minmax(110px, max-content) 1fr;
  gap: 4px 10px; align-items: baseline; padding: 2px 0;
}
.sc-keys { white-space: nowrap; }
.sc-desc { font-size: .85em; color: var(--fg); }
.sc-join { margin: 0 3px; color: var(--fg-muted); font-size: .8em; }
/* Top right of the header's brand line. NOT in the toolbar: that row wraps,
   and a version wedged between two buttons on the second line reads as a
   control. Here it is a corner readout that stays put however the toolbar
   reflows. */
.app-header .app-version {
  margin-left: auto; align-self: center;
  font-size: .78em; white-space: nowrap; cursor: default;
  color: rgba(255, 255, 255, .62);
  font-variant-numeric: tabular-nums;
}
@media (max-width: 620px) { .app-header .app-version { display: none; } }

.sc-version {
  grid-column: 1 / -1;
  margin: 14px 0 0; padding-top: 10px;
  border-top: 1px solid var(--border);
  font-size: .82em; color: var(--fg-muted); text-align: right;
}
.sc-build { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; opacity: .75; }

kbd {
  display: inline-block; padding: 1px 6px; font: inherit; font-size: .78em;
  line-height: 1.5; background: var(--bg-muted); color: var(--fg);
  border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 4px;
}
@media (max-width: 560px) { .sc-body { column-count: 1; } }
dialog label {
  display: block; margin: 10px 0; font-size: .9em; color: var(--fg-muted);
}
dialog input[type=number], dialog input[type=text], dialog textarea, dialog select {
  display: block; width: 100%; padding: 6px 8px; margin-top: 4px;
  border: 1px solid var(--border); border-radius: 6px; font: inherit;
  background: var(--bg-panel); color: var(--fg);
}
/* Combobox: a text field that filters a popup listbox (species picker). One
   control replacing the old search-box + dropdown pair. */
.combobox { position: relative; display: block; }
.combobox-input:disabled { opacity: .55; }
.combobox-list {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
  margin: 2px 0 0; padding: 4px; list-style: none;
  max-height: 240px; overflow-y: auto;
  background: var(--bg-panel); color: var(--fg);
  border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .18);
}
.combobox-option {
  display: flex; align-items: baseline; gap: 6px;
  padding: 5px 8px; border-radius: 4px; cursor: pointer;
}
.combobox-option.active { background: var(--bg-muted); }
.combobox-label { font-weight: 500; }
.combobox-hint { color: var(--fg-muted); font-size: .85em; font-style: italic; }
.combobox-badge {
  margin-left: auto; font-size: .72em; padding: 1px 6px; border-radius: 999px;
  background: var(--bg-soft); border: 1px solid var(--border); color: var(--fg-muted);
}
.combobox-empty { padding: 6px 8px; color: var(--fg-muted); font-style: italic; }
/* Section heading inside the popup (hedge mode: Empfohlen / Weitere Arten). */
.combobox-group {
  padding: 6px 8px 2px; font-size: .72em; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; color: var(--fg-muted);
}
/* Bulk-edit hint above the fields: only shown when the dialog edits a
   multi-marker selection (explains that blank fields stay untouched). */
.pd-bulk-hint { font-size: .8em; margin: 4px 0 0; }
/* Button row: wraps if the dialog is narrow; the destructive "Löschen"
   button sits on the far left (margin-right:auto) and is visually
   separated from the primary actions — idiomatic dialog layout and it
   avoids the cut-off we had when all four buttons fought for space. */
dialog menu {
  display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end;
  padding: 0; margin: 16px 0 0;
}
dialog menu button {
  padding: 6px 12px; border-radius: 6px; border: 1px solid var(--border);
  background: var(--bg-panel); color: var(--fg);
  cursor: pointer; font-size: .9em;
  white-space: nowrap;
}
dialog menu button[type=submit] {
  background: var(--primary); color: white; border-color: var(--primary);
}
dialog menu button.danger {
  background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border);
  margin-right: auto;        /* push the destructive action to the left */
}
.muted { color: var(--fg-muted); font-size: .9em; margin: 4px 0; }

/* ---------- Lightbox ---------- */
/* Native <dialog> takes care of Esc + focus trap. We strip its default
   chrome and lay the image / nav buttons absolutely on the backdrop. */
dialog.lightbox {
  background: transparent;
  border: none;
  padding: 0;
  margin: 0;
  width: 100vw; max-width: 100vw;
  height: 100vh; max-height: 100vh;
  overflow: hidden;
}
dialog.lightbox::backdrop { background: rgba(0, 0, 0, .88); }
.lightbox .lb-img {
  position: absolute; top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  max-width: 92vw; max-height: 86vh;
  border-radius: 8px;
  box-shadow: 0 10px 40px rgba(0, 0, 0, .6);
  background: #222;
  cursor: default;
}
.lightbox .lb-btn {
  position: absolute;
  background: rgba(0, 0, 0, .55);
  color: white;
  border: 1px solid rgba(255, 255, 255, .3);
  border-radius: 999px;
  width: 44px; height: 44px;
  font-size: 1.6em;
  line-height: 1;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .15s;
}
.lightbox .lb-btn:hover { background: rgba(0, 0, 0, .8); }
.lightbox .lb-close { top: 16px; right: 16px; }
.lightbox .lb-nav   { top: 50%; transform: translateY(-50%); }
.lightbox .lb-prev  { left: 16px; }
.lightbox .lb-next  { right: 16px; }
.lightbox .lb-caption {
  position: absolute; bottom: 18px; left: 50%; transform: translateX(-50%);
  background: rgba(0, 0, 0, .55);
  color: white;
  padding: 6px 14px;
  border-radius: 999px;
  font-size: .9em;
  margin: 0;
  max-width: 80vw;
  text-align: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Make catalog photos look clickable */
.ci-thumb, .ci-gallery img { cursor: zoom-in; }
.pd-photo { cursor: zoom-in; }
.pd-head {
  display: grid; grid-template-columns: 70px 1fr; gap: 10px;
  align-items: center; margin: 6px 0 10px;
}
.pd-photo {
  width: 70px; height: 70px; object-fit: cover;
  border-radius: 8px; background: var(--bg-image-fallback);
}
.pd-headinfo { min-width: 0; }
.pd-facts { font-size: .8em; margin-top: 2px; }
/* The 12-month chart inside the dialog. .ci-monthbars handles its own
   layout (52 px label column + 12 equal cells); we just give it some
   breathing room from the photo/facts block above. Empty when the plant
   has no month data — `monthBarsHtml` returns ""; the wrapper collapses. */
.pd-monthbars { margin: 4px 0 10px; }
.pd-monthbars:empty { display: none; }

/* ---------- Geo-Maßstab dialog ---------- */
#georef-dialog { max-width: 460px; }
.georef-help { margin: 4px 0 12px; }
.georef-help code {
  background: var(--bg-muted, rgba(0,0,0,.06));
  padding: 0 4px; border-radius: 3px;
  font-size: .95em;
}
.georef-fields {
  border: 1px solid var(--border, rgba(0,0,0,.18));
  border-radius: 6px;
  padding: 6px 10px 8px;
  margin: 6px 0;
}
.georef-fields legend {
  padding: 0 6px;
  font-size: .85em; font-weight: 600;
  color: var(--muted, #666);
  text-transform: uppercase; letter-spacing: .04em;
}
.georef-fields label {
  display: inline-flex; flex-direction: column; gap: 2px;
  font-size: .85em; margin-right: 12px;
}
.georef-fields label input {
  width: 110px; padding: 3px 6px;
}
.georef-fields .small { margin: 6px 0 0; }
.georef-fields .georef-pair {
  width: 220px; padding: 3px 6px;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: .9em;
}
#georef-status:not(:empty) {
  color: #c43030;
  margin: 6px 0 0;
}

/* ---------- Print options dialog (on-screen) ---------- */
.print-options {
  border: none; padding: 0; margin: 8px 0 4px;
  display: grid; gap: 6px;
}
.print-options label {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 0; cursor: pointer;
}
.print-options input[type="checkbox"] { margin: 0; }
#print-dialog .small { font-size: .85em; }

/* ---------- Print view (#print-view) ---------- */
/* Off-screen on screen (not display:none) so the browser actually loads
   images inside it before the print dialog snapshots the page — some
   engines defer image loading inside `display:none` subtrees, which made
   photos render as blank boxes when the user printed without first
   expanding the matching catalog cards.
   The @media print block below relocates it into the document flow. */
#print-view {
  position: fixed;
  left: -100000px;
  top: 0;
  width: 210mm;
  pointer-events: none;
  visibility: hidden;
  z-index: -1;
}

@media print {
  /* Page setup: A4 with sane margins so the plan has room to breathe. */
  @page { size: A4; margin: 14mm; }

  /* Light surface for printing — never carry dark-mode colors onto paper.
     Also forces backgrounds (calendar cells use rgba) to actually print
     in Chromium / Firefox. */
  :root, body {
    background: #fff !important;
    color: #111 !important;
  }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  /* Hide everything that's part of the live app. */
  .app-header, .app-main, .toast-stack,
  dialog, #hidden-plan-input { display: none !important; }

  /* The print view becomes the document. */
  #print-view {
    position: static !important;
    left: auto !important; top: auto !important;
    width: auto !important;
    visibility: visible !important;
    pointer-events: auto !important;
    z-index: auto !important;
    display: block !important;
    margin: 0; padding: 0;
    color: #111;
    font: 11pt/1.35 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  }

  .print-header {
    border-bottom: 1pt solid #888;
    margin-bottom: 6mm;
    padding-bottom: 3mm;
  }
  .print-header h1 { font-size: 18pt; margin: 0 0 2mm; }
  .print-header .print-meta { margin: 0; color: #444; font-size: 10pt; }

  /* Each major section starts on a new page after the first. */
  .print-section {
    break-inside: avoid;
    page-break-inside: avoid;
    margin-bottom: 8mm;
  }
  .print-section + .print-section {
    break-before: page;
    page-break-before: always;
    margin-top: 0;
  }
  .print-section h2 {
    font-size: 14pt; margin: 0 0 4mm;
    border-bottom: 0.5pt solid #bbb; padding-bottom: 1mm;
  }
  .print-empty { color: #666; font-style: italic; }

  /* ----- Plan ----- */
  .print-plan .print-svg {
    display: block;
    width: 100%;
    /* Leave headroom for the title + page margin so a wide aerial doesn't
       force a second page on its own. */
    max-height: 230mm;
  }

  /* ----- 3D-Standpunkte ----- */
  /* Figures stay atomic (image + caption never split); the SECTION may
     flow across pages — several viewpoints don't fit on one. */
  .print-viewpoints {
    break-inside: auto;
    page-break-inside: auto;
  }
  .print-viewpoint {
    break-inside: avoid;
    page-break-inside: avoid;
    margin: 0 0 6mm;
  }
  .print-viewpoint img {
    display: block;
    width: 100%;
    max-height: 120mm;
    object-fit: contain;
    border: 0.5pt solid #ccc;
  }
  .print-viewpoint figcaption {
    margin-top: 1.5mm;
    font-size: 10pt;
    color: #444;
  }

  /* ----- Sichtschutz-Analyse ----- */
  /* One block per Sichtlinie stays atomic (heading + summary + charts);
     the SECTION flows across pages — several Sichtlinien don't fit on
     one. */
  .print-sightline {
    break-inside: auto;
    page-break-inside: auto;
  }
  .print-sightline-block {
    break-inside: avoid;
    page-break-inside: avoid;
    margin: 0 0 8mm;
  }
  .print-sightline-block h3 { font-size: 12pt; margin: 0 0 1mm; }
  .print-sl-summary { margin: 0 0 3mm; font-size: 9.5pt; color: #444; }
  .print-sl-figure { margin: 0 0 3mm; }
  .print-sl-figure svg { display: block; width: 100%; }
  .print-sl-figure figcaption { margin-top: 0.5mm; font-size: 9pt; color: #555; }
  /* The charts must print with their light-theme colors even when the
     app is in dark mode — the [data-theme="dark"] chart overrides would
     put near-white grid/axis strokes on white paper. The #print-view id
     outranks the attribute+class selectors. */
  #print-view .sightline-svg, #print-view .sichtfeld-svg { background: #fff; }
  #print-view .sl-grid { stroke: rgba(0,0,0,.06); }
  #print-view .sl-axis-label { fill: #555; }
  #print-view .sl-axis-title { fill: #333; }
  #print-view .sl-ground, #print-view .sl-tick { stroke: #444; }

  .print-svg .print-boundary {
    fill: rgba(192, 138, 58, 0.08);
    stroke: #c08a3a;
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
  }
  .print-svg .print-marker-num {
    fill: #fff;
    font-weight: 700;
    paint-order: stroke;
    stroke: rgba(0,0,0,.3);
    stroke-width: 0.5;
  }
  .print-svg .print-marker-label {
    fill: #111;
    font-weight: 600;
    paint-order: stroke;
    stroke: #fff;
    stroke-linejoin: round;
  }
  /* Name drawn ON a big enough dot; fill set inline per marker. */
  .print-svg .print-marker-label.inside {
    stroke: none;
    font-weight: 700;
  }
  .print-svg .print-marker-label.existing { fill: #4a4a4a; }
  /* Bestandspflanze in print: dashed grey outline to match the on-screen
     marker and signal "vorhanden, nicht bestellt". */
  .print-svg .print-marker.existing .print-dot {
    stroke: #5a5a5a;
    stroke-width: 1.5;
    stroke-dasharray: 3 2;
  }
  /* Bestandspflanzen section: explanatory note under the heading. */
  .print-existing-note {
    margin: 0 0 3mm;
    font-size: 9pt;
    color: #555;
  }

  /* ----- Bedarfsliste ----- */
  .print-summary {
    display: flex; gap: 14mm; flex-wrap: wrap;
    margin-bottom: 4mm; font-size: 10.5pt;
  }
  .print-table {
    width: 100%; border-collapse: collapse;
    font-size: 10pt;
  }
  .print-table th, .print-table td {
    text-align: left; padding: 1.5mm 2mm;
    border-bottom: 0.4pt solid #ccc;
    vertical-align: top;
  }
  /* The species dot is the whole point of printing it — browsers strip
     backgrounds from print by default, so ask for it explicitly. */
  #print-view .species-swatch {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .print-table thead th {
    border-bottom: 0.6pt solid #555;
    font-weight: 600;
  }
  .print-table .num { text-align: right; white-space: nowrap; }
  .print-table .lat { color: #555; font-style: italic; }
  .print-table .muted { color: #888; }
  .print-table .tag {
    display: inline-block; font-size: 8pt;
    border: 0.4pt solid #888; border-radius: 2pt;
    padding: 0 1mm; margin-left: 1mm; color: #555;
  }
  .print-table tr { break-inside: avoid; page-break-inside: avoid; }

  /* ----- Jahreskalender ----- */
  /* 13 columns: a label cell + 12 month cells. Mirrors .ci-monthbars but
     sized in mm so the print output is consistent across browsers. */
  .print-cal {
    display: grid;
    grid-template-columns: 22mm repeat(12, 1fr);
    gap: 1mm;
    align-items: center;
  }
  .print-cal .pc-head, .print-cal .pc-cell {
    height: 6mm; border: 0.3pt solid #ddd;
    display: flex; align-items: center; justify-content: center;
    font-size: 8.5pt; color: #555;
  }
  .print-cal .pc-cell { background: #f1f1f1; }
  .print-cal .pc-cell.on { border-color: rgba(0,0,0,.15); }
  .print-cal .pc-lbl {
    font-size: 9.5pt; color: #333; font-weight: 600; padding-right: 2mm;
  }

  /* ----- Fotogalerie ----- */
  .print-photos .pp-card {
    break-inside: avoid; page-break-inside: avoid;
    margin-bottom: 6mm;
  }
  .print-photos .pp-card header { margin-bottom: 2mm; }
  .print-photos .pp-card h3 { margin: 0; font-size: 12pt; }
  .print-photos .pp-card .lat { margin: 0; color: #555; font-style: italic; font-size: 9.5pt; }
  .print-photos .pp-card .facts { margin: 1mm 0 0; color: #444; font-size: 9.5pt; }
  .print-photos .pp-grid {
    display: grid; grid-template-columns: repeat(4, 1fr);
    gap: 2mm; margin-top: 2mm;
  }
  .print-photos .pp-grid img {
    width: 100%; height: 32mm; object-fit: cover;
    border: 0.3pt solid #ccc;
  }
}

/* ---------- Cursor-anchored context menu (right-click popover) ---------- */
.context-menu {
  position: fixed;
  z-index: 1000;
  min-width: 220px;
  padding: 4px 0;
  background: var(--bg-panel, #fff);
  color: var(--fg, #222);
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
  font-size: 13px;
  user-select: none; -webkit-user-select: none;
}
:root[data-theme="dark"] .context-menu {
  border-color: rgba(255, 255, 255, 0.1);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.5);
}
.context-menu-item {
  display: block; width: 100%;
  padding: 6px 14px;
  border: none; background: transparent;
  color: inherit;
  text-align: left;
  font: inherit;
  cursor: pointer;
}
.context-menu-item:hover { background: rgba(0, 0, 0, 0.06); }
:root[data-theme="dark"] .context-menu-item:hover { background: rgba(255, 255, 255, 0.08); }
.context-menu-item.danger { color: #b03030; }
:root[data-theme="dark"] .context-menu-item.danger { color: #ff7a7a; }
.context-menu-sep {
  height: 1px; margin: 4px 0;
  background: var(--border, rgba(0, 0, 0, 0.12));
}
.context-menu-header {
  padding: 4px 14px 2px;
  font-size: .78em; font-weight: 600; letter-spacing: .02em;
  color: var(--muted, #777); text-transform: uppercase;
}
/* An indented item belongs to the header above it; the ✓ column keeps the
   labels aligned whether or not an entry is the current one. */
.context-menu-item.indent { padding-left: 26px; position: relative; }
.context-menu-item.checked::before {
  content: "✓";
  position: absolute; left: 10px;
  color: var(--accent);
}

/* ---------- Catalog browse (full-pane modal) ---------- */
.browse-dialog {
  width: min(1100px, 94vw); max-width: 94vw; height: min(80vh, 860px);
  padding: 0; border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden;
  background: var(--bg-panel); color: var(--fg);
}
/* Gate the flex layout on [open] — an unconditional `display` on a <dialog>
   beats the UA `dialog:not([open]) { display: none }` (author > UA), which
   would force the dialog permanently visible and unclosable. */
.browse-dialog[open] { display: flex; flex-direction: column; }
/* Resize grips (enableDialogResize) — thin zones just inside the dialog edges
   so `overflow: hidden` doesn't clip them; the side grips start below the
   header so they don't fight the search/sort/close controls. */
.dlg-rsz { position: absolute; z-index: 8; touch-action: none; }
.dlg-rsz-s  { bottom: 0; left: 16px; right: 16px; height: 8px; cursor: ns-resize; }
.dlg-rsz-e  { right: 0; top: 56px; bottom: 16px; width: 7px;  cursor: ew-resize; }
.dlg-rsz-w  { left: 0;  top: 56px; bottom: 16px; width: 7px;  cursor: ew-resize; }
.dlg-rsz-se { right: 0; bottom: 0; width: 16px; height: 16px; cursor: nwse-resize; }
.dlg-rsz-sw { left: 0;  bottom: 0; width: 16px; height: 16px; cursor: nesw-resize; }
/* Faint diagonal hint on the bottom-right grip so resize is discoverable. */
.dlg-rsz-se::after {
  content: ""; position: absolute; right: 3px; bottom: 3px; width: 7px; height: 7px;
  border-right: 2px solid var(--fg-muted); border-bottom: 2px solid var(--fg-muted);
  opacity: .55;
}
.browse-head {
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
  background: var(--bg-muted);
}
.browse-head h3 {
  margin: 0; font-size: 1.05em; color: var(--primary); white-space: nowrap;
  cursor: move; user-select: none;   /* the drag handle (enableDialogDrag) */
}
.browse-dialog .browse-search {
  flex: 1; min-width: 120px; display: block; width: auto; margin: 0;
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px;
  font: inherit; background: var(--bg-panel); color: var(--fg);
}
.browse-dialog .browse-sort {
  display: flex; align-items: center; gap: 6px; margin: 0;
  font-size: .85em; color: var(--fg-muted); white-space: nowrap;
}
.browse-dialog .browse-sort select {
  display: inline-block; width: auto; margin: 0; padding: 4px 8px;
}
.browse-close {
  border: none; background: transparent; color: var(--fg-muted);
  font-size: 1.4em; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.browse-close:hover { color: var(--fg); }
.browse-body { flex: 1; display: flex; min-height: 0; }
.browse-filters {
  width: 210px; flex: none; overflow-y: auto;
  padding: 12px 14px; border-right: 1px solid var(--border); background: var(--bg-soft);
}
/* ---- Material Symbols, inlined as SVG (src/icons.js). They take the text
   color of whatever they sit in, so one rule covers every host. ---- */
.mi {
  width: 1.15em; height: 1.15em; flex: none;
  fill: currentColor; vertical-align: -0.2em;
}
/* Herzwurzler: the fork points DOWN into the soil. */
.mi-flip { transform: rotate(180deg); }

/* ---- filter rail (src/facet-rail.js) — shared by the browse modal and the
   Katalog sidebar, so everything below is host-agnostic. ---- */
.fr-group { margin: 0 0 10px; }
.fr-group > summary {
  cursor: pointer; list-style: none; margin: 0 0 4px;
  display: flex; align-items: center; gap: 5px;
}
.fr-group > summary::-webkit-details-marker { display: none; }
.fr-group > summary::before {
  content: "▸"; font-size: .8em; color: var(--fg-muted);
  transition: transform .12s ease;
}
.fr-group[open] > summary::before { transform: rotate(90deg); }
.fr-group h4 { margin: 0; font-size: .82em; color: var(--primary); text-transform: uppercase; letter-spacing: .03em; }
/* Option row: label left, live hit count right. An option that would yield
   nothing is dimmed (but stays clickable when it is the active one). */
.fr-group label.fr-opt {
  display: flex; align-items: center; gap: 5px; margin: 3px 0;
  font-size: .88em; color: var(--fg); cursor: pointer;
}
.fr-group label.fr-opt .mi { color: var(--fg-muted); }
.fr-group label.fr-opt input { width: auto; margin: 0; flex: none; }
.fr-opt-n {
  margin-left: auto; font-size: .85em; color: var(--fg-muted);
  font-variant-numeric: tabular-nums;
}
.fr-opt.empty { opacity: .45; }
.fr-month.empty { opacity: .4; }
.fr-months { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; }
.fr-month {
  padding: 4px 0; font-size: .82em; cursor: pointer;
  border: 1px solid var(--border); border-radius: 5px;
  background: var(--bg-panel); color: var(--fg);
}
.fr-month.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.fr-reset { margin-top: 4px; padding: 5px 10px; font-size: .85em; cursor: pointer; }

/* ---- Wuchsgröße: two two-handle sliders, drawn along the axis they measure
   — Höhe upright, Breite flat. Both are the same markup; the vertical one is
   rotated, so the fill math (--fr-lo / --fr-hi) is shared. ---- */
/* Stacked by default: side by side, each slider gets half the rail, and at
   the ~180 px a filter rail normally has that is not enough for the Breite
   scale — its labels run into each other and the readout truncates. Above
   the threshold below (measured, not guessed) there IS room, and then the
   two go side by side and save the vertical space. */
.fr-group { container-type: inline-size; }
.fr-sizes { display: flex; flex-direction: column; gap: 10px; }
@container (min-width: 300px) {
  .fr-sizes { flex-direction: row; align-items: flex-start; gap: 14px; }
  .fr-sizes .fr-size-y { flex: none; }
  .fr-sizes .fr-size-x { flex: 1; min-width: 0; }
}
.fr-size { min-width: 0; }
.fr-size-head {
  display: flex; align-items: center; gap: 4px;
  font-size: .82em; white-space: nowrap;
}
.fr-size-head .mi { color: var(--fg-muted); }
.fr-size-label { color: var(--fg-muted); }
.fr-size-val { margin-left: auto; color: var(--fg); font-variant-numeric: tabular-nums; }
/* The scale: one mark per stop, a number on the named ones. Marks are placed
   with the same inset as the handles, so a handle always sits on its mark. */
.fr-scale { position: relative; }
.fr-tick { position: absolute; background: var(--border); }
.fr-tick > i {
  position: absolute; font-style: normal;
  font-size: .68em; color: var(--fg-muted); white-space: nowrap;
}
/* Breite: scale under the track, labels centered on their mark. */
.fr-size-x .fr-size-body { display: block; }
.fr-size-x .fr-scale { height: 15px; margin-top: -2px; }
.fr-size-x .fr-tick { left: var(--p); top: 0; width: 1px; height: 3px; }
.fr-size-x .fr-tick.named { height: 5px; }
.fr-size-x .fr-tick > i { top: 6px; left: 50%; transform: translateX(-50%); }
/* …except the two outer ones, which would hang off the rail. */
.fr-size-x .fr-tick:first-child > i { left: 0; transform: none; }
.fr-size-x .fr-tick:last-child > i { left: auto; right: 0; transform: none; }
/* The container owns the pointer gesture (see facet-rail.js) — the inputs
   inside are keyboard controls and the visible thumbs. */
.fr-slider { position: relative; height: 22px; margin: 6px 0 2px; touch-action: none; }
.fr-track, .fr-fill {
  position: absolute; top: 50%; transform: translateY(-50%);
  height: 4px; border-radius: 2px; pointer-events: none;
}
.fr-track { left: 0; right: 0; background: var(--bg-muted); border: 1px solid var(--border); }
.fr-fill { left: var(--fr-lo); right: calc(100% - var(--fr-hi)); background: var(--primary); }
/* Both handles stack on one track. They are transparent overlays that only
   DRAW the thumbs (and take keyboard focus) — the drag itself is handled on
   the container, since two stacked inputs can never both be grabbable. */
/* The input is exactly as thick as its thumb and centred in the container:
   WebKit lays the thumb out at the START of the runnable track's cross axis,
   not centred on it, so a taller track leaves the thumb sitting above the
   line (and left of it once the slider is turned upright). Matching the two
   heights removes the offset instead of correcting it with a margin. */
.fr-handle {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 100%; height: 14px; margin: 0;
  -webkit-appearance: none; appearance: none;
  background: transparent; pointer-events: none;
}
.fr-handle::-webkit-slider-runnable-track { background: transparent; height: 14px; }
.fr-handle::-moz-range-track { background: transparent; height: 14px; }
/* SOLID, not a ring: two handles pushed together overlap, and two rings
   overlapping read as a figure-eight blob while two discs merge into a
   clean capsule. The outer ring is the panel color, so they stay apart
   against the filled bar. */
.fr-handle::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; pointer-events: auto;
  box-sizing: border-box; width: 14px; height: 14px; border-radius: 50%; cursor: grab;
  background: var(--primary); border: 2px solid var(--bg-panel);
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}
.fr-handle::-moz-range-thumb {
  pointer-events: auto;
  box-sizing: border-box; width: 14px; height: 14px; border-radius: 50%; cursor: grab;
  background: var(--primary); border: 2px solid var(--bg-panel);
  box-shadow: 0 1px 2px rgba(0,0,0,.3);
}
.fr-handle:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--primary); outline-offset: 2px; }
.fr-handle:focus-visible::-moz-range-thumb { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Höhe stands up: the slider runs along the axis it measures. `direction: rtl`
   puts 0 m at the BOTTOM, where the ground is. */
.fr-size-y .fr-slider {
  height: 108px; width: 22px; margin: 2px 0;
  writing-mode: vertical-lr; direction: rtl;
}
.fr-size-y .fr-track, .fr-size-y .fr-fill {
  top: auto; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 4px; height: auto;
}
.fr-size-y .fr-track { top: 0; }
.fr-size-y .fr-fill { bottom: var(--fr-lo); top: calc(100% - var(--fr-hi)); }
/* No `-webkit-appearance: slider-vertical` here: it would undo the base
   `appearance: none` and bring back Chrome's native blue slider, hiding the
   custom thumbs. `writing-mode` alone gives the vertical axis. */
.fr-size-y .fr-handle {
  width: 14px; height: 100%;
  top: 0; left: 50%; transform: translateX(-50%);
  writing-mode: vertical-lr; direction: rtl;
}
.fr-size-y .fr-handle::-webkit-slider-runnable-track { width: 14px; height: 100%; }
.fr-size-y .fr-handle::-moz-range-track { width: 14px; height: 100%; }
/* Höhe reads as an upright axis: track on the left, scale climbing beside it
   — 0 m on the ground, the open-ended stop at the top. */
.fr-size-y .fr-size-body { display: flex; align-items: stretch; }
.fr-size-y .fr-scale { flex: 1; margin-left: 2px; }
.fr-size-y .fr-tick { bottom: var(--p); left: 0; height: 1px; width: 3px; }
.fr-size-y .fr-tick.named { width: 5px; }
.fr-size-y .fr-tick > i { left: 7px; top: 50%; transform: translateY(-50%); }

/* Active-filter chips (browse: above the grid; sidebar: under the filters). */
.fr-chips {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: center;
  padding: 8px 14px 0;
}
.fr-chips-label { font-size: .82em; color: var(--fg-muted); }
.fr-chip {
  padding: 2px 8px; font-size: .82em; cursor: pointer;
  border: 1px solid var(--primary); border-radius: 11px;
  background: var(--bg-soft); color: var(--fg);
}
.fr-chip:hover { background: var(--bg-muted); }
.browse-results { flex: 1; overflow-y: auto; padding: 12px 16px; }
.browse-count { font-size: .82em; color: var(--fg-muted); margin-bottom: 10px; }
.browse-grid {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px;
}
.browse-empty { grid-column: 1 / -1; padding: 30px; text-align: center; color: var(--fg-muted); }
.browse-card {
  position: relative;              /* anchors the ★ favorite toggle */
  display: flex; flex-direction: column;
  border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
  background: var(--bg-panel);
}
.bc-fav {
  position: absolute; right: 6px; top: 6px; z-index: 2;
  width: 26px; height: 26px; padding: 0;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(0, 0, 0, .35); color: #fff;
  font-size: 15px; line-height: 26px; text-align: center;
}
.bc-fav:hover { background: rgba(0, 0, 0, .55); }
.bc-fav.is-fav { color: #ffc83d; }
.bc-photo { width: 100%; height: 110px; object-fit: cover; cursor: zoom-in; display: block; }
.bc-photo-empty {
  width: 100%; height: 110px; display: flex; align-items: center; justify-content: center;
  font-size: 2em; background: var(--bg-muted);
}
.bc-body { padding: 8px 10px; flex: 1; }
.bc-name { font-weight: 600; font-size: .95em; }
.bc-lat { font-style: italic; font-size: .8em; color: var(--fg-muted); }
.bc-badges { margin: 5px 0 4px; display: flex; flex-wrap: wrap; gap: 4px; }
.bc-meta { font-size: .8em; color: var(--fg-muted); margin-bottom: 6px; }
.badge.hedge { background: #d5e6d0; color: #35602e; }
.bc-compare {
  display: flex; align-items: center; gap: 6px; margin: 0;
  padding: 6px 10px; border-top: 1px solid var(--border);
  font-size: .82em; color: var(--fg-muted); cursor: pointer;
}
.bc-compare input { width: auto; margin: 0; }
.browse-comparebar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 16px; border-top: 1px solid var(--border); background: var(--bg-muted);
}
.bc-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.bc-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 3px 4px 3px 9px; font-size: .82em;
  background: var(--bg-panel); border: 1px solid var(--border); border-radius: 12px;
}
.bc-chip-x { border: none; background: transparent; color: var(--fg-muted); cursor: pointer; font-size: 1.1em; line-height: 1; padding: 0 2px; }
.bc-chip-x:hover { color: var(--fg); }
.bc-bar-actions { display: flex; gap: 8px; white-space: nowrap; }
.bc-clear, .bc-show { padding: 5px 12px; font-size: .85em; cursor: pointer; }
.bc-show[disabled] { opacity: .5; cursor: not-allowed; }
.bc-show-warn { color: #b03030; }

/* Compare table (side-by-side facts) */
.compare-dialog {
  width: min(900px, 92vw); max-width: 92vw; max-height: 88vh;
  padding: 0; border: 1px solid var(--border); border-radius: 10px;
  background: var(--bg-panel); color: var(--fg); overflow: hidden;
}
.compare-dialog[open] { display: flex; flex-direction: column; }
.compare-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--bg-muted);
}
.compare-head h3 { margin: 0; font-size: 1.05em; color: var(--primary); }
.compare-body { overflow: auto; padding: 12px 16px; }
.compare-table { border-collapse: collapse; width: 100%; }
.compare-table th, .compare-table td {
  border: 1px solid var(--border); padding: 8px 10px; text-align: left;
  vertical-align: top; font-size: .88em;
}
.cmp-rowhead { width: 130px; background: var(--bg-soft); font-weight: 600; color: var(--fg-muted); white-space: nowrap; }
.cmp-head { display: flex; flex-direction: column; align-items: center; gap: 3px; text-align: center; }
.cmp-head img { width: 90px; height: 60px; object-fit: cover; border-radius: 5px; }
.cmp-head .bc-photo-empty { width: 90px; height: 60px; border-radius: 5px; font-size: 1.5em; }
.cmp-name { font-weight: 600; }
.cmp-lat { font-style: italic; font-size: .82em; color: var(--fg-muted); }

@media (max-width: 640px) {
  .browse-body { flex-direction: column; }
  .browse-filters { width: auto; border-right: none; border-bottom: 1px solid var(--border); }
}

/* ---------- plant detail (Steckbrief + Foto-Slideshow) ---------- */
.detail-dialog {
  width: min(1000px, 94vw); max-width: 94vw; height: min(78vh, 780px);
  padding: 0; border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden; background: var(--bg-panel); color: var(--fg);
}
/* [open]-gated layout — see the .browse-dialog note above. */
.detail-dialog[open] { display: flex; flex-direction: column; }
.detail-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 12px 16px; border-bottom: 1px solid var(--border); background: var(--bg-muted);
}
.detail-head h3 {
  margin: 0; font-size: 1.05em; color: var(--primary); white-space: nowrap;
  cursor: move; user-select: none;   /* the drag handle (enableDialogDrag) */
}
.detail-lat { font-style: italic; font-size: .88em; color: var(--fg-muted); }
.detail-badges { display: flex; gap: 4px; flex-wrap: wrap; }
.detail-spacer { flex: 1; }
.detail-fav {
  border: none; background: transparent; cursor: pointer;
  font-size: 1.2em; line-height: 1; color: var(--fg-muted); padding: 2px 4px;
}
.detail-fav.is-fav { color: #e0a100; }
.detail-compare {
  padding: 4px 10px; font-size: .85em; cursor: pointer;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-panel); color: var(--fg);
}
.detail-compare[disabled] { opacity: .55; cursor: default; }
/* Two panes side by side; auto-fit collapses to one column when the user
   resizes the dialog narrow (it is drag-resizable like the browser). */
.detail-body {
  flex: 1; min-height: 0; overflow: auto;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 16px; padding: 14px 16px;
}
.detail-slideshow { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ds-stage {
  position: relative; display: flex; align-items: center; justify-content: center;
  aspect-ratio: 4 / 3; border-radius: 8px; overflow: hidden;
  background: var(--bg-image-fallback);
}
.ds-img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }
.ds-empty { flex-direction: column; gap: 6px; color: var(--fg-muted); font-size: 2em; }
.ds-empty-txt { font-size: .42em; }
.ds-nav {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 34px; height: 44px; cursor: pointer;
  border: none; border-radius: 6px; font-size: 1.5em; line-height: 1;
  background: rgba(0, 0, 0, .42); color: #fff;
}
.ds-nav:hover { background: rgba(0, 0, 0, .62); }
.ds-prev { left: 6px; }
.ds-next { right: 6px; }
.ds-counter {
  position: absolute; right: 8px; bottom: 8px;
  padding: 2px 7px; border-radius: 10px; font-size: .78em;
  background: rgba(0, 0, 0, .5); color: #fff;
}
.ds-thumbs { display: flex; gap: 6px; flex-wrap: wrap; }
.ds-thumb {
  padding: 0; border: 2px solid transparent; border-radius: 5px;
  background: none; cursor: pointer; line-height: 0;
}
.ds-thumb.active { border-color: var(--primary); }
.ds-thumb img {
  width: 58px; height: 42px; object-fit: cover; border-radius: 3px;
  background: var(--bg-image-fallback);
}
.detail-info { min-width: 0; }
.detail-info .steckbrief { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.detail-note { font-size: .88em; line-height: 1.45; margin: 10px 0 0; color: var(--fg); }
.detail-info h4 {
  margin: 14px 0 4px; font-size: .82em; color: var(--primary);
  text-transform: uppercase; letter-spacing: .03em;
}

/* ---------- 3D view (scene3d) ---------- */
.scene3d-dialog {
  width: min(1200px, 94vw); max-width: 94vw; height: min(84vh, 900px);
  padding: 0; border: 1px solid var(--border); border-radius: 10px;
  overflow: hidden;
  background: var(--bg-panel); color: var(--fg);
}
/* display gated on [open] — see Known quirks (author CSS beats the UA's
   dialog:not([open]) { display: none }). */
.scene3d-dialog[open] { display: flex; flex-direction: column; }
.scene3d-head {
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px;
  padding: 8px 12px; border-bottom: 1px solid var(--border);
  background: var(--bg-muted);
  cursor: move; user-select: none;   /* the header is the dialog's drag handle */
}
.scene3d-head h3 { margin: 0; font-size: 1.05em; color: var(--primary); white-space: nowrap; }
.s3d-viewpoint-label {
  display: flex; align-items: center; gap: 6px; margin: 0;
  font-size: .85em; color: var(--fg-muted); white-space: nowrap;
}
.s3d-viewpoint-label select {
  display: inline-block; width: auto; margin: 0; padding: 4px 8px;
}
/* Compact gesture reference: the full cheat sheet lives in the tooltip
   (and in the ⌨ Kürzel card) — the header must not grow with every mode. */
.s3d-hint {
  margin-left: auto; flex: none;
  font-size: .78em; color: var(--fg-muted); white-space: nowrap;
  border: 1px solid var(--border); border-radius: 6px;
  padding: 3px 8px; cursor: help;
}
.s3d-wrap { flex: 1; min-height: 0; position: relative; }
.s3d-wrap canvas { display: block; }
/* 🗂️ Ebenen popover inside the 3D dialog: visibility + opacity per plan
   layer while the modal dialog blocks the floating 2D layer panel. */
.s3d-layers-pop {
  position: absolute;
  top: 46px;
  right: 12px;
  z-index: 6;
  min-width: 240px;
  max-width: 320px;
  max-height: 50%;
  overflow-y: auto;
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg-panel, #fff);
  box-shadow: 0 6px 22px rgba(0, 0, 0, .25);
  font-size: .88em;
}
.s3d-layer-row {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 3px 0;
  cursor: pointer;
}
.s3d-layer-row .s3d-layer-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.s3d-layer-row input[type="range"] { flex: 0 0 90px; width: 90px; }
/* Hover tooltip for plants / structures (raycast hit under the cursor). */
.s3d-tip {
  position: absolute; z-index: 5; pointer-events: none;
  max-width: 320px;
  padding: 6px 11px; border-radius: 8px;
  background: rgba(18, 22, 18, .92); color: #f2f4f0;
  font-size: .84em; line-height: 1.4;
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow: 0 3px 12px rgba(0, 0, 0, .35);
}
/* Height readout at the top of the 3D measuring ruler (wheel-adjust). */
.s3d-ruler-label {
  position: absolute; z-index: 6; pointer-events: none;
  padding: 2px 7px; border-radius: 6px;
  background: rgba(18, 22, 18, .9); color: #fff;
  font-size: .82em; font-weight: 700; font-variant-numeric: tabular-nums;
  border: 1px solid rgba(255, 255, 255, .18);
  box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
  white-space: nowrap;
}
.s3d-tip-title {
  display: flex; align-items: center; gap: 6px;
  font-weight: 600;
}
.s3d-tip-dot {
  width: 10px; height: 10px; border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, .5);
  flex: none;
}
.s3d-tip-chip {
  padding: 0 6px; border-radius: 8px;
  background: rgba(255, 255, 255, .18);
  font-size: .78em; font-weight: 500;
}
.s3d-tip-meta { color: #c4ccc2; font-size: .92em; }
/* Standpunkt tauschen + Umsehen + Bestand toggle in the 3D header
   (the 👀 Umsehen button reuses the .s3d-swap chrome incl. .active). */
.s3d-swap {
  padding: 3px 9px; font-size: 1em; line-height: 1; cursor: pointer;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--bg-panel); color: var(--fg);
}
.s3d-swap:disabled { opacity: .4; cursor: default; }
.s3d-swap.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.scene3d-head .s3d-bestand-label {
  display: flex; align-items: center; gap: 5px; margin: 0;
  font-size: .85em; color: var(--fg-muted); white-space: nowrap; cursor: pointer;
}
.scene3d-head .s3d-bestand-label input { width: auto; margin: 0; }
.scene3d-head .s3d-compare-label {
  display: flex; align-items: center; gap: 5px; margin: 0;
  font-size: .85em; color: var(--fg-muted); white-space: nowrap;
}
.scene3d-head .s3d-compare-label select {
  display: inline-block; width: auto; margin: 0; padding: 2px 6px; font-size: .95em;
}
.s3d-compare-count { font-variant-numeric: tabular-nums; opacity: .85; }
.scene3d-head .s3d-sun-label {
  display: flex; align-items: center; gap: 5px; margin: 0;
  font-size: .85em; color: var(--fg-muted); white-space: nowrap;
}
.scene3d-head .s3d-sun-label input[type="range"] { width: 90px; margin: 0; }
.scene3d-head .s3d-sun-label #s3d-sun-val {
  min-width: 3.2em; font-variant-numeric: tabular-nums;
}

/* ---------- Responsive: stack on narrow screens ---------- */
@media (max-width: 900px) {
  .app-main {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr auto;
  }
  .sidebar { max-height: 30vh; }
  .splitter { display: none; }     /* stacked layout has no horizontal panes */
}

/* ---- Docked 3D view ------------------------------------------------- */
/* The dock host fills the plan pane above the 2D plan; the reparented
   .scene3d-head + #s3d-wrap lay out exactly like inside the dialog.
   Floating panels (Heckenliste etc., position: fixed) stay reachable. */
.s3d-dock {
  position: absolute; inset: 0; z-index: 40;
  display: flex; flex-direction: column;
  background: var(--bg-panel);
}
/* Docked, the header is not a drag handle. */
.s3d-dock .scene3d-head { cursor: default; }
