/* ===========================================================
   equirectime — tema "consola de nave" (futurista minimalista)
   =========================================================== */
@font-face {
  font-family: 'Orbitron';
  src: url('../assets/fonts/Orbitron.ttf') format('truetype');
  font-weight: 400 900;
  font-display: swap;
}

:root {
  --bg: #04070e;
  --fg: #d6e6f2;
  --muted: #97a8bf;
  --cyan: #5de7ff;
  --cyan-2: #22b8d9;
  --amber: #ffbe5c;
  --violet: #b98bff;
  --line: rgba(93, 231, 255, 0.18);
  --line-soft: rgba(93, 231, 255, 0.10);
  --field: rgba(93, 231, 255, 0.05);
  --panel: rgba(6, 12, 22, 0.82);
  --glow: 0 0 18px rgba(93, 231, 255, 0.25);
  --mono: ui-monospace, "Cascadia Code", "Segoe UI Mono", "SF Mono", Consolas, "Liberation Mono", monospace;
  --display: 'Orbitron', var(--mono);
  /* Texto legible sobre la foto (mapa/globo), sin caja de fondo detrás. "soft" para
     texto que ya tiene una caja semi-opaca propia (marker-label); "strong" para texto
     flotando directo sobre la imagen sin ninguna caja (reloj). */
  --text-glow-soft: 0 1px 3px rgba(0,0,0,.85);
  --text-glow-strong: 0 1px 4px rgba(0,0,0,.95), 0 0 10px rgba(0,0,0,.85), 0 0 16px rgba(93,231,255,.35);
}
* { box-sizing: border-box; }

html { color-scheme: dark; }
html, body {
  margin: 0; height: 100%; width: 100%;
  overflow: hidden; background: var(--bg); color: var(--fg);
  font-family: var(--mono);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none; touch-action: none;
  user-select: none; -webkit-user-select: none;
}

/* Escena ---------------------------------------------------- */
#scene {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh; height: 100dvh;
  display: block; z-index: 0; cursor: grab; touch-action: none;
}
#scene.grabbing { cursor: grabbing; }
#globe {
  position: fixed; inset: 0;
  width: 100vw; height: 100vh; height: 100dvh;
  display: none; z-index: 0; cursor: grab; touch-action: none; background: #000;
}
#globe:active { cursor: grabbing; }

/* Marcador -------------------------------------------------- */
/* El contenedor se ancla exactamente en (x,y) = la ubicación; el PUNTO se
   centra ahí y la etiqueta cuelga a la derecha sin desplazar el punto. */
.marker {
  position: fixed; left: -9999px; top: -9999px; z-index: 5;
  display: none; pointer-events: none; will-change: left, top;
}
.marker.on { display: block; }
.marker-dot {
  position: absolute; left: 0; top: 0; transform: translate(-50%, -50%);
  width: 11px; height: 11px; border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 0 2px rgba(0,0,0,.5), 0 0 14px 2px var(--cyan);
}
.marker-dot::after {
  content: ""; position: absolute; inset: -7px; border-radius: 50%;
  border: 1px solid var(--cyan); opacity: .7;
  animation: pulse 2.4s ease-out infinite;
}
@keyframes pulse { 0% { transform: scale(.5); opacity: .8; } 100% { transform: scale(2.6); opacity: 0; } }
.marker-label {
  position: absolute; left: 14px; top: 0; transform: translateY(-50%);
  display: flex; flex-direction: column; line-height: 1.2;
  padding: 5px 9px;
  --o: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 0 100%);
  --i: polygon(0 0, calc(100% - 6.4px) 0, 100% 6.4px, 100% 100%, 0 100%);
  --fill: rgba(4, 9, 17, 0.62);
  background: var(--line); clip-path: var(--o);
  font-size: 11px; letter-spacing: .5px; white-space: nowrap;
  text-shadow: var(--text-glow-soft);
}
.marker-label::before { backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); }
.marker-name { font-weight: 600; text-transform: uppercase; color: var(--cyan); font-size: 10px; letter-spacing: 1.5px; }
.marker-sub  { color: var(--fg); font-size: 12px; font-variant-numeric: tabular-nums; }

/* Antípoda: mismo componente, acento ámbar para distinguirlo del marcador principal */
.marker.anti .marker-dot { background: var(--amber); box-shadow: 0 0 0 2px rgba(0,0,0,.5), 0 0 14px 2px var(--amber); }
.marker.anti .marker-dot::after { border-color: var(--amber); }
.marker.anti .marker-name { color: var(--amber); }

/* Marcador 2: acento violeta para distinguirlo del principal (cyan) y de la antípoda (ámbar) */
.marker.m2 .marker-dot { background: var(--violet); box-shadow: 0 0 0 2px rgba(0,0,0,.5), 0 0 14px 2px var(--violet); }
.marker.m2 .marker-dot::after { border-color: var(--violet); }
.marker.m2 .marker-name { color: var(--violet); }

/* Reloj ----------------------------------------------------- */
/* Arrastrable (ver applyClockPos()/wireClockDrag() en app.js): posición libre con
   snap a la posición por defecto o a las 4 esquinas, persistida en S.clockPos. */
.clock {
  position: fixed;
  left: 50%; transform: translateX(-50%);
  bottom: calc(14px + env(safe-area-inset-bottom));
  z-index: 5; display: flex; flex-direction: column; align-items: center; line-height: 1;
  pointer-events: auto; text-align: center; cursor: grab; touch-action: none;
  padding: 0;
  text-shadow: var(--text-glow-strong);
}
.clock.dragging { cursor: grabbing; }
.clock[hidden] { display: none; }
#clock-time {
  font-family: var(--display); font-weight: 700; font-size: 26px;
  letter-spacing: 3px; color: #eafcff; font-variant-numeric: tabular-nums;
}
#clock-date { margin-top: 5px; font-size: 10px; letter-spacing: 2px; text-transform: uppercase; color: var(--muted); text-align: center; }

/* Logo de marca (esquina sup. izq.) -------------------------- */
/* Siempre visible (no se atenúa con body.idle, a diferencia de .controls/.vmenu):
   es una marca persistente, no chrome transitorio. También se hornea al exportar
   (ver exportFlat()/drawExportLogo() en app.js), con el mismo max-width/padding. */
.brand-corner {
  position: fixed; top: 0; left: 0; z-index: 10;
  max-width: 222px; padding: 4px;
  pointer-events: auto; user-select: none; cursor: grab; touch-action: none;
  -webkit-user-drag: none;   /* es un <img>: sin esto, el navegador arranca su propio
                                drag-and-drop nativo (sacar la imagen) en vez de dejar
                                pasar los pointermove que necesita nuestro drag propio */
}
.brand-corner.dragging { cursor: grabbing; }

/* Controles flotantes --------------------------------------- */
.controls {
  position: fixed;
  top: calc(15px + env(safe-area-inset-top));
  right: calc(15px + env(safe-area-inset-right));
  z-index: 10; display: flex; gap: 10px; transition: opacity .5s ease;
}
.fab {
  --o: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 9px 100%, 0 calc(100% - 9px));
  --i: polygon(0 0, calc(100% - 8.4px) 0, 100% 8.4px, 100% 100%, 8.4px 100%, 0 calc(100% - 8.4px));
  --fill: rgba(6, 12, 22, 0.55);
  width: 44px; height: 44px; display: grid; place-items: center;
  background: var(--brd, var(--line)); clip-path: var(--o);
  color: var(--cyan); cursor: pointer;
  transition: transform .2s, color .2s, filter .2s;
}
.fab::before { backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); }
.fab:hover { --brd: var(--cyan); --fill: rgba(93,231,255,0.12); color: #eafcff; filter: drop-shadow(0 0 8px rgba(93,231,255,.45)); }
.fab:active { transform: scale(.93); }
#btn-settings:hover { transform: rotate(30deg); }
body.idle .controls { opacity: 0; pointer-events: none; }
/* Modo "fondo de pantalla" (?wallpaper=1, ver app.js): sin controles visibles nunca,
   no solo tras inactividad — pensado para Lively Wallpaper y motores similares. */
body.wallpaper-mode .controls { display: none !important; }

/* Backdrop + modal ------------------------------------------ */
.backdrop {
  position: fixed; inset: 0; z-index: 20;
  background: rgba(2, 5, 11, 0.55);
  backdrop-filter: blur(10px) saturate(.85); -webkit-backdrop-filter: blur(10px) saturate(.85);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .3s ease, visibility 0s linear .3s;
}
.backdrop.open { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .3s ease, visibility 0s; }

.modal {
  position: fixed; left: 50%; top: 50%; z-index: 21;
  width: min(440px, 94vw); max-height: 88vh; display: flex; flex-direction: column;
  /* El resto de la página bloquea pan/zoom táctil a propósito (touch-action:none en
     html/body, look de wallpaper) — los diálogos son la excepción: tienen controles e
     info real, así que un usuario que necesite pinch-zoom para leerlos/tocarlos con
     precisión debe poder hacerlo (ver también el <meta viewport> sin user-scalable=no). */
  touch-action: auto;
  transform: translate(-50%, -46%) scale(.97);
  opacity: 0; visibility: hidden; pointer-events: none;
  background: var(--panel);
  border: 1px solid var(--line);
  box-shadow: 0 30px 80px rgba(0,0,0,.6), inset 0 0 40px rgba(93,231,255,.04);
  backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2);
  transition: transform .28s cubic-bezier(.2,.7,.3,1), opacity .28s, visibility 0s linear .28s;
}
.modal.open { transform: translate(-50%, -50%) scale(1); opacity: 1; visibility: visible; pointer-events: auto; transition: transform .28s cubic-bezier(.2,.7,.3,1), opacity .28s, visibility 0s; }
/* rejilla técnica sutil */
.modal::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5;
  background-image: linear-gradient(var(--line-soft) 1px, transparent 1px), linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 26px 26px; mask-image: linear-gradient(180deg, #000, transparent 60%);
}
/* brackets de esquina */
.bracket { position: absolute; width: 16px; height: 16px; border: 1.5px solid var(--cyan); opacity: .7; pointer-events: none; }
.bracket.tl { top: -1px; left: -1px; border-right: 0; border-bottom: 0; }
.bracket.tr { top: -1px; right: -1px; border-left: 0; border-bottom: 0; }
.bracket.bl { bottom: -1px; left: -1px; border-right: 0; border-top: 0; }
.bracket.br { bottom: -1px; right: -1px; border-left: 0; border-top: 0; }

.modal-head {
  position: relative; display: flex; align-items: center; justify-content: space-between;
  padding: 10px 16px 8px; border-bottom: 1px solid var(--line);
}
.modal-head h1 { margin: 0; display: flex; align-items: center; }
.modal-head h1 img.brand-logo { display: block; height: 26px; width: auto; }
.icon-btn {
  border: none; background: transparent; color: var(--muted); font-size: 14px; cursor: pointer;
  width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; padding: 0;
}
.icon-btn:hover { color: var(--cyan); }
.head-tools { display: flex; align-items: center; gap: 10px; }
.lang { display: flex; border: 1px solid var(--line); }
.lang-btn {
  border: none; background: transparent; color: var(--muted);
  font-family: var(--mono); font-size: 10px; font-weight: 700; letter-spacing: 1px;
  padding: 4px 9px; cursor: pointer; transition: background .15s, color .15s;
}
.lang-btn + .lang-btn { border-left: 1px solid var(--line); }
.lang-btn:hover { color: var(--fg); }
.lang-btn.active { background: rgba(93,231,255,0.16); color: var(--cyan); box-shadow: inset 0 0 12px rgba(93,231,255,.2); }
.modal-head { flex: 0 0 auto; }
.modal-body { position: relative; flex: 1 1 auto; min-height: 0; padding: 16px 18px calc(18px + env(safe-area-inset-bottom)); overflow-y: auto; -webkit-overflow-scrolling: touch; }
/* El resto de la app bloquea selección de texto a propósito (look de wallpaper), pero
   el contenido del modal de info (créditos, texto de autor) es texto real que alguien
   podría querer copiar. */
.modal.info .modal-body, .modal.info .modal-body * { user-select: text; -webkit-user-select: text; }

/* Panel de config: cajón lateral derecho (sin backdrop) ------ */
.drawer {
  position: fixed; top: 0; right: 0; z-index: 20;
  width: min(340px, 92vw); height: 100%; height: 100dvh;
  display: flex; flex-direction: column;
  touch-action: auto;   /* excepción al touch-action:none de html/body, ver .modal */
  background: rgba(6, 12, 22, 0.9);
  border-left: 1px solid var(--line);
  box-shadow: -18px 0 50px rgba(0,0,0,.5), inset 0 0 40px rgba(93,231,255,.04);
  backdrop-filter: blur(12px) saturate(1.15); -webkit-backdrop-filter: blur(12px) saturate(1.15);
  transform: translateX(100%); visibility: hidden; pointer-events: none;
  transition: transform .34s cubic-bezier(.22,.61,.36,1), visibility 0s linear .34s;
}
.drawer.open { transform: translateX(0); visibility: visible; pointer-events: auto; transition: transform .34s cubic-bezier(.22,.61,.36,1), visibility 0s; }
@media (max-width: 560px) { .drawer { width: 88vw; } }

/* Layout compacto (sin scroll) ------------------------------ */
/* Ritmo vertical unificado: 6px entre controles de un mismo grupo, 11px
   arriba de cada título de sección (los márgenes de bloques contiguos
   colapsan, así que el gap real entre grupos queda en ese máximo de 11px —
   3px más que el resto del ritmo, a propósito, para separar secciones). */
.compact { padding: 10px 13px calc(10px + env(safe-area-inset-bottom)); }
.compact h2 {
  margin: 16px 0 6px; font-size: 9.5px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--cyan-2); display: flex; align-items: center; gap: 8px;
}
.compact h2:first-child { margin-top: 2px; }
.compact h2::before { content: ""; width: 7px; height: 7px; background: var(--cyan); box-shadow: var(--glow); clip-path: polygon(0 0, 100% 0, 100% 100%); }
.compact h2::after { content: ""; flex: 1; height: 1px; background: var(--line); }
/* Sub-encabezado liviano dentro de un grupo .compact (ej. "Marcador 2"/"Velocidad"
   dentro de "Configuración avanzada") — mismo tono que un h2 pero sin la decoración
   de línea doble, para no competir visualmente con el título real de la sección. */
.compact .sel-sub {
  margin: 10px 0 6px; font-size: 8.5px; font-weight: 700; letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--muted);
}
.compact .sel-sub:first-child { margin-top: 0; }
.grid { display: grid; gap: 6px; margin-bottom: 6px; }
.grid.one { grid-template-columns: 1fr; }
.grid.two { grid-template-columns: 1fr 1fr; }
.grid.three { grid-template-columns: 1fr 1fr 1fr; }
/* Ciudad + lat + lon + botón en una sola línea (marcador 1 y 2) — el buscador
   va en un wrapper .search propio (min-width:0 para poder achicarse dentro
   del grid; el desplegable de resultados se ensancha más que el input para
   seguir siendo legible, ver .results). */
.grid.loc { grid-template-columns: minmax(0, 1.3fr) minmax(0, .85fr) minmax(0, .85fr) auto; }
.grid.loc .search { margin-bottom: 0; min-width: 0; }
.grid.dt { grid-template-columns: auto 1fr; }
.compact .sel { margin-bottom: 0; gap: 3px; }
.compact .sel > span { font-size: 8px; letter-spacing: 1.5px; }
.btn.icon { width: auto; padding: 0 10px; font-size: 15px; }

.toggles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin-bottom: 8px; }
.tg { display: flex; flex-direction: column; align-items: center; gap: 3px; cursor: pointer; text-align: center; }
.tg-txt { display: flex; align-items: center; gap: 3px; }
.tg-txt > span { font-size: 9px; letter-spacing: .2px; color: var(--fg); }
.tg kbd {
  font-family: var(--mono); font-size: 8px; color: var(--muted);
  border: 1px solid var(--line); border-radius: 2px; padding: 0 4px; min-width: 12px; opacity: .75;
}

.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; margin-top: 6px; }
.actions .btn { font-size: 10px; padding: 7px 6px; }

/* Configuración avanzada (Premium) --------------------------- */
/* reflectAdvancedLock() en app.js decide: sin licencia, #adv-banner queda visible y
   #adv-body atenuado + con sus inputs/selects/botones deshabilitados (JS, no solo
   CSS — para que tampoco se puedan operar por teclado). Con licencia, el banner se
   oculta y el bloque recupera opacidad/uso normal. */
.adv-banner {
  display: block; width: 100%; margin-bottom: 10px; padding: 8px 10px;
  background: rgba(255,190,92,0.08); border: 1px solid rgba(255,190,92,0.35);
  color: var(--amber); font-family: var(--mono); font-size: 10px; letter-spacing: .3px;
  text-align: left; line-height: 1.4; cursor: pointer; transition: background .15s;
}
.adv-banner:hover { background: rgba(255,190,92,0.16); }
.adv-banner[hidden] { display: none; }
/* .42 dejaba el texto casi ilegible (candado sin poder confirmar qué había detrás) —
   .65 sigue leyéndose "apagado"/deshabilitado sin volverse un misterio. */
.adv-body.locked { opacity: .65; filter: grayscale(.3); }
.adv-body.locked * { cursor: not-allowed; }

/* Grupos y campos ------------------------------------------- */
.grp { margin-bottom: 14px; }
.grp h2 {
  margin: 0 0 7px; font-size: 10px; font-weight: 700; letter-spacing: 2.5px;
  text-transform: uppercase; color: var(--cyan-2);
  display: flex; align-items: center; gap: 8px;
}
.grp h2::before { content: ""; width: 8px; height: 8px; background: var(--cyan); box-shadow: var(--glow); clip-path: polygon(0 0, 100% 0, 100% 100%); }
.grp h2::after { content: ""; flex: 1; height: 1px; background: var(--line); }

.row { margin-bottom: 7px; }
.row.two { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
label { font-size: 12px; color: var(--fg); display: block; }
.sel, .row label { display: flex; flex-direction: column; gap: 4px; font-size: 9px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--muted); margin-bottom: 7px; }

/* Mismo alto que los botones del panel (~26px) — antes eran más altos que
   cualquier botón de la fila (p.ej. el de geolocalización), sin razón. */
input[type="number"], input[type="text"], input[type="email"], input[type="datetime-local"], select {
  width: 100%; padding: 6px 9px;
  border: 1px solid var(--line); background: var(--field); color: var(--fg);
  font-family: var(--mono); font-size: 11px; letter-spacing: .5px;
  outline: none; appearance: none; -webkit-appearance: none; border-radius: 0;
  transition: border-color .15s, box-shadow .15s;
}
select {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%235de7ff' d='M6 8 0 0h12z'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center; padding-right: 26px; cursor: pointer;
}
input:focus, select:focus { border-color: var(--cyan); box-shadow: var(--glow); }
select:disabled { opacity: .38; cursor: not-allowed; }
option { background: #071322; color: var(--fg); }

/* Dropdown propio (reemplaza el picker nativo de <select>) ------------- */
/* El <select> real queda en el DOM (fuente de la verdad de .value/'change'
   para el resto del código) pero invisible e inerte — el popup nativo es una
   ventana del sistema operativo, y en un WebView sin foco real (motores de
   wallpaper tipo Lively) puede no abrirse ni recibir clics. Mismo alto/
   tamaño de fuente que los inputs de al lado, pero con la técnica de esquina
   cortada de los botones en vez del recuadro recto del select nativo. */
.dd { position: relative; }
.dd-native { position: absolute; opacity: 0; pointer-events: none; }
.dd-trigger {
  --o: polygon(0 0, calc(100% - 6px) 0, 100% 6px, 100% 100%, 6px 100%, 0 calc(100% - 6px));
  --i: polygon(0 0, calc(100% - 5.4px) 0, 100% 5.4px, 100% 100%, 5.4px 100%, 0 calc(100% - 5.4px));
  --fill: var(--field);
  width: 100%; padding: 6px 9px; background: var(--brd, var(--line)); clip-path: var(--o);
  color: var(--fg); font-family: var(--mono); font-size: 11px; letter-spacing: .5px;
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  cursor: pointer; text-align: left; transition: color .15s, filter .15s;
}
.dd-trigger:hover { --brd: var(--cyan); --fill: rgba(93,231,255,0.14); color: var(--bg); }
.dd-trigger.open { --brd: var(--cyan); --fill: rgba(93,231,255,0.14); color: var(--bg); }
.dd-trigger:disabled { opacity: .38; cursor: not-allowed; }
.dd-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd-trigger::after {
  content: ""; flex: 0 0 auto; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 6px solid var(--cyan);
  transition: transform .15s;
}
.dd-trigger.open::after { transform: rotate(180deg); }

.dd-list {
  list-style: none; margin: 3px 0 0; padding: 3px;
  position: absolute; left: 0; right: 0; top: 100%; z-index: 6; max-height: 240px; overflow-y: auto;
  background: rgba(7, 15, 26, 0.98); border: 1px solid var(--line); box-shadow: var(--glow);
}
.dd-list[hidden] { display: none; }
/* Sin espacio hacia abajo (dropdown cerca del final del panel) — se abre hacia
   arriba en su lugar, ver toggle de esta clase en enhanceSelect()/open(). */
.dd-list.up { top: auto; bottom: 100%; margin: 0 0 3px; }
.dd-list li { padding: 8px 10px; cursor: pointer; font-size: 12px; letter-spacing: .5px; }
.dd-list li:hover, .dd-list li.hover { background: rgba(93,231,255,0.26); color: var(--fg); }
.dd-list li.active { background: var(--cyan); color: var(--bg); }
/* Opción Premium sin licencia (ver fillSelect()/DAY_SOURCES "pro:true" en app.js):
   visible con candado, pero atenuada y sin el resaltado de hover normal — clickearla
   avisa por qué está bloqueada en vez de seleccionarla. */
.dd-list li.locked { color: var(--muted); opacity: .6; }
.dd-list li.locked:hover, .dd-list li.locked.hover { background: none; color: var(--muted); }

/* Sliders (tamaño / sombreador / opacidad) ------------------ */
/* padding vertical para que la fila quede ~26px, igual que botones/inputs */
.slider { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 9px; margin-bottom: 6px; padding: 4px 0; }
.sl-label { font-size: 8px; text-transform: uppercase; letter-spacing: 1.2px; color: var(--muted); white-space: nowrap; }
/* Hint de atajo inline junto al label — mismo estilo que .tg kbd (toggles), para el
   único slider que por ahora tiene atajo propio (Sombreador, tecla "s"). */
.sl-label kbd {
  font-family: var(--mono); font-size: 8px; color: var(--muted); text-transform: none;
  border: 1px solid var(--line); border-radius: 2px; padding: 0 4px; min-width: 12px;
  opacity: .75; margin-left: 4px;
}
.slider .sl-val { font-size: 10px; color: var(--cyan); font-variant-numeric: tabular-nums; min-width: 58px; text-align: right; white-space: nowrap; }

/* Selector de velocidad (segmented control), de más lenta a más rápida:
   Detener · Tiempo real (ambos ocupan la fila completa) · 8 tasas en 2 filas de 4. */
.speedgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; margin: 4px 0 6px; }
/* Mismos bevels (esquina cortada) que .btn/.vbtn. --brd/--fill siguen el mismo
   patrón que .btn:hover. Con 2 filas de 4 (en vez de 1 sola de 6-7) hay más ancho
   por botón — permite un tamaño de fuente legible en vez del mínimo apretado. */
.spd-btn {
  --o: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 7px 100%, 0 calc(100% - 7px));
  --i: polygon(0 0, calc(100% - 6.4px) 0, 100% 6.4px, 100% 100%, 6.4px 100%, 0 calc(100% - 6.4px));
  --fill: var(--field);
  padding: 7px 2px; font-size: 9.5px; letter-spacing: .2px; text-transform: uppercase;
  font-family: var(--mono); color: var(--muted);
  background: var(--brd, var(--line)); clip-path: var(--o); cursor: pointer; text-align: center;
  transition: color .15s, filter .15s, opacity .2s;
}
.spd-btn:hover { --brd: var(--cyan); --fill: rgba(93,231,255,0.7); color: var(--bg); }
.spd-btn.active { --brd: var(--cyan); --fill: var(--cyan); color: var(--bg); }
/* "Detener" y "Tiempo real" comparten la misma altura que los botones de tasa
   (mismo padding vertical/tamaño de fuente que .spd-btn), al ser las dos filas de
   ancho completo del grupo. */
.spd-btn.real, .spd-btn.stop { grid-column: 1 / -1; padding: 7px 4px; font-size: 9.5px; }
.spd-btn.real { font-weight: 700; --brd: var(--cyan); --fill: var(--bg); color: var(--cyan); }
.spd-btn.real.active { --fill: var(--cyan); color: var(--bg); }
.spd-btn.stop:hover { --brd: var(--amber); --fill: rgba(255,190,92,0.7); color: var(--bg); }
.spd-btn.stop.active { --brd: var(--amber); --fill: var(--amber); color: var(--bg); }
.speedgrid.stopped .spd-btn:not(.stop) { opacity: .6; filter: grayscale(.5); }
input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; height: 6px; padding: 0; margin: 0;
  background: var(--field); border: 1px solid var(--line); outline: none; cursor: pointer;
}
input[type="range"]:focus { border-color: var(--cyan); }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 18px; height: 18px;
  background: var(--cyan); border: 0; box-shadow: var(--glow); cursor: pointer;
}
input[type="range"]::-moz-range-thumb { width: 18px; height: 18px; border: 0; border-radius: 0; background: var(--cyan); box-shadow: var(--glow); cursor: pointer; }
input[type="range"]::-moz-range-track { height: 6px; background: var(--field); border: 1px solid var(--line); }

/* Buscador de ciudades -------------------------------------- */
.search { position: relative; margin-bottom: 6px; }
.results {
  list-style: none; margin: 3px 0 0; padding: 3px;
  position: absolute; left: 0; z-index: 5; max-height: 240px; overflow-y: auto;
  /* El input ahora comparte fila con lat/lon/botón y queda angosto — el
     desplegable se ensancha más allá de ese ancho para seguir siendo legible. */
  width: max(100%, 210px);
  background: rgba(7, 15, 26, 0.98); border: 1px solid var(--line); box-shadow: var(--glow);
}
.results[hidden] { display: none; }
/* Mismo mecanismo que .dd-list.up: si no entra hacia abajo, se abre hacia arriba. */
.results.up { top: auto; bottom: 100%; margin: 0 0 3px; }
.results li { padding: 8px 10px; cursor: pointer; font-size: 12px; display: flex; justify-content: space-between; gap: 8px; letter-spacing: .5px; }
.results li small { color: var(--muted); text-transform: uppercase; letter-spacing: 1px; }
.results li:hover, .results li.active { background: var(--cyan); color: var(--bg); }
.results li:hover small, .results li.active small { color: var(--bg); opacity: .7; }
.results li.empty { color: var(--muted); cursor: default; justify-content: center; }
.results li.empty:hover { background: none; color: var(--muted); }

/* Toggles --------------------------------------------------- */
.chk { display: flex; flex-direction: row; align-items: center; gap: 9px; font-size: 12px; color: var(--fg); margin-top: 8px; cursor: pointer; letter-spacing: .5px; }
.chk input, .tg input {
  appearance: none; -webkit-appearance: none; flex: 0 0 auto;
  border: 1px solid var(--line); background: var(--field); position: relative; cursor: pointer;
  transition: background .15s, border-color .15s, box-shadow .15s;
}
.chk input { width: 30px; height: 16px; }
.chk input::after, .tg input::after { content: ""; position: absolute; background: var(--muted); transition: transform .18s, background .18s; }
.chk input::after { top: 2px; left: 2px; width: 10px; height: 10px; }
.chk input:checked, .tg input:checked { border-color: var(--cyan); background: rgba(93,231,255,0.16); box-shadow: var(--glow); }
.chk input:checked::after, .tg input:checked::after { background: var(--cyan); }
.chk input:checked::after { transform: translateX(14px); }
/* Toggles del panel de opciones: mismo alto que los botones (~26px) — el
   indicador (el "cuadradito" que se desliza) sigue siendo cuadrado, así que
   el riel queda un poco más ancho para darle recorrido. */
.tg input { width: 43px; height: 24px; }
.tg input::after { top: 2px; left: 3px; width: 18px; height: 18px; }
.tg input:checked::after { transform: translateX(18px); }
.chk kbd {
  margin-left: auto; font-family: var(--mono); font-size: 10px; color: var(--cyan);
  border: 1px solid var(--line); padding: 1px 6px; min-width: 20px; text-align: center; opacity: .8;
}

/* Botones --------------------------------------------------- */
.btn {
  --o: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 9px 100%, 0 calc(100% - 9px));
  --i: polygon(0 0, calc(100% - 8.4px) 0, 100% 8.4px, 100% 100%, 8.4px 100%, 0 calc(100% - 8.4px));
  --fill: var(--field);
  width: 100%; padding: 9px 10px;
  background: var(--brd, var(--line)); clip-path: var(--o);
  color: var(--fg); font-family: var(--mono);
  font-size: 11px; letter-spacing: 1.4px; text-transform: uppercase; cursor: pointer;
  transition: color .15s, filter .15s;
  display: flex; align-items: center; justify-content: center; gap: 7px;
}
.btn:hover { --brd: var(--cyan); --fill: rgba(93,231,255,0.14); color: var(--bg); filter: drop-shadow(0 0 7px rgba(93,231,255,.4)); }
.btn:active { transform: translateY(1px); }
/* Botón primario: relleno cyan sólido → texto negro (no cyan sobre cyan) en reposo y hover */
.btn.primary { --brd: var(--cyan); --fill: var(--cyan); color: var(--bg); }
.btn.primary:hover { --fill: var(--cyan); color: var(--bg); filter: brightness(1.12) drop-shadow(0 0 10px rgba(93,231,255,.55)); }
.btn.ghost { color: var(--muted); }
.btn.ghost:hover { color: var(--bg); }
.btn kbd { font-family: var(--mono); font-size: 10px; border: 1px solid var(--line); padding: 0 5px; opacity: .8; }

/* Borde uniforme en formas con esquinas cortadas (mismo grosor en 0°, 45° y 90°).
   Técnica de dos capas: la caja exterior lleva el COLOR de borde recortado a la
   forma; una ::before de RELLENO va 1px hacia dentro (inset) con un recorte cuyo
   "corte" se reduce en b·(2−√2)≈0.59px para que la diagonal quede también a 1px
   perpendicular. La ::before se pinta con z-index:-1 (encima del borde, debajo
   del contenido). */
.btn, .fab, .vbtn, .spd-btn, .dd-trigger, .perigami-link { position: relative; }
.btn, .fab, .vbtn, .spd-btn, .dd-trigger, .toast, .marker-label, .perigami-link { border: none !important; isolation: isolate; }
.btn::before, .fab::before, .vbtn::before, .spd-btn::before, .dd-trigger::before, .toast::before, .marker-label::before, .perigami-link::before {
  content: ""; position: absolute; inset: 1px; z-index: -1;
  clip-path: var(--i); background: var(--fill);
}

.foot { margin-top: 16px; font-size: 10px; line-height: 1.6; letter-spacing: .5px; color: var(--muted); opacity: .8; }

/* Toast ----------------------------------------------------- */
.toast {
  position: fixed;
  right: calc(18px + env(safe-area-inset-right));
  bottom: calc(24px + env(safe-area-inset-bottom));
  z-index: 30; padding: 10px 18px;
  --o: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
  --i: polygon(0 0, calc(100% - 7.4px) 0, 100% 7.4px, 100% 100%, 7.4px 100%, 0 calc(100% - 7.4px));
  --fill: rgba(6, 12, 22, 0.94);
  background: var(--line); clip-path: var(--o);
  color: var(--cyan); font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
  filter: drop-shadow(0 0 10px rgba(93,231,255,.28));
  opacity: 0; transform: translateY(6px); transition: opacity .25s, transform .25s, bottom .2s; pointer-events: none;
}
/* El reloj puede quedar parqueado justo en esa esquina (drag + snap a esquinas,
   ver applyClockPos()) — se sube el toast para no superponerse. */
body.clock-br .toast { bottom: calc(94px + env(safe-area-inset-bottom)); }
.toast[hidden] { display: none; }
.toast.show { opacity: 1; transform: translateY(0); }

/* Banner "nueva versión" ------------------------------------ */
.update-banner {
  position: fixed; top: calc(14px + env(safe-area-inset-top)); left: 50%; transform: translateX(-50%);
  z-index: 31; padding: 9px 18px;
  background: var(--cyan); color: #04121f; border: none;
  font-family: var(--mono); font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  cursor: pointer; box-shadow: 0 0 22px rgba(93,231,255,.6);
  clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 8px 100%, 0 calc(100% - 8px));
  animation: updIn .3s ease;
}
.update-banner[hidden] { display: none; }
@keyframes updIn { from { opacity: 0; transform: translate(-50%, -8px); } to { opacity: 1; transform: translate(-50%, 0); } }

/* Menú de vistas 3D ----------------------------------------- */
.vmenu {
  position: fixed;
  left: calc(16px + env(safe-area-inset-left));
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 8; display: flex; flex-direction: row; align-items: center; gap: 6px;
  transition: opacity .5s ease;
}
.vmenu[hidden] { display: none; }
.vbtn {
  --o: polygon(0 0, calc(100% - 7px) 0, 100% 7px, 100% 100%, 0 100%);
  --i: polygon(0 0, calc(100% - 6.4px) 0, 100% 6.4px, 100% 100%, 0 100%);
  --fill: rgba(6,12,22,0.5);
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--brd, var(--line)); clip-path: var(--o);
  color: var(--fg); font-family: var(--mono); font-size: 11px; letter-spacing: .5px;
  padding: 7px 11px; cursor: pointer; white-space: nowrap;
  transition: color .15s, filter .15s;
}
.vbtn::before { backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.vbtn:hover { --brd: var(--cyan); --fill: rgba(93,231,255,0.14); color: var(--bg); filter: drop-shadow(0 0 7px rgba(93,231,255,.4)); }
.vbtn:active { transform: translateY(1px); }
.vbtn:disabled { opacity: .5; cursor: not-allowed; pointer-events: none; }
.vbtn.export { --brd: var(--cyan); --fill: var(--cyan); color: var(--bg); }
.vbtn.export:hover { --fill: var(--cyan); color: var(--bg); filter: brightness(1.12) drop-shadow(0 0 8px rgba(93,231,255,.5)); }
/* Vista activa (Mi ubicación/Iluminada/Luna/Sombra): mismo tratamiento "filled" que
   Exportar, para que se lea con el mismo lenguaje visual de "estado seleccionado". */
.vbtn.active { --brd: var(--cyan); --fill: var(--cyan); color: var(--bg); }
.vbtn.active:hover { filter: brightness(1.12) drop-shadow(0 0 8px rgba(93,231,255,.5)); }
body.idle .vmenu { opacity: 0; pointer-events: none; }
@media (max-width: 620px) {
  .vbtn { padding: 7px 8px; font-size: 9.5px; }   /* sin iconos: texto compacto en móvil */
}

/* Modal de información -------------------------------------- */
.modal.info { width: min(480px, 94vw); }
.lead { font-size: 12px; line-height: 1.5; color: var(--fg); margin: 0 0 10px; letter-spacing: .3px; }
.lead strong { color: var(--cyan); font-weight: 600; }
#plan-badge.pro { color: var(--amber); }   /* Premium activo: mismo acento ámbar que la antípoda, para distinguirlo del cyan "gratis" */
.howto {
  display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px;
  padding: 8px 10px; border: 1px solid var(--line-soft); background: var(--field);
  font-size: 11px; color: var(--muted); letter-spacing: .3px;
}
.howto b { color: var(--fg); font-weight: 600; }
.howto kbd { font-family: var(--mono); font-size: 10px; color: var(--cyan); border: 1px solid var(--line); padding: 0 4px; }
.toggle-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 16px; }
.toggle-grid .chk { margin-top: 5px; }
.credits { list-style: none; margin: 0; padding: 0; font-size: 11px; line-height: 1.6; color: var(--muted); }
.credits b { color: var(--fg); font-weight: 600; }
.author { font-size: 11.5px; line-height: 1.5; color: var(--muted); margin: 0 0 10px; }
.author b { color: var(--fg); }
.plan-current { margin-bottom: 6px; }
.plan-perks { margin-bottom: 0; }
.text-link {
  color: var(--fg); text-decoration: underline; text-decoration-color: var(--line);
  text-underline-offset: 2px; cursor: pointer; background: none; border: none; padding: 0;
  font: inherit; letter-spacing: inherit; transition: color .15s;
}
.text-link:hover { color: var(--cyan); }
.perigami-link { padding: 4px; }
.perigami-link:hover { filter: drop-shadow(0 0 6px rgba(93,231,255,.4)); }
.perigami-link img { display: block; height: 48px; width: auto; filter: drop-shadow(0 0 6px rgba(93,231,255,.4)); }
.info-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 14px; padding-top: 11px; border-top: 1px solid var(--line);
}
.info-foot .chk { margin-top: 0; font-size: 11px; }
.info-foot .btn { width: auto; padding: 9px 18px; }
/* Botonera de la sección Plan. reflectPlanBadge() la oculta entera para quien ya
   compró (plan-plus.md §5: nada de publicidad de lo ya pagado). */
.plan-cta { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; margin-top: 10px; }
.plan-cta[hidden] { display: none; }
.plan-cta .btn { width: auto; padding: 9px 16px; }

/* Modal de compra (equirectime+) ----------------------------
   Tres pantallas turnándose el mismo cuerpo (ver js/compra.js); lo de acá sirve a
   las tres. El estado "elegido" de cada producto se marca DOS veces a propósito: el
   radio nativo (accent-color) y el realce de la tarjeta vía :has() — donde :has() no
   exista, el radio sigue diciendo cuál está elegido, que es lo que no puede faltar. */
.modal.compra { width: min(460px, 94vw); }
.buy-title { font-family: var(--mono); font-size: 13px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: var(--cyan); }
.buy-opts { display: grid; gap: 8px; margin-bottom: 12px; }
.buy-opt {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 9px; align-items: center;
  padding: 10px 12px; border: 1px solid var(--line); background: var(--field);
  cursor: pointer; transition: border-color .15s, background .15s;
}
.buy-opt:hover { border-color: rgba(93,231,255,.45); }
.buy-opt:has(input:checked) { border-color: var(--cyan); background: rgba(93,231,255,0.10); box-shadow: inset 0 0 18px rgba(93,231,255,.08); }
.buy-opt input { grid-column: 1; grid-row: 1; width: 14px; height: 14px; margin: 0; accent-color: var(--cyan); cursor: pointer; }
.buy-opt-head { grid-column: 2; grid-row: 1; display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.buy-opt-name { font-family: var(--mono); font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: var(--fg); }
.buy-opt-price { font-family: var(--mono); font-size: 13px; font-weight: 700; color: var(--cyan); }
.buy-specs { grid-column: 2; grid-row: 2; list-style: none; margin: 0; padding: 0; font-size: 11px; line-height: 1.55; color: var(--muted); }
.buy-specs li::before { content: "· "; color: var(--cyan); }
.buy-field { display: flex; flex-direction: column; gap: 4px; margin-bottom: 10px; font-size: 9px; letter-spacing: 1.5px; text-transform: uppercase; color: var(--muted); }
/* El límite de la versión gratis, dicho de frente y antes de pagar nada. */
.buy-free { font-size: 11px; line-height: 1.5; color: var(--muted); margin: 0 0 10px; padding: 8px 10px; border: 1px solid var(--line-soft); background: var(--field); }
.buy-aviso { margin: 0 0 10px; font-size: 11px; line-height: 1.45; color: var(--amber); }
.buy-aviso[hidden] { display: none; }
.buy-foot { display: flex; flex-direction: column; align-items: center; gap: 10px; margin-top: 4px; }
.buy-foot .btn { width: auto; padding: 10px 22px; }
.buy-foot .btn:disabled { opacity: .5; cursor: not-allowed; filter: none; }
.buy-foot .text-link { font-size: 11px; color: var(--muted); }
.buy-res { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 10px; padding: 8px 0 4px; }
.buy-res p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--fg); }
.buy-res-icon { width: 48px; height: 48px; }
.buy-res-icon.ok { color: var(--cyan); filter: drop-shadow(0 0 10px rgba(93,231,255,.45)); }
.buy-res-icon.espera { color: var(--muted); }
.buy-res-icon.error { color: var(--amber); }
.buy-res-icon[hidden] { display: none; }

@media (max-width: 560px) {
  .fab { width: 48px; height: 48px; }
  #clock-time { font-size: 22px; }
}
/* Foco visible por teclado (a11y) */
:focus-visible { outline: 2px solid var(--cyan); outline-offset: 2px; }
.btn:focus-visible, .fab:focus-visible, .vbtn:focus-visible, .lang-btn:focus-visible,
.tg input:focus-visible, .chk input:focus-visible, input:focus-visible, select:focus-visible,
.icon-btn:focus-visible, .update-banner:focus-visible, .dd-trigger:focus-visible {
  outline: 2px solid var(--cyan); outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .marker-dot::after { animation: none; }
  .modal, .backdrop, .drawer, .toast, .controls, .vmenu, .update-banner { transition: none !important; animation: none !important; }
}
