/*
  Instrument Serif, aus dem eigenen Haus ausgeliefert.

  Vorher hing hier ein Stylesheet von `fonts.googleapis.com`, und damit ging bei
  jedem Seitenaufruf die IP des Kunden an Google. Die Dateien liegen jetzt unter
  `public/vendor/fonts/` - Herkunft und Version stehen in
  `public/vendor/README.md`, die Lizenz daneben in `OFL.txt`.

  Die vier Regeln sind dieselben, die Google ausgeliefert hat: normal und
  kursiv, je einmal `latin` und einmal `latin-ext`. `latin-ext` ist kein Beiwerk
  - deutsche Ortsnamen mit Umlaut fallen sonst auf die Ersatzschrift zurueck.
  `font-display: swap` entspricht dem `&display=swap` von damals: erst der Text
  in der Ersatzschrift, dann der Tausch, nie eine leere Ueberschrift.
*/
@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/instrument-serif-latin-400-normal.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/instrument-serif-latin-ext-400-normal.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/instrument-serif-latin-400-italic.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: "Instrument Serif";
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url("vendor/fonts/instrument-serif-latin-ext-400-italic.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/*
  WARUM HIER EINE KLASSE STEHT UND NICHT `:root`, `*` UND `body`.

  Seit dem 2026-08-12 laeuft dieselbe Oberflaeche an zwei Orten: auf unserer
  eigenen Seite und als Section in einem fremden Shopify-Theme. Auf unserer
  Seite ist der Picker die ganze Seite; im Theme ist er ein Abschnitt zwischen
  Kopf- und Fusszeile, die jemand anderem gehoeren.

  Seitenweite Regeln greifen dort auf die ganze Seite durch: Ein `body {
  background }` faerbt den Shop um, ein `* { box-sizing }` ordnet fremde
  Bausteine neu. Deshalb haengt alles, was hier frueher global stand, jetzt
  unter `.picker` - und diese Klasse traegt bei uns das `<body>` und im Theme
  der Rahmen der Section. Die Regeln darunter (`.layout`, `.panel`, ...) waren
  schon immer Klassen und bleiben, wie sie sind.
*/
.picker {
  --sage: #c3cca6;
  --sage-deep: #93a06c;
  --ink: #16170f;
  --muted: #6d6f60;
  --line: #dedbcf;
  --paper: #faf8f2;
  --card: #ffffff;
  --danger: #a4402c;

  /*
    Wie hoch die Arbeitsflaeche wird.

    Bei uns der Bildschirm ohne die Kopfzeile (57 px). Im Theme setzt die
    Section eine eigene Hoehe - dort waere ein voller Bildschirm falsch, weil
    darueber und darunter der Shop steht.
  */
  --picker-hoehe: calc(100vh - 57px);

  margin: 0;
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--ink);
  background: var(--paper);
}

.picker *,
.picker::before,
.picker::after {
  box-sizing: border-box;
}

/* Our `display:` rules below would otherwise beat the `hidden` attribute. */
.picker [hidden] {
  display: none !important;
}

.topbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1.25rem;
  border-bottom: 1px solid var(--line);
  background: var(--card);
}

.topbar h1 {
  font-family: "Instrument Serif", Georgia, serif;
  font-weight: 400;
  font-size: 1.5rem;
  margin: 0;
}

.quota {
  font-size: 0.85rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.quota.low {
  color: var(--danger);
}

.layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 560px;
  gap: 1px;
  background: var(--line);
  height: var(--picker-hoehe);
}

.map-pane {
  position: relative;
  background: var(--card);
  min-width: 0;
}

#map {
  position: absolute;
  inset: 0;
}

.search {
  position: absolute;
  z-index: 500;
  top: 12px;
  left: 12px;
  right: 12px;
  max-width: 460px;
  display: flex;
  gap: 6px;
}

.search input {
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--card);
  font: inherit;
  font-size: 0.9rem;
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.08);
}

.search button {
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--sage-deep);
  border-radius: 8px;
  background: var(--sage);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}

/* --- Form der Auswahl ----------------------------------------------------- */

/*
 * Rechts oben, nicht links: links sitzen der Zoom und die Ebenen. Und auf der
 * Karte, nicht in der Seitenleiste - die Form ist das, was man auf der Karte
 * anfasst.
 */
.shapes {
  position: absolute;
  z-index: 500;
  top: 62px;
  right: 12px;
  margin: 0;
  padding: 0.5rem 0.7rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgb(255 255 255 / 0.94);
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.1);
  font-size: 0.8rem;
  max-width: 200px;
}

.shapes legend {
  padding: 0 0.2rem;
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.shape-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

.shape-buttons button {
  padding: 0.3rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
  color: var(--ink);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
}

.shape-buttons button:hover {
  border-color: var(--sage-deep);
}

.shape-buttons button[aria-pressed='true'] {
  border-color: var(--sage-deep);
  background: var(--sage);
  font-weight: 600;
}

/*
  Die Spalte der Bedienfelder links auf der Karte.

  Sie trägt die Position, die Felder darin tragen nur noch ihr Aussehen. Vorher
  war jedes Feld selbst absolut gesetzt, und solange es eines war, ging das
  auch. Beim zweiten müsste man seine Höhe kennen - und die hängt an einem
  Hinweistext, der mal leer ist und mal drei Zeilen hat.

  62px lassen Leaflets Zoomknöpfen darüber ihren Platz.
*/
.map-panels {
  position: absolute;
  z-index: 500;
  top: 62px;
  left: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.layers {
  margin: 0;
  padding: 0.5rem 0.7rem 0.55rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: rgb(255 255 255 / 0.94);
  box-shadow: 0 2px 10px rgb(0 0 0 / 0.1);
  font-size: 0.8rem;
  max-width: 190px;
}

.layers legend {
  padding: 0 0.2rem;
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.layers label {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  cursor: pointer;
}

.layers input {
  accent-color: var(--sage-deep);
}

/* Das Auswahlfeld der Routenart. Volle Breite des Felds, damit „Mountainbike"
   nicht schmaler steht als „Reiten" und die Spalte bei jedem Wechsel zuckt. */
.layers select {
  width: 100%;
  margin-top: 0.15rem;
  font: inherit;
  color: inherit;
  padding: 0.2rem 0.25rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  background: #fff;
}

.layers-note {
  margin: 0.35rem 0 0;
  font-size: 0.7rem;
  line-height: 1.35;
  color: var(--muted);
}

.layers-note:empty {
  display: none;
}

.layers-note.error {
  color: var(--danger);
}

/* Leaflet wraps divIcon markers in a styled box by default. */
.feature-pin {
  background: none;
  border: 0;
}

.feature-tip {
  font-size: 0.78rem;
  line-height: 1.35;
}

.feature-tip .muted {
  color: var(--muted);
}

.results {
  position: absolute;
  z-index: 500;
  top: 58px;
  left: 12px;
  max-width: 460px;
  right: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 6px 20px rgb(0 0 0 / 0.12);
  overflow: hidden;
}

.results li button {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.6rem 0.75rem;
  border: 0;
  border-bottom: 1px solid var(--line);
  background: none;
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

.results li:last-child button {
  border-bottom: 0;
}

.results li button:hover {
  background: var(--paper);
}

.panel {
  background: var(--card);
  padding: 1.1rem;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}

.field label {
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.field output {
  font-variant-numeric: tabular-nums;
  font-size: 0.9rem;
}

.field select {
  padding: 0.55rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
  background: var(--card);
}

.field input[type="range"] {
  accent-color: var(--sage-deep);
  width: 100%;
}

.hint {
  margin: 0;
  font-size: 0.78rem;
  color: var(--muted);
}

.primary {
  padding: 0.75rem 1rem;
  border: 1px solid var(--sage-deep);
  border-radius: 10px;
  background: var(--sage);
  color: var(--ink);
  font: inherit;
  font-size: 0.95rem;
  cursor: pointer;
}

.primary:disabled,
.secondary:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.secondary {
  padding: 0.55rem 0.9rem;
  border: 1px solid var(--line, #d8d5cd);
  border-radius: 10px;
  background: transparent;
  color: var(--ink);
  font: inherit;
  font-size: 0.85rem;
  cursor: pointer;
}

/*
 * Das Betreiberpanel.
 *
 * Optisch bewusst abgesetzt und nicht huebsch: es soll auf einen Blick klar
 * sein, dass hier nichts steht, was ein Kunde je sehen darf. Sichtbar nur mit
 * ?admin=1 in der Adresse.
 */
.admin {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-top: 0.5rem;
  padding: 0.9rem;
  border: 1px dashed var(--sage-deep, #93a06c);
  border-radius: 12px;
  background: rgba(195, 204, 166, 0.12);
}

.admin h2 {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.admin code {
  font-size: 0.75rem;
  padding: 0 0.2em;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.06);
}

.status {
  margin: 0;
  min-height: 1.2em;
  font-size: 0.82rem;
  color: var(--muted);
}

.status.error {
  color: var(--danger);
}

.preview {
  position: relative;
  /* Die Vorschau ist das Produkt, nicht eine Abbildung daneben - sie bekommt
     die Flaeche, die dazu passt. */
  aspect-ratio: 4 / 3;
  flex: none; /* the panel is a flex column - don't let it squash the viewport */
  min-height: 360px;
  border: 1px solid var(--line);
  border-radius: 10px;
  overflow: hidden;
  background: linear-gradient(180deg, #f6f4ec 0%, #e9e6da 100%);
}

.view3d {
  position: absolute;
  inset: 0;
}

.view3d canvas {
  display: block;
  width: 100%;
  height: 100%;
  cursor: grab;
}

.view3d canvas:active {
  cursor: grabbing;
}

/*
  Die Auflage ueber der Vorschau.

  Zwei Zustaende, ein Bauteil. Ohne Vorschau deckt sie ab, weil darunter nichts
  steht. Bei einer ueberholten Vorschau *nicht*: dann wird das alte Gelaende
  ausgegraut und bleibt sichtbar, denn der Kunde vergleicht es gerade mit dem,
  was er auf der Karte aufzieht. Ein Schleier, der es verdeckt, wuerde ihm
  genau die Information nehmen, wegen der er hinsieht.
*/
.preview-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 1.25rem;
  text-align: center;
}

.preview-overlay.is-empty {
  background: var(--paper);
}

.preview-overlay.is-stale {
  background: rgb(250 248 242 / 0.35);
  backdrop-filter: blur(1px);
}

.preview-overlay-text {
  margin: 0;
  font-size: 0.85rem;
  color: var(--muted);
  max-width: 22rem;
}

.preview-overlay.is-stale .preview-overlay-text {
  /* Auf dem ausgegrauten Gelaende braucht der Satz einen eigenen Grund, sonst
     liest er sich gegen jedes Relief anders. */
  padding: 0.35rem 0.6rem;
  border-radius: 6px;
  background: rgb(255 255 255 / 0.88);
  color: var(--ink);
  font-weight: 600;
  box-shadow: 0 1px 6px rgb(22 23 15 / 0.12);
}

.preview-overlay button {
  width: auto;
  min-width: 12rem;
  box-shadow: 0 1px 8px rgb(22 23 15 / 0.18);
}

/* Das alte Gebiet ausgrauen: erkennbar, aber sichtbar nicht mehr das aktuelle
   Bild. Dieselbe Sprache, die eine ausgegraute Schaltflaeche spricht. */
.preview.is-stale .view3d canvas {
  filter: grayscale(0.85) contrast(0.9) brightness(1.08);
  transition: filter 220ms ease;
}

/*
 * Dasselbe fuer ein Gelaende, das noch nicht fertig ist.
 *
 * Solange die Textur fehlt, faerbt die Hoehenrampe - und die legt breite
 * gruene und braune Baender ueber die Landschaft, die im fertigen Modell
 * nirgends vorkommen. Farbig hingestellt sieht das nach einem Ergebnis aus,
 * und zwar nach einem falschen: der Kunde sieht Streifen, die er so nie
 * gedruckt bekommt. Ausgegraut sagt dasselbe wie eine ausgegraute
 * Schaltflaeche - da tut sich was, aber es ist noch nicht deins.
 *
 * Bewusst dieselbe Behandlung wie bei `is-stale`, nur etwas kraeftiger: es ist
 * zweimal dieselbe Aussage, "das hier gilt noch nicht".
 */
.preview.is-loading .view3d canvas {
  filter: grayscale(0.95) contrast(0.88) brightness(1.06);
  transition: filter 260ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .preview.is-loading .view3d canvas {
    transition: none;
  }
}

.preview-busy {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-size: 0.85rem;
  color: var(--muted);
  background: rgb(250 248 242 / 0.82);
}

.preview-busy-inner {
  width: min(22rem, 80%);
  text-align: center;
}

/*
 * Sobald das Gelände steht, rückt der Ladeschirm an den unteren Rand.
 *
 * Vorher lag er die ganze Zeit über der Vorschau - auch dann noch, wenn längst
 * ein fertiges Gelände darunter stand und nur die Textur fehlte. Zwei Minuten
 * milchige Fläche, obwohl es etwas zu sehen gäbe. `pointer-events: none` gehört
 * dazu: das Gelände lässt sich ab hier drehen, und ein Balken, der das
 * abfängt, wäre schlimmer als gar keiner.
 */
.preview-busy.is-compact {
  inset: auto 0 0 0;
  display: block;
  padding: 0.5rem 0.75rem 0.6rem;
  border-top: 1px solid var(--line);
  background: rgb(250 248 242 / 0.94);
  pointer-events: none;
}

.preview-busy.is-compact .preview-busy-inner {
  width: 100%;
}

.preview-busy.is-compact .preview-busy-step {
  margin-bottom: 0.4rem;
}

.preview-busy-step {
  margin: 0 0 0.6rem;
  color: var(--ink);
}

/* Der Balken selbst. Fest verdrahtet auf eine Breite, damit er beim Wachsen
   nicht springt, und mit einem Übergang, damit ein Sprung von 20 auf 45 Prozent
   als Bewegung lesbar ist statt als Zucken. */
.preview-busy-bar {
  height: 4px;
  border-radius: 999px;
  background: var(--line);
  overflow: hidden;
}

.preview-busy-bar > span {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--sage-deep);
  transition: width 260ms ease;
}

.preview-busy-detail {
  margin: 0.55rem 0 0;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  min-height: 1.1em;
}

@media (prefers-reduced-motion: reduce) {
  .preview-busy-bar > span {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .preview.is-stale .view3d canvas {
    transition: none;
  }
}

.attribution {
  margin: 0;
  font-size: 0.7rem;
  color: var(--muted);
}

.stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
  margin: 0;
}

.stats dt {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.stats dd {
  margin: 0.1rem 0 0;
  font-size: 0.95rem;
  font-variant-numeric: tabular-nums;
}

/*
  Die Bestellliste im Bedienfeld. Sie sieht kein Kunde - nur der Betreiber, und
  der will lesen und drücken, nicht bewundert werden.
*/
.orders {
  list-style: none;
  margin: 0.6rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.4rem;
}

.orders li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--line);
  border-radius: 6px;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* Der Text nimmt den Platz, der Knopf bleibt rechts und behält seine Größe. */
.orders li span {
  flex: 1;
  min-width: 0;
}

.orders li button {
  flex: none;
  margin: 0;
  padding: 0.3rem 0.7rem;
  font-size: 0.8rem;
  width: auto;
}

.selection-box {
  cursor: move;
}

@media (max-width: 900px) {
  .layout {
    grid-template-columns: 1fr;
    grid-template-rows: 55vh auto;
    height: auto;
  }
}

/* --- Auswahlquadrat: Anfasser und Beschriftung --------------------------- */

/*
 * Der Anfasser muss auf dem Handy treffbar sein, ohne als Klotz auf der Karte
 * zu sitzen: der sichtbare Punkt ist klein, die Trefferfläche ist es nicht.
 */
.box-handle {
  background: #fff;
  border: 2px solid #93a06c;
  border-radius: 3px;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.35);
  box-sizing: border-box;
  cursor: nwse-resize;
}

.box-handle.ne,
.box-handle.sw {
  cursor: nesw-resize;
}

/*
 * Die Kantengriffe sehen anders aus, weil sie etwas anderes tun: rund statt
 * eckig, und der Zeiger zeigt die eine Achse, um die es geht.
 */
.box-handle.edge {
  border-radius: 50%;
}

.box-handle.n,
.box-handle.s {
  cursor: ns-resize;
}

.box-handle.e,
.box-handle.w {
  cursor: ew-resize;
}

.box-handle::after {
  content: '';
  position: absolute;
  inset: -10px;
}

.leaflet-tooltip.box-label {
  background: rgb(255 255 255 / 0.88);
  border: none;
  border-radius: 4px;
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.2);
  color: #40482c;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 7px;
  pointer-events: none;
  white-space: nowrap;
}

.leaflet-tooltip.box-label::before {
  display: none;
}

/* --- Filamentfarben ------------------------------------------------------ */

.swatches {
  display: flex;
  flex-direction: column;
  gap: 4px;
  list-style: none;
  margin: 8px 0 0;
  padding: 0;
}

.swatches li {
  align-items: center;
  display: flex;
  font-size: 0.78rem;
  gap: 8px;
}

/*
 * Der Rand ist nicht Zierde: "Ivory White" auf weissem Grund waere sonst kein
 * Feld, sondern eine Luecke.
 */
.swatches .chip {
  border: 1px solid rgb(0 0 0 / 0.18);
  border-radius: 3px;
  flex: none;
  height: 15px;
  width: 15px;
}

.swatches .share {
  color: #7a7a70;
  margin-left: auto;
  font-variant-numeric: tabular-nums;
}

.swatches .fixed {
  color: #7a7a70;
}
