/* public/css/layout.css —— 骨架栅格与三档断点(DESIGN §11.4)
 *
 * 为什么用一整块 grid 覆盖层,而不是给每个控件写 position:absolute:
 * 1. 地图是全屏底(#map fixed inset:0),UI 全部叠在上面。若各控件各自 absolute + 硬编码
 *    偏移,三个断点就要维护三套坐标,还挡不住互相重叠(图例压时间轴、详情压图例)。
 *    一块 grid 用具名区域一次排完,断点只换 grid-template-areas 与几个变量。
 * 2. 覆盖层默认 pointer-events:none,只有插槽里的**实际控件**(.slot > *)吃指针事件 ——
 *    这样插槽之间的空白仍然能拖地图。UI 模块只管往插槽里塞自己的根元素,不用关心穿透。
 * 3. 高度用 100dvh:移动端浏览器地址栏收起/展开会改变 vh,用 vh 时间轴会被顶出屏幕。
 * 4. 安全区:覆盖层整体加 env(safe-area-inset-*) 内边距(index.html 已声明 viewport-fit=cover),
 *    刘海/圆角/手势条区域内不放控件。
 *
 * 插槽契约(app.js 按 §5.2a 把它们喂给各 mount*):
 *   #map 地图容器 | #topbar{#search,#model-picker} 顶部 | #layer-bar 图层栏 | #legend 图例
 *   #attribution 署名 | #timeline 时间轴 | #detail 详情 | #menu 浮层(设置/关于/抽屉) | #toasts 提示
 */

/* ─────────────────────────── 底:地图 ─────────────────────────── */

#map {
  position: fixed;
  inset: 0;
  z-index: var(--z-map);
  background: var(--bg);
}

/* 标注 Canvas(map/label-layer.js 自己插进 #map)压在地图上、UI 之下,不吃事件(§7.5) */
#map canvas.aeolus-labels {
  position: absolute;
  inset: 0;
  z-index: var(--z-labels);
  pointer-events: none;
}

/* ─────────────────────────── UI 覆盖层 ─────────────────────────── */

#ui {
  position: fixed;
  inset: 0;
  z-index: var(--z-ui);
  height: 100vh;
  height: 100dvh;
  display: grid;
  gap: 0;
  padding:
    env(safe-area-inset-top) env(safe-area-inset-right)
    env(safe-area-inset-bottom) env(safe-area-inset-left);
  pointer-events: none;

  /* 桌面(≥ 1024):左图层栏 | 中(顶部条 / 图例 / 署名 / 时间轴) | 右详情面板 */
  grid-template-columns: auto minmax(0, 1fr) auto;
  grid-template-rows: auto minmax(0, 1fr) auto auto auto;
  grid-template-areas:
    "bar top    detail"
    "bar .      detail"
    "bar legend detail"
    "bar attrib detail"
    "tl  tl     detail";
}

/* 插槽本身透明于指针,插槽里的控件正常交互 */
.slot { pointer-events: none; }
.slot > * { pointer-events: auto; }

/* ── 顶部条:搜索 + 模式条 ── */
#topbar {
  grid-area: top;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--gap);
  padding: var(--gap);
  min-width: 0;
  pointer-events: none;
}

#search {
  flex: 0 1 380px;
  min-width: 0;
}

#model-picker {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  justify-content: flex-end;
}

/* ── 左侧图层栏 ── */
#layer-bar {
  grid-area: bar;
  align-self: center;
  justify-self: start;
  padding: var(--gap);
  max-height: 100%;
  overflow-y: auto;
  overflow-x: hidden;
}

/* ── 图例(右下,详情面板左侧) ── */
#legend {
  grid-area: legend;
  justify-self: end;
  align-self: end;
  padding: 0 var(--gap);
  max-width: min(420px, 100%);
}

/* ── 署名行(常驻,图例之下、时间轴之上) ── */
#attribution {
  grid-area: attrib;
  justify-self: end;
  align-self: end;
  padding: 6px var(--gap);
}

/* ── 底部时间轴 ── */
#timeline {
  grid-area: tl;
  min-width: 0;
  min-height: 0;
  padding: 0 var(--gap) var(--gap);
}

/* ── 右侧详情面板:内容决定宽度;没挂载/没内容时整列塌成 0 ── */
#detail {
  grid-area: detail;
  align-self: stretch;
  min-width: 0;
  max-width: min(var(--detail-w), 92vw);
  height: 100%;
  padding: var(--gap) var(--gap) var(--gap) 0;
  overflow: hidden;
}

#detail:empty { display: none; }

/* ─────────────────────────── 浮层:设置 / 关于 / 抽屉 ─────────────────────────── */

#menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  pointer-events: none;
  padding:
    env(safe-area-inset-top) env(safe-area-inset-right)
    env(safe-area-inset-bottom) env(safe-area-inset-left);
}

#menu:empty { display: none; }

/* ─────────────────────────── 提示条 ─────────────────────────── */

/* 提示条浮在时间轴之上。
   --timeline-actual-h 是 app.js 的 ResizeObserver 量出来的 **#timeline 插槽实高**(含它自己的
   var(--gap) 下内边距),所以只要再加一个 --gap 的呼吸就一定不压面板。
   为什么不能用名义常量:--timeline-h 只说"面板应该多高" —— 手机档名义 56 px,而 T24 的紧凑
   面板实测 66 px(日历条行高随字体走),按名义值算出来的底边会压进面板 2 px。
   var() 的兜底分支是原来的名义算法:JS 没起来 / ResizeObserver 缺席时行为不退化。 */
#toasts {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(
    var(--timeline-actual-h, calc(var(--timeline-h) + var(--gap)))
    + var(--gap) + env(safe-area-inset-bottom));
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gap-sm);
  width: max-content;
  max-width: min(520px, calc(100vw - 2 * var(--gap)));
  pointer-events: none;
}

/* ─────────────────────────── 平板 600–1023 ─────────────────────────── */

@media (max-width: 1023px) {
  :root {
    --bar-w: 48px;
    --detail-w: 340px;
    --gap: 10px;
  }

  /* 图层栏折叠成图标列(§11.4):具体折叠由 ui/layer-menu.js 读同一断点决定,
     这里只把可用宽度收窄,免得平板上左栏吃掉半屏。 */
  #layer-bar { max-width: calc(var(--bar-w) + 2 * var(--gap)); }

  #search { flex-basis: 300px; }
}

/* ─────────────────────────── 手机 < 600 ─────────────────────────── */

@media (max-width: 599px) {
  :root {
    --timeline-h: var(--timeline-h-compact);
    --gap: 8px;
  }

  /* 顶部搜索 + 右侧竖排图层图标 + 底部紧凑时间轴 + 抽屉(§11.4) */
  #ui {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-rows: auto minmax(0, 1fr) auto auto auto;
    grid-template-areas:
      "top    bar"
      ".      bar"
      "legend legend"
      "attrib attrib"
      "tl     tl";
  }

  #topbar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--gap-sm);
  }

  #search { flex: none; }

  #model-picker { justify-content: flex-start; }

  #layer-bar {
    align-self: center;
    justify-self: end;
    max-width: calc(var(--tap) + 2 * var(--gap));
  }

  #legend {
    justify-self: stretch;
    max-width: none;
  }

  #attribution {
    justify-self: end;
    padding: 2px var(--gap);
  }

  /* 详情面板变成底部抽屉:脱离栅格,高度由 ui/mobile.js 按 peek/half/full 控制 */
  #detail {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    z-index: var(--z-panel);
    width: auto;
    max-width: none;
    height: auto;
    max-height: 100dvh;
    padding: 0;
    padding-bottom: env(safe-area-inset-bottom);
  }

  /* 抽屉展开时把图例/署名让出去,避免叠在抽屉上 */
  body[data-sheet="half"] #legend,
  body[data-sheet="full"] #legend,
  body[data-sheet="full"] #attribution { visibility: hidden; }

  /* 抽屉在 peek 档(96 px)时把时间轴抬到抽屉之上:抽屉是 fixed bottom:0,不抬就会盖住
     时间滑块 —— 手机上"开着城市详情同时拖时间"是主用例(§11.4 紧凑时间轴与抽屉共存)。
     half/full 档抽屉本来就该覆盖时间轴,由 ui/mobile.js 决定要不要藏。
     做法是给整个 #ui 栅格加一段等于 peek 高的下内边距(border-box,§base.css),而不是只
     transform 时间轴:集成实测(390×844)只抬时间轴时,它正好压在上一行的图例刻度上,
     图例只剩半截露在时间轴下面;加内边距则 1fr 那一行(图层栏,自己可滚)缩短,
     图例 / 署名 / 时间轴整体上移,相对位置不变。#toasts 是 fixed 的,另按下面的规则抬。 */
  body[data-detail="open"][data-sheet="peek"] #ui {
    padding-bottom: calc(env(safe-area-inset-bottom) + var(--sheet-peek));
  }

  /* 时间轴被抬起来了,提示条跟着抬:transform 不改 offsetHeight,ResizeObserver 量不到这一位移。 */
  body[data-detail="open"][data-sheet="peek"] #toasts {
    bottom: calc(
      var(--timeline-actual-h, calc(var(--timeline-h) + var(--gap)))
      + var(--gap) + var(--sheet-peek) + env(safe-area-inset-bottom));
  }
}

/* ─────────────────────────── 触控设备:控件放大到 44 px ─────────────────────────── */

@media (pointer: coarse) {
  #layer-bar .a-btn,
  #topbar .a-btn,
  #timeline .a-btn {
    min-width: var(--tap);
    min-height: var(--tap);
  }
}

/* ─────────────────────────── 极矮视口(横屏手机) ─────────────────────────── */

@media (max-height: 480px) {
  #legend { display: none; }
  #timeline { padding-bottom: var(--gap-sm); }
}
