/* shell-v2.css -- the bottom-bar shell.
 *
 * Loaded only by /v2. It re-arranges the EXISTING elements: the drawers, the
 * stat card and the weather panel become bottom sheets over a map that stays
 * visible, and a fixed bar sits within thumb reach. Nothing here restyles a
 * component -- only where things sit and how they come in.
 *
 * The point of the compromise: sheets, not full-screen tabs. Every area in this
 * app is about the map (weather colours the line, route lists its waypoints,
 * chat draws it), so a navigation that hides the map would separate a control
 * from the thing it controls.
 */

/* The band and the whole weather redesign live in band.css, shared by every
   shell that renders the band -- see its header for why. */
@import url('/band.css');

:root[data-shell="v2"] {
  --v2-bar-h: 56px;
  --v2-sheet-max: 62vh;
  /* A closed sheet has to travel its own height PLUS the gap between its
     bottom edge and the viewport floor -- otherwise it parks in the strip
     behind the bar and its buttons stay hittable through it. translateY(103%)
     only clears a sheet taller than ~1900px, which is to say never. */
  --v2-sheet-hide: calc(100% + var(--v2-bar-h) + var(--safe-bottom) + 24px);

  /* ---- one motion vocabulary ---------------------------------------------
     Two speeds and one curve, used by every moving part in this shell. The
     curve decelerates hard: things arrive rather than glide to a stop, which
     is what makes a panel feel placed instead of floated.

     --v2-travel  a sheet crossing the screen edge (open from the map, close
                  to the map). Long enough to read as a place being entered.
     --v2-swap    one sheet replacing another. SHORT and small: the panel is
                  already there, only its content changed, so sending it off
                  the bottom and hauling it back is a lie about what happened
                  -- and it was the worst of the old behaviour.
     --v2-micro   pressed/active feedback on a control. */
  --v2-ease: cubic-bezier(.22, 1, .28, 1);
  --v2-travel: 340ms;
  --v2-leave: 240ms;
  --v2-swap: 190ms;
  --v2-micro: 160ms;
  --v2-swap-lift: 14px;
}

/* ---- the bar ------------------------------------------------------------ */
:root[data-shell="v2"] #v2-bar {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 20;
  height: calc(var(--v2-bar-h) + var(--safe-bottom));
  padding-bottom: var(--safe-bottom);
  display: flex; background: var(--surface); border-top: 1px solid var(--border);
}
:root[data-shell="v2"] #v2-bar button {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; background: none; border: 0; cursor: pointer; position: relative;
  color: var(--text-faint); font: 500 .58rem/1 var(--font-mono); letter-spacing: .05em;
  min-height: 44px; /* the tap target, not the icon */
  transition: color var(--v2-micro) ease;
}
:root[data-shell="v2"] #v2-bar svg {
  width: 19px; height: 19px; stroke: currentColor; fill: none;
  stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
  transition: transform var(--v2-travel) var(--v2-ease);
}
:root[data-shell="v2"] #v2-bar button.on { color: var(--accent); }
/* The icon lifts a hair when its place is the open one. Small enough that you
   feel it rather than watch it. */
:root[data-shell="v2"] #v2-bar button.on svg { transform: translateY(-2px); }
/* Press feedback belongs on the icon, not the whole button: scaling a 44px tap
   target makes the bar look like it wobbles. */
:root[data-shell="v2"] #v2-bar button:active svg { transform: scale(.88); transition-duration: 90ms; }
:root[data-shell="v2"] #v2-bar button:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

/* The active marker is ONE element that travels along the top edge, so moving
   between places reads as movement instead of one underline blinking off and
   another blinking on. On the TOP edge because there it says "this sheet is
   standing open above", which is exactly what it means. */
:root[data-shell="v2"] .v2-ink {
  position: absolute; top: 0; left: 0; height: 2px; width: 0;
  background: var(--accent); border-radius: 0 0 2px 2px; opacity: 0;
  transition: transform var(--v2-travel) var(--v2-ease),
              width var(--v2-travel) var(--v2-ease),
              opacity var(--v2-micro) ease;
  pointer-events: none;
}
:root[data-shell="v2"] #v2-bar button.has::after {
  content: ""; position: absolute; top: 8px; right: 27%;
  width: 5px; height: 5px; border-radius: 50%; background: var(--accent);
  animation: v2-pop 260ms var(--v2-ease);
}
@keyframes v2-pop { from { transform: scale(0); } to { transform: scale(1); } }

/* On a wide screen the pointer makes the sheet's own X trivial to hit, and
   every sheet has one -- so "Karte" is a second way to do what the X does.
   It stays on touch, where reaching a small X at the top of a tall sheet is
   worse than a labelled target under the thumb. */
@media (min-width: 1024px) {
  :root[data-shell="v2"] #v2-bar [data-tab="map"] { display: none; }
}

/* ---- the sheets --------------------------------------------------------- */
/* Four elements, one behaviour. Before this the drawers slid and the card and
   the weather panel appeared instantly (display:none has nothing to animate),
   so switching between them mixed two idioms in one gesture. */
:root[data-shell="v2"] .drawer.left,
:root[data-shell="v2"] .drawer.right,
:root[data-shell="v2"] .stat-card,
:root[data-shell="v2"] .v2-sheet {
  /* position/margin/order are re-stated, not inherited: at >=1024px the default
     shell turns the left drawer into a docked flex COLUMN (position:relative,
     margin-left:-380px when closed). On this shell it stays a sheet at every
     width, so those properties have to be taken back explicitly. */
  position: absolute; order: 0; flex: none; margin-left: 0;
  left: 0; right: 0; top: auto; width: auto; max-width: none;
  bottom: calc(var(--v2-bar-h) + var(--safe-bottom));
  height: auto; max-height: var(--v2-sheet-max);
  background: var(--surface);
  border: 1px solid var(--border); border-bottom: 0;
  border-radius: 14px 14px 0 0;
  box-shadow: 0 -10px 30px rgba(0,0,0,.35);
  z-index: 16;
  /* A flex column with height:auto + max-height does not clip: without this
     the profile fields simply carried on past the sheet's edge and under the
     bar. Clipping here is what lets the inner body's overflow-y do its job. */
  overflow: hidden;
  transform: none; opacity: 1; visibility: visible;
  transition: transform var(--v2-travel) var(--v2-ease),
              opacity var(--v2-travel) ease,
              visibility 0s linear 0s;
}
/* [hidden] is display:none, which cannot be animated -- so this shell paints
   the card and the weather sheet from .v2-open instead. The attribute is still
   kept in sync (app.js reads it), and a sheet the app hides on its own is
   caught by the observer in shell-v2.js, which closes the tab with it. */
:root[data-shell="v2"] .stat-card[hidden],
:root[data-shell="v2"] .v2-sheet[hidden] { display: block; }

:root[data-shell="v2"] .drawer.left:not(.v2-open),
:root[data-shell="v2"] .drawer.right:not(.v2-open),
:root[data-shell="v2"] .stat-card:not(.v2-open),
:root[data-shell="v2"] .v2-sheet:not(.v2-open) {
  transform: translateY(var(--v2-sheet-hide));
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: transform var(--v2-leave) var(--v2-ease),
              opacity var(--v2-leave) ease,
              visibility 0s linear var(--v2-leave);
}

/* A switch is not a departure. The outgoing sheet drops a few pixels and fades;
   the incoming one rises the same few pixels into its place. Nothing crosses
   the screen edge, because nothing left the screen. */
:root[data-shell="v2"][data-v2-motion="switch"] .drawer.left:not(.v2-open),
:root[data-shell="v2"][data-v2-motion="switch"] .drawer.right:not(.v2-open),
:root[data-shell="v2"][data-v2-motion="switch"] .stat-card:not(.v2-open),
:root[data-shell="v2"][data-v2-motion="switch"] .v2-sheet:not(.v2-open) {
  transform: translateY(var(--v2-swap-lift));
  transition: transform var(--v2-swap) ease-in,
              opacity 130ms ease,
              visibility 0s linear 130ms;
}
:root[data-shell="v2"][data-v2-motion="switch"] .drawer.left.v2-open,
:root[data-shell="v2"][data-v2-motion="switch"] .drawer.right.v2-open,
:root[data-shell="v2"][data-v2-motion="switch"] .stat-card.v2-open,
:root[data-shell="v2"][data-v2-motion="switch"] .v2-sheet.v2-open {
  transition: transform var(--v2-swap) var(--v2-ease), opacity var(--v2-swap) ease;
}
/* First paint: the shell decides which sheet is open before anyone is looking,
   and animating that would be a sheet flying in at load. */
:root[data-shell="v2"][data-v2-motion="none"] .drawer.left,
:root[data-shell="v2"][data-v2-motion="none"] .drawer.right,
:root[data-shell="v2"][data-v2-motion="none"] .stat-card,
:root[data-shell="v2"][data-v2-motion="none"] .v2-sheet { transition: none; }

/* The chat transcript scrolls inside a sheet that is no longer full height. */
:root[data-shell="v2"] #chat-drawer .drawer-body { overflow-y: auto; }
:root[data-shell="v2"] .stat-card,
:root[data-shell="v2"] .v2-sheet { overflow-y: auto; padding: 0 14px calc(12px + var(--safe-bottom)); }
/* The card's own peek/grab affordances belong to the old shell: a drag handle
   for a gesture this shell does not have, and no title next to it. */
:root[data-shell="v2"] .stat-card .grab,
:root[data-shell="v2"] .stat-card .stat-peek { display: none; }

/* ---- sheet headers ------------------------------------------------------ */
/* Route and Weather had neither a title nor a way out: in the default shell
   they are sections INSIDE the stat card, which has both. As sheets of their
   own they need their own. Sticky, so the way out does not scroll away. */
:root[data-shell="v2"] .v2-head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between; gap: .5rem;
  margin: 0 -14px 4px; padding: 12px 14px 8px;
  background: var(--surface); border-bottom: 1px solid var(--border);
}
:root[data-shell="v2"] .v2-title { margin: 0; font-size: .95rem; font-weight: 700; color: var(--text); }
:root[data-shell="v2"] .v2-x {
  flex: none; display: grid; place-items: center; width: 34px; height: 34px;
  background: none; border: 1px solid var(--border); border-radius: var(--radius-sm);
  color: var(--text-muted); cursor: pointer;
  transition: background var(--v2-micro) ease, color var(--v2-micro) ease, transform var(--v2-micro) var(--v2-ease);
}
/* The weather panel drew a rule along its top because it was a SECTION inside
   the route card. As a sheet with its own header that rule is a second line
   under the first. */
:root[data-shell="v2"] #v2-wx .wx-panel { border-top: 0; padding-top: 0; }
:root[data-shell="v2"] .v2-x svg { width: 17px; height: 17px; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; }
:root[data-shell="v2"] .v2-x:hover { background: var(--surface-hover); color: var(--text); }
:root[data-shell="v2"] .v2-x:active { transform: scale(.9); }
:root[data-shell="v2"] .v2-x:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }

/* Settings has its own place in the bar now, so the button in the profile
   drawer's foot would be a second door to the same room. shell-v2.js still
   clicks it -- it is the app's own opener, and reusing it keeps the form's
   population and save paths untouched. */
:root[data-shell="v2"] #profile-drawer .drawer-foot { display: none; }
/* ...but shell-v2.js still clicks that button to fill the form in, so it has
   to stay in the DOM. display:none does not stop HTMLElement.click(). */

/* ---- things the bar replaces or displaces -------------------------------- */
/* Both corner FABs opened a drawer. The bar does that now, and a control that
   duplicates a tab is one the user has to think about twice. */
:root[data-shell="v2"] .corner-fab.tl,
:root[data-shell="v2"] .corner-fab.tr { display: none; }
:root[data-shell="v2"] #stat-pill { top: calc(var(--safe-top) + 8px); }

/* Everything anchored to the bottom edge gets the bar's height added to the
   offset it already had, so the cluster keeps its own internal spacing
   (toolbar -> menu -> elevation) instead of collapsing into one pile. */
:root[data-shell="v2"] .draw-toolbar { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 16px); }
:root[data-shell="v2"] .more-menu,
:root[data-shell="v2"] .elevation-panel { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 70px); }
:root[data-shell="v2"] .gesture-hint { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 76px); }
:root[data-shell="v2"] body.elev-showing .more-menu { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 214px); }
/* The home chip only appears >=768px, where it would sit on the bar. */
:root[data-shell="v2"] .corner-fab.bl { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 16px); }
/* Attribution is a licence condition, not decoration: the OSM and BRouter
   links have to stay readable and clickable, so they move above the bar too. */
:root[data-shell="v2"] .map-attribution { bottom: calc(var(--v2-bar-h) + var(--safe-bottom)); }
/* The toolbar cluster sits bottom-right on a wide screen and the attribution
   ran underneath it, so the last two credits (Mapterhorn, BRouter) were half
   covered. Attribution is a licence obligation, not decoration -- it gets its
   own line above the cluster rather than sharing one with it. */
@media (min-width: 768px) {
  :root[data-shell="v2"] .map-attribution { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 46px); }
}
/* The dev button is fixed at the bottom-left, i.e. straight through a sheet
   (z-index 30 beats the sheet's 16). Park it in the slot the chat FAB just
   vacated -- above the sheets, out of the reading area. */
:root[data-shell="v2"] .dev-btn {
  top: calc(var(--safe-top) + 12px); bottom: auto; left: 12px;
}

/* Settings stays the app's own <dialog> -- it is modal and per-device, not a
   view of the map -- but on this shell it arrives the way everything else
   does, from the bottom edge, instead of as a centred box. */
/* The settings form now lives in #v2-settings; the <dialog> it came out of is
   an empty shell that must not paint a border over the map. */
:root[data-shell="v2"] #settings-dialog { display: none; }
/* The form supplied its own padding and its own ✕ when it was a dialog. The
   sheet provides both, so the inner ones would be a second set. */
:root[data-shell="v2"] #v2-settings .dev-form { padding: 12px 0 0; }
:root[data-shell="v2"] #v2-settings .dev-head { display: none; }

/* On a wide screen the sheets stay sheets but do not stretch to a wall of
   text: the shell is designed for a thumb, and a 1400px sheet is neither. It
   does grow with the monitor, though -- 520px on a laptop reads cramped next
   to a map that has 2000px to spread out in. */
@media (min-width: 768px) {
  :root[data-shell="v2"] .drawer.left,
  :root[data-shell="v2"] .drawer.right,
  :root[data-shell="v2"] .stat-card,
  :root[data-shell="v2"] .v2-sheet {
    left: 50%; right: auto; width: min(94vw, clamp(560px, 46vw, 880px));
    margin-left: calc(min(94vw, clamp(560px, 46vw, 880px)) / -2);
    border-radius: 14px 14px 0 0;
    /* 62vh is a phone number: it keeps the map visible above a thumb-height
       sheet. On a desktop it clipped the weather panel mid-meteogram with 340
       unused pixels underneath it, so the sheet gets the room the screen has. */
    max-height: 76vh;
  }
  /* The default shell nudges the card aside for the docked columns
     (.app:has(#chat-drawer.open) .stat-card{left:380px}). That selector carries
     an id inside :has(), so it outweighs the rule above -- matched here by
     naming the card by id, which also lands later in the cascade. */
  :root[data-shell="v2"] .app #stat-card { left: 50%; right: auto; }
  :root[data-shell="v2"] #v2-bar { justify-content: center; }
  :root[data-shell="v2"] #v2-bar button { flex: 0 0 108px; }
}

/* Reduced motion means no motion, not less of it: every transition and every
   keyframe above is decoration on a state change that is already legible. */
@media (prefers-reduced-motion: reduce) {
  :root[data-shell="v2"] .drawer.left,
  :root[data-shell="v2"] .drawer.right,
  :root[data-shell="v2"] .stat-card,
  :root[data-shell="v2"] .v2-sheet,
  :root[data-shell="v2"] .v2-ink,
  :root[data-shell="v2"] .v2-x,
  :root[data-shell="v2"] #v2-bar button,
  :root[data-shell="v2"] #v2-bar svg { transition: none !important; }
  :root[data-shell="v2"] #v2-bar button.has::after { animation: none !important; }
}

/* ---- the bottom cluster steps aside ---------------------------------------
   With a sheet open the centred toolbar sits underneath it. Above 1100px there
   is room beside the sheet, so it slides to the right edge and stays usable;
   the transform is the only thing that changes, so it animates. Below that the
   gap is narrower than the cluster itself and moving it would only trade one
   overlap for another. */
@media (min-width: 1100px) {
  :root[data-shell="v2"] .draw-toolbar,
  :root[data-shell="v2"] .more-menu { transition: transform var(--v2-travel) var(--v2-ease); }
  :root[data-shell="v2"]:not([data-v2-tab="map"]) .draw-toolbar,
  :root[data-shell="v2"]:not([data-v2-tab="map"]) .more-menu {
    transform: translateX(calc(50vw - 100% - 16px));
  }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-shell="v2"] .draw-toolbar,
  :root[data-shell="v2"] .more-menu,
  :root[data-shell="v2"] .brow,
  :root[data-shell="v2"] .beye { transition: none !important; }
}


/* ---- the peek bar ---------------------------------------------------------
   What stays when colouring the line closes the sheet: the mode's name, its
   strip, the one sentence, and a way back up. Hidden whenever a sheet is open
   -- expressed off the shell's own data-v2-tab so there is no second piece of
   state to keep in step. */
:root[data-shell="v2"] #v2-peek {
  position: absolute; left: 0; right: 0; z-index: 15;
  bottom: calc(var(--v2-bar-h) + var(--safe-bottom));
  display: flex; align-items: center; gap: 9px;
  padding: 8px 12px calc(8px + var(--safe-bottom));
  background: var(--surface); border-top: 1px solid var(--border);
  animation: v2-peek-in var(--v2-travel) var(--v2-ease);
}
:root[data-shell="v2"] #v2-peek[hidden] { display: none; }
:root[data-shell="v2"]:not([data-v2-tab="map"]) #v2-peek { display: none; }
@keyframes v2-peek-in { from { transform: translateY(100%); } to { transform: none; } }
:root[data-shell="v2"] .pk-name { flex: none; font-size: .78rem; font-weight: 700; }
:root[data-shell="v2"] .pk-stripwrap { flex: 1 1 auto; min-width: 40px; display: block; }
:root[data-shell="v2"] .pk-strip { display: block; width: 100%; height: 11px; border-radius: 2px; }
:root[data-shell="v2"] .pk-note {
  flex: none; font: 500 .66rem/1 var(--font-mono); color: var(--text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
:root[data-shell="v2"] .pk-up {
  flex: none; width: 34px; height: 34px; display: grid; place-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text-muted); cursor: pointer; font-size: 12px;
  transition: background var(--v2-micro) ease, transform var(--v2-micro) var(--v2-ease);
}
:root[data-shell="v2"] .pk-up:hover { background: var(--surface-hover); color: var(--text); }
:root[data-shell="v2"] .pk-up:active { transform: scale(.9); }
:root[data-shell="v2"] .pk-up:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
/* The map furniture sits above the peek bar, not under it. */
:root[data-shell="v2"]:has(#v2-peek:not([hidden])) .draw-toolbar { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 66px); }
:root[data-shell="v2"]:has(#v2-peek:not([hidden])) .map-attribution { bottom: calc(var(--v2-bar-h) + var(--safe-bottom) + 50px); }
@media (prefers-reduced-motion: reduce) {
  :root[data-shell="v2"] #v2-peek { animation: none !important; }
  :root[data-shell="v2"] .pk-up { transition: none !important; }
}
