/* ══ Phone landscape — rotate guard ══════════════════════════════════════════ */

#rotate-overlay {
  display: none;
  position: fixed; inset: 0; z-index: 99999;
  background: var(--surface-app, #202020);
  flex-direction: column; align-items: center; justify-content: center;
  gap: 20px; color: var(--text-primary, #eee); text-align: center; padding: 32px;
}

#rotate-overlay svg {
  opacity: 0.45;
  animation: rotate-hint 2.6s ease-in-out infinite;
}

#rotate-overlay p {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--text-subtle, #ccc);
  margin: 0;
  max-width: 220px;
  line-height: 1.5;
}

@keyframes rotate-hint {
  0%, 100% { transform: rotate(0deg); }
  30%, 70%  { transform: rotate(-90deg); }
}

@media (max-width: 767px) and (orientation: landscape) {
  #rotate-overlay { display: flex; }
}

/* ══ Frictionless mode transition ════════════════════════════════════════════ */

/* THE SIDE PANEL IS NOT IN HERE ANY MORE.
   A form-factor crossing is not a transition between two things the user is looking at — on the way
   up from mobile the panel is ARRIVING, and fading it in is an intro animation. It also broke the
   one thing the crossing is now supposed to get right: the panel and the map scale together from
   the panel's minimum width (scripts/resize.js), and a panel that spends 180ms fading is not
   scaling with anything. It mounts at its size, visible, in one frame.
   map-controller.js's fadeView skips the same moment, for the same reason. */
.mss-backdrop {
  transition: opacity 0.18s ease;
}

html.ui-transitioning .mss-backdrop {
  opacity: 0;
  pointer-events: none;
}

/* …and no WIDTH animation either, which is the one that was actually visible.
   `.app-body` carries `transition: margin-left var(--sidebar-dur)` for the sidebar collapsing —
   a deliberate animation of a deliberate act. A form-factor crossing changes the same margin, from
   the mobile shell's 0 to the desktop rail's width, and there it is an intro: the panel and the map
   spend 250ms sliding and narrowing into a frame they should simply BE in, which is exactly the
   "it animated to a width first" the mount is supposed to have stopped doing.
   It also lied to the split solver — resize.js measures app-body to size the panel at its minimum,
   and during the slide it was measuring a box still a quarter-second away from its final width. */
html.ui-transitioning .app-body { transition: none; }

/* ══ Mode utilities ══════════════════════════════════════════════════════════ */

[data-ui="desktop"] .mobile-only  { display: none !important; }
[data-ui="mobile"]  .desktop-only { display: none !important; }

/* ══ Dev form-factor override (Settings → Dev) ═══════════════════════════════
   Forcing tablet/phone constrains the app to a device width and centres it so
   the layout can be previewed on a wide desktop window. `app-resize` (fired by
   responsive.js) tells the map to resize into the new frame. */
[data-ff="tablet"], [data-ff="phone"] { background: #0a0a0b; }
[data-ff="tablet"] .app-body,
[data-ff="phone"]  .app-body {
  margin-left: auto !important;
  margin-right: auto !important;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1), 0 24px 70px rgba(0, 0, 0, 0.55);
}
[data-ff="tablet"] .app-body { max-width: 834px; }
[data-ff="phone"]  .app-body { max-width: 400px; }

/* Safety net: hide mobile backdrop when in desktop mode */
[data-ui="desktop"] .mss-backdrop { display: none !important; }
