/* nvr/static/styles.css と同じデザイントークンを使う。閲覧専用のため大幅に小さい。 */
/* 配色は役割名だけで決まる。以後、直書きの色をこの下に置かないこと。
   管理者が選んだ配色は <html data-theme="…"> で切り替わる。 */
:root{
  --sidebar:250px;
  --bg:#0b1016;--surface:#111820;--surface-2:#17212b;--line:#263543;
  --line-strong:#3a5163;--text:#e8eef3;--text-2:#9cadba;--text-3:#7c8794;
  --muted:#7f91a1;--sidebar-bg:#0e151d;--topbar-bg:#101820e8;--input-bg:#0a1118;
  --hover:#17222c;--active-bg:#1d3040;--shadow:#0008;--backdrop:#000a;
  --login-glow:#193348;--focus-ring:#3d8bfd20;--on-accent:#fff;
  --cyan:#22b8cf;--blue:#3d8bfd;--blue-strong:#2f7bea;
  --green:#34c38f;--amber:#f1b44c;--red:#f46a6a;
  --warn-line:#6b5730;--warn-text:#d5b774;--warn-bg:#f1b44c0d;
  --info-line:#2c4a52;--info-text:#8fc9d6;--info-bg:#22b8cf14;
  --ok-bg:#34c38f14;--bad-bg:#f46a6a14;
  --tag-bg:#263847;--tag-warn-bg:#3d331b;
  --thumb-bg:#091017;--thumb-line:#2b3d4b;
  /* 映像の入れ物。明るい配色でも枠の中は暗いままにする。 */
  --card-bg:#0c1218;--card-bar-bg:#111a22;--card-line:#1f2c36;
  --card-text:#dce7ee;--card-text-2:#9aa9b6;--card-hover:#1d2a34;
}
[hidden]{display:none!important}
.mobile-only{display:none!important}
*{box-sizing:border-box}
/* 取りこぼしがあっても横スクロールにしない。原因は個別に潰すが、実機の
   Safariでしか出ない幅の違いがあり、手元のChromiumでは再現できない。
   NVR側も同じ砦を置いている。 */
img,video{max-width:100%}
html,body{margin:0;min-height:100%;max-width:100%;overflow-x:hidden;background:var(--bg);color:var(--text);font-family:Inter,"Noto Sans JP",system-ui,sans-serif}
button,input,select{font:inherit}
button{cursor:pointer}
.muted{color:var(--muted)}
.small{font-size:.68rem}
.error{color:var(--red);min-height:1.2em;font-size:.72rem}

/* ログイン */
.login-shell{min-height:100vh;display:grid;place-items:center;padding:24px;background:radial-gradient(circle at 50% 0,var(--login-glow) 0,transparent 40%),var(--bg)}
.login-card{width:min(410px,100%);padding:38px;border:1px solid var(--line);border-radius:12px;background:var(--surface);box-shadow:0 28px 80px var(--shadow)}
.brand-lockup{display:grid;gap:10px;text-align:center}
.brand-logo{display:block;object-fit:contain;border-radius:4px;background:#fff;max-width:100%}
.product-name{font-size:.82rem;letter-spacing:.08em}
.login-card h1{margin:24px 0 5px;font-size:1.6rem}
.login-card label{display:grid;gap:7px;margin:19px 0;color:var(--muted);font-size:.78rem}
.login-card input{padding:13px;border:1px solid var(--line);border-radius:6px;background:var(--input-bg);color:var(--text);outline:none}
.login-card input:focus{border-color:var(--blue);box-shadow:0 0 0 3px var(--focus-ring)}
.login-card button{width:100%;margin-top:5px;border:0;border-radius:6px;background:var(--blue);color:var(--on-accent);padding:13px;font-weight:700}
.login-button{display:block;margin-top:18px;padding:13px;border-radius:6px;background:var(--blue);color:var(--on-accent);font-weight:700;text-align:center;text-decoration:none}
.login-button:hover{background:var(--blue-strong)}
.login-card a{color:var(--cyan)}
.enrol-title{margin:20px 0 0;font-size:1rem}
.enrol-link{display:block;margin:10px 0;padding:11px;border:1px solid var(--line);border-radius:6px;background:var(--input-bg);color:var(--cyan);text-align:center;text-decoration:none}
.enrol-qr{display:block;width:min(240px,100%);height:auto;margin:12px auto;border-radius:6px;background:#fff}
.enrol-manual{margin:10px 0}
.enrol-manual summary{color:var(--cyan);font-size:.7rem;cursor:pointer}
.enrol-secret{display:block;margin:6px 0 14px;padding:11px;border:1px solid var(--line);border-radius:6px;background:var(--input-bg);color:var(--text);font:600 .9rem ui-monospace,monospace;letter-spacing:.08em;text-align:center;overflow-wrap:anywhere}

/* 骨格 */
/* 骨格。左メニューの構成はローカルNVRの画面に合わせる。両方を見る人が
   同じ場所で同じものを探せるようにするため。 */
.app-shell{display:grid;grid-template-columns:var(--sidebar) 1fr;min-height:100vh}
.sidebar{position:fixed;inset:0 auto 0 0;width:var(--sidebar);display:flex;flex-direction:column;background:var(--sidebar-bg);border-right:1px solid var(--line);z-index:10}
.sidebar-brand{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;padding:16px 14px 10px}
.sidebar-identity{display:grid;gap:8px;min-width:0}
.sidebar-logo{width:100%;max-width:190px}
.sidebar-identity .product-name{font-size:.62rem;letter-spacing:.06em;color:var(--cyan)}
.sidebar-clock{display:grid;gap:2px;margin:2px 20px 0;color:var(--text);font-size:1rem;font-weight:700;letter-spacing:.02em;line-height:1.3;text-align:center;white-space:nowrap}
.side-nav{padding:0 10px}
.side-nav p{margin:20px 10px 6px;font-size:.55rem;letter-spacing:.15em;color:var(--text-3);font-weight:800}
.nav-item{width:100%;display:flex;align-items:center;gap:11px;padding:11px 12px;border:0;border-radius:6px;background:transparent;color:var(--text-2);text-align:left;font-size:.76rem}
.nav-item:hover{background:var(--hover);color:var(--text)}
.nav-item.active{background:var(--active-bg);color:var(--text);box-shadow:inset 3px 0 var(--cyan)}
.nav-item span{width:17px;text-align:center;font-size:1rem}
.sidebar-footer{margin-top:auto;padding:14px 16px;border-top:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;gap:8px}
.sidebar-footer>div{display:grid;gap:2px;min-width:0}
.sidebar-footer small{color:var(--muted);font-size:.55rem}
.sidebar-footer strong{font-size:.66rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
#cloud-version{margin-top:3px;color:var(--text-3);font:600 .54rem ui-monospace,monospace;letter-spacing:.06em}
.sidebar-actions{display:flex;gap:6px;padding:0 14px 10px}
.sidebar-actions .tool-button{flex:1;text-align:center;text-decoration:none}
.sidebar-copyright{padding:0 10px 10px;color:var(--text-3);font-size:.49rem;line-height:1.45;text-align:center}
/* ローカルNVRと同じ枠付きの四角。透明のままだと、押せるものかどうかが
   見た目から分からない（ログアウトの印について実際に指摘を受けた）。 */
.icon-button{display:grid;place-items:center;width:31px;height:31px;padding:0;border:1px solid var(--line);border-radius:5px;background:var(--surface-2);color:var(--text-2);font-size:1rem}
.icon-button:hover{background:var(--hover);color:var(--text);border-color:var(--line-strong)}
#fullscreen{margin-left:auto}
/* グループ名はメールアドレスより一段控えめに。 */
.sidebar-footer #user-group{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.workspace{grid-column:2;min-width:0}
/* 画面内での最大化（右上の⛶）。**ブラウザの全画面(Fullscreen API)は使わない。**
   全画面にすると、他のウィンドウへ移っても window の blur が飛ばず、
   document.hasFocus() も真のまま、mouseleave も出ない。離席を知る合図が
   1つも届かなくなり、ライブが流れ続けて回線と記録を無駄にする（2026-08-25
   実測。2画面のWindowsで、画面2の窓をクリックしても止まらないと報告された）。
   窓の中で広げるだけなら焦点の判定はそのまま働く。**拠点の画面と同じ。**

   横幅はサイドバーと main の最大幅(1180px)の分だけ広がる。--sidebar を0に
   すると、列の割り振りも拡大中の札の位置も一緒に付いてくる。 */
.app-shell.stage{--sidebar:0px}
.app-shell.stage .sidebar{display:none}
.app-shell.stage .topbar{height:44px;padding:0 12px}
/* 見出しだけ畳む。≡は残す。消すと狭い画面でメニューを開けなくなる。 */
.app-shell.stage #view-title{display:none}
/* 素の main は中央に寄せて1180pxで頭打ち。最大化では画面いっぱいに使う。 */
.app-shell.stage main{max-width:none;padding:8px}
.app-shell.stage .live-data-notice{display:none}
/* 上の帯が56pxから44pxへ縮むので、拡大中の札の上端も合わせる。 */
.app-shell.stage .live-card.focused{inset:56px 12px 12px 12px}
/* 管理画面には左メニューが無い。それでも骨格の2列組みを当てていたため、
   見出し帯がメニュー1列ぶん(250px)に押し込められていた。 */
.toolbar{display:flex;align-items:center;gap:8px;min-width:0;margin-left:auto}
/* 長いメールアドレスは折り返せない。狭い画面では縮めて省略する。 */
#user-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.68rem}
.topbar{height:56px;display:flex;align-items:center;gap:10px;padding:0 16px;border-bottom:1px solid var(--line);background:var(--topbar-bg);position:sticky;top:0;z-index:5}
.topbar h2{margin:0;font-size:1rem;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.tool-button{border:1px solid var(--line);border-radius:5px;background:var(--surface-2);color:var(--text-2);padding:7px 10px;font-size:.67rem}
.tool-button:hover{color:var(--text);border-color:var(--line-strong)}
main{max-width:1180px;margin:auto;padding:16px}
.panel{display:none}
.panel.active{display:block}
.section-heading{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 0 16px}
.section-heading>div{min-width:0}
/* 見出しの右側に並べる操作。縮まないので、長い見出しの側が先に詰まる。 */
.heading-actions{display:flex;flex:0 0 auto;align-items:center;gap:8px}
.heading-actions select{height:32px;max-width:100%;padding:0 8px;border:1px solid var(--line);border-radius:5px;background:var(--input-bg);color:var(--text-2);font-size:.67rem}
.section-heading small{color:var(--cyan);font-size:.55rem;letter-spacing:.12em}
.section-heading h3{margin:3px 0 0;font-size:1.15rem}
.empty{padding:45px 20px;text-align:center;color:var(--text-3);font-size:.72rem}
/* 取れなかったことを、空だったことと同じ見た目にしない。.error より後に
   書かれた .empty の色が勝つため、組で上書きする。灰色のままだと「録画が
   無い」と読めてしまい、取り直す手が思い浮かばない。 */
.empty.error{color:var(--red)}

/* 一覧 */
.data-list{display:grid;gap:6px}
.list-row{display:grid;grid-template-columns:minmax(150px,1fr) auto;gap:14px;align-items:center;padding:14px 16px;border:1px solid var(--line);border-radius:5px;background:var(--surface);color:var(--text);text-align:left}
button.list-row:hover{background:var(--surface-2);border-color:var(--line-strong)}
.list-row>div{display:grid;gap:3px;min-width:0}
.list-row strong{font-size:.78rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.list-row small{color:var(--muted);font-size:.63rem}
/* .list-row>div{display:grid} のほうが強い。型を含めて上書きする。
   これを怠ると印が縦に積まれ、状態の上に容量が乗る。 */
.list-row>.row-marks{display:flex;align-items:center;gap:6px}
.row-marks .status-pill{flex:0 0 auto}
.camera-chip-warning{border-color:var(--warn-line);color:var(--warn-text)}
.status-pill{padding:3px 9px;border-radius:10px;font-size:.62rem;white-space:nowrap}
.status-pill.online{color:var(--green);background:var(--ok-bg)}
/* 数字を淡く出す用。良し悪しの色を持たせない。 */
.status-pill.plain{color:var(--text-2);background:var(--surface-2)}
.status-pill.offline{color:var(--amber);background:var(--warn-bg)}
.offline-banner{margin:0 0 12px;padding:9px 12px;border:1px solid var(--warn-line);border-radius:5px;background:var(--warn-bg);color:var(--warn-text);font-size:.7rem;line-height:1.5}
/* 拠点の異常。offline-banner と同じ見え方に揃える。異常があるときだけ出る
   ので、出ていること自体が知らせになる。 */
#site-health{margin:0 0 12px;padding:9px 12px;border:1px solid var(--warn-line);border-radius:5px;background:var(--warn-bg);font-size:.7rem;line-height:1.6}
#site-health h4{margin:0 0 5px;font-size:.74rem;color:var(--warn-text)}
#site-health p{margin:0 0 5px;white-space:pre-line;word-break:break-word}
/* 次にすることは、状況の説明より目立たせる。読む人が動けるのはここだけ。 */
#site-health .site-health-action{color:var(--text);font-weight:600}
#site-health summary{cursor:pointer;color:var(--text-2)}
#site-health ul{margin:5px 0 0;padding-left:1.1em}
#site-health li{margin:0 0 3px;word-break:break-word}

/* タブ */
.tab-bar{display:flex;gap:4px}
.tab{border:1px solid var(--line);border-radius:5px;background:transparent;color:var(--text-2);padding:8px 14px;font-size:.72rem}
.tab.active{background:var(--active-bg);color:var(--text);border-color:var(--line-strong)}
.tab-panel{display:none}
.tab-panel.active{display:block}

/* ライブ */
.chip-row{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}
.camera-chip{border:1px solid var(--line);border-radius:14px;background:var(--surface-2);color:var(--text-2);padding:7px 13px;font-size:.7rem}
.camera-chip:hover{color:var(--text);border-color:var(--line-strong)}
.camera-chip.playing{border-color:var(--green);color:var(--green)}
/* 拠点の画面と同じく、横に並ぶのは2つまで。auto-fillだと幅に合わせて数が
   増えるので、大きな画面ほど1つあたりが小さくなるという逆の結果になる。 */
.live-grid{display:grid;grid-template-columns:repeat(2,minmax(280px,1fr));gap:10px;margin-top:8px}
@media(max-width:1000px){.live-grid{grid-template-columns:1fr 1fr}}
/* 分割の型は枠の数で決まる（app.js の liveGridLayout）。**拠点の画面と同じ
   規則にする。** 両方を行き来する利用者に、操作を覚え直させないため。
   狭い画面では従枠が読めない大きさになるので、広い画面だけに効かせる。 */
@media(min-width:1001px){
.live-grid[data-layout=single]{grid-template-columns:minmax(280px,1fr)}
/* 行が2つ以上になる型は、枠の並びを画面の高さへ収める。札なりの高さで積むと
   下の行が画面の外へ出て、同時に見張るという目的を満たさない。ここの計算は
   知らせの帯が無いときの値で、app.js が実際の位置から測って上書きする。
   役割変数（var(--…)）を使わないのは、高さは色ではなく、混ぜると配色の検査に
   引っかかるため（実際に落ちた）。 */
.live-grid[data-layout=quad],.live-grid[data-layout="1+5"],.live-grid[data-layout="1+7"]{align-content:stretch;grid-auto-rows:minmax(0,1fr);height:calc(100vh - 150px)}
.live-grid[data-layout=quad] .live-card,.live-grid[data-layout="1+5"] .live-card,.live-grid[data-layout="1+7"] .live-card{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%}
.live-grid[data-layout=quad] .frame,.live-grid[data-layout="1+5"] .frame,.live-grid[data-layout="1+7"] .frame{aspect-ratio:auto;min-height:0}
/* 主枠＋従枠。5枠以上は等分にすると1枠あたりが小さくなるだけなので、先頭の枠を
   大きく取り、残りを右と下へ小さく並べる。 */
.live-grid[data-layout="1+5"]{grid-template-columns:repeat(3,minmax(0,1fr))}
.live-grid[data-layout="1+5"]>.live-slot:first-child{grid-area:1/1/3/3}
.live-grid[data-layout="1+7"]{grid-template-columns:repeat(4,minmax(0,1fr))}
.live-grid[data-layout="1+7"]>.live-slot:first-child{grid-area:1/1/4/4}
/* 行が1つの型は引き伸ばさない。16:9のまま収まるならそのまま見せる。ただし
   置かれる位置が低いと札なりの高さでも画面の下から出るので、そのときだけ
   app.js が fitted を付け、quad と同じ縮め方に切り替える。 */
.live-grid.fitted{align-content:stretch;grid-auto-rows:minmax(0,1fr)}
.live-grid.fitted .live-card{display:grid;grid-template-rows:auto minmax(0,1fr) auto;height:100%}
/* 縮めても16:9を保つ。**縦横比を崩してはいけない。** 映像は
   object-fit:cover なので、枠が横長になるとその分だけ上下が切り落とされ、
   カメラが焼き込んだ日時（左上）が消える。実測で上から14.9%が失われて
   いた（2026-08-25 報告）。高さを枠に合わせ、幅は縦横比から決めて中央に
   置く。左右に余白は出るが、映像は1画素も欠けない。 */
.live-grid.fitted .frame{min-height:0;height:100%;width:auto;max-width:100%;margin:0 auto}
}
.live-card{border:1px solid var(--line);border-radius:5px;overflow:hidden;background:var(--card-bg)}
.live-card .frame{aspect-ratio:16/9;position:relative;background:radial-gradient(circle,#172936,#080d12 65%)}
/* **映像を切らない。** cover は枠を埋めるために画像を切り落とす。枠は画面へ
   収めるために16:9より横長になるので、上下が消える。カメラは日時を映像へ
   焼き込んでおり、左上にあるため真っ先に失われる。クラウドの4枠では上から
   21%が切れていた（2026-08-25 実測・報告）。contain なら左右に帯が出るが、
   映っているものは1画素も欠けない。監視の映像は証拠なので、埋まりの良さ
   より欠けないことを採る。拡大中の札は元から contain だった。 */
.live-card video{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;background:#000}
.live-card .bar{color:var(--card-text);display:flex;align-items:center;justify-content:space-between;gap:8px;padding:7px 10px;background:var(--card-bar-bg);border-top:1px solid var(--card-line);font-size:.68rem}
.live-card .bar strong{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* 拠点の画面と同じ作り。名前は札の頭に置き、そこを掴む。下の帯は停止と
   拡大だけなので右へ寄せる。 */
.live-card-heading{display:flex;align-items:center;gap:7px;min-height:38px;padding:7px 11px;border-bottom:1px solid var(--card-line);background:var(--card-bar-bg);color:var(--card-text);cursor:grab;touch-action:none;user-select:none}
.live-card-heading:active{cursor:grabbing}
.live-card-heading strong{display:-webkit-box;min-width:0;font-size:.78rem;line-height:1.35;overflow:hidden;overflow-wrap:anywhere;-webkit-box-orient:vertical;-webkit-line-clamp:2}
/* 札を閉じる印。名前がどれだけ長くても縮まない位置に置く。掴む場所の中に
   在るので、指の縦振りを掴みに取られないよう touch-action を戻す。 */
.live-card-close{flex:0 0 auto;margin-left:auto;width:28px;height:28px;font-size:.9rem;touch-action:auto;cursor:pointer}
.live-card .bar{justify-content:flex-end}
.live-drag-grip{flex:0 0 auto;color:var(--text-3);font-size:.82rem;letter-spacing:-.25em}
/* 配色を切り替えても効くよう、色は役割変数から取る（直書きは禁じ手）。 */
.live-card.dragging{opacity:.82;border-color:var(--cyan)}
/* 枠は3つ固定。空き枠は場所だけ取って隠れており、運んでいる間だけ受け皿と
   して姿を見せる。1台しか開いていなくても動かせるのはこのため。 */
.live-slot{position:relative;min-width:0}
.live-slot.empty-slot{min-height:1px;visibility:hidden}
.live-grid.reordering .live-slot.empty-slot{visibility:visible;border:1px dashed var(--line-strong);border-radius:5px;background:var(--surface-2)}
.live-grid.reordering .live-slot.empty-slot:after{content:"ここに移動";display:grid;height:100%;place-items:center;color:var(--text-2);font-size:.95rem;font-weight:700}
/* 運んでいる先は、空でも映像が動いていても同じ印を出す。映像のある枠は札が
   覆いかぶさるので、枠の縁や内側の影は見えない。札そのものの縁を光らせ、
   文字は覆いに載せる。 */
.live-grid.reordering .live-slot.drop-target{visibility:visible;border-color:var(--cyan)}
.live-grid.reordering .live-slot.drop-target>.live-card{border-color:var(--cyan);box-shadow:0 0 0 2px var(--focus-ring)}
.live-grid.reordering .live-slot.drop-target:after{content:"ここに移動";position:absolute;inset:0;z-index:5;display:grid;place-items:center;border-radius:5px;background:var(--backdrop);color:var(--on-accent);font-size:1.05rem;font-weight:700;pointer-events:none}
.live-data-notice{display:flex;align-items:center;gap:7px;min-width:0;margin:0 0 10px;padding:8px 11px;border:1px solid var(--warn-line);border-radius:5px;background:var(--warn-bg);color:var(--warn-text);font-size:.68rem;line-height:1.5}
.live-data-notice span{display:grid;flex:0 0 auto;width:17px;height:17px;place-items:center;border-radius:50%;background:var(--amber);color:var(--on-accent);font-size:.65rem;font-weight:900}
.viewer-total{font-size:1.3rem;color:var(--green)}
.build-tag{margin-left:9px;color:var(--muted);font-weight:600;letter-spacing:.02em;font-family:ui-monospace,monospace;white-space:nowrap}
.live-notice{margin:0 0 8px;padding:7px 10px;border:1px solid var(--info-line);border-radius:5px;background:var(--info-bg);color:var(--info-text);font-size:.68rem}
.live-card .bar-buttons{display:flex;flex:0 0 auto;gap:4px}
/* 1段目の拡大。左メニューと見出し帯は見えたままにする。大きく見ながら
   カメラを移ったり一覧へ戻ったりできるようにするため。ローカルNVRと同じ。 */
.live-card.focused{position:fixed;inset:70px 18px 18px calc(var(--sidebar) + 18px);z-index:20;display:grid;grid-template-rows:auto minmax(0,1fr) auto}
/* 拡大中の札は画面へ固定する。上の分割で入れた height:100% を効かせない。
   inset で上下が決まっているので、高さを与えると下へはみ出す。 */
@media(min-width:1001px){.live-grid[data-layout] .live-card.focused{height:auto}}
.live-card.focused .frame{aspect-ratio:auto;min-height:0}
.live-card.focused video{object-fit:contain}
.live-card.focused .focus-toggle{background:var(--card-hover);color:var(--card-text)}
.live-card .bar button{color:var(--card-text-2);border:0;border-radius:4px;background:transparent;color:var(--text-2);padding:4px 8px;font-size:.66rem}
.live-card .bar button:hover{background:var(--card-hover);color:var(--card-text)}
.live-status{position:absolute;inset:auto 8px 8px 8px;padding:5px 8px;border-radius:4px;background:#05090ccc;color:#dce7ee;font-size:.64rem;text-align:center}
.live-connecting{position:absolute;inset:50% auto auto 50%;transform:translate(-50%,-50%);display:grid;gap:7px;justify-items:center;width:min(200px,66%);padding:10px 12px;border-radius:4px;background:#05090ccc;color:#dce7ee;font-size:.64rem}
.live-connecting .track{width:100%;height:4px;border-radius:2px;background:#ffffff26;overflow:hidden}
.live-connecting .fill{width:0;height:100%;border-radius:2px;background:#22b8cf;transition:width .2s linear}

/* 録画 */
.recording-filter{display:grid;grid-template-columns:minmax(140px,1fr) repeat(2,minmax(130px,.7fr)) auto auto;gap:8px;align-items:end;margin:0 0 10px;padding:10px;border:1px solid var(--line);border-radius:5px;background:var(--surface)}
.recording-filter label{display:grid;gap:4px;min-width:0;color:var(--muted);font-size:.62rem}
/* 幅を親に合わせ、縮むことを許す。既定では入力欄も選択欄も「中身より狭く
   ならない」ため、長いカメラ名を持つ選択欄や、iOSで幅を持つ日付欄が、
   そのまま列を押し広げて画面からはみ出す。NVR側は select に width:100% を
   置いており、クラウドだけ漏れていた。 */
.recording-filter select,.recording-filter input{width:100%;min-width:0;height:32px;padding:0 8px;border:1px solid var(--line);border-radius:4px;background:var(--input-bg);color:var(--text)}
.recording-filter button{height:32px}
.recording-row{grid-template-columns:minmax(0,1fr) auto}
.recording-tags{display:flex;gap:6px;flex-wrap:wrap}
.tag{padding:2px 7px;border-radius:9px;background:var(--tag-bg);color:var(--cyan);font-size:.58rem}
.tag.protected{background:var(--tag-warn-bg);color:var(--amber)}
/* ページ数が増えると1行に並びきらない。折り返さないと画面の外へ出て、
   横スクロールが生まれる。実機(iPhone 16 Pro Max 縦・440px)で13ページ
   並べたときに左右へはみ出した。 */
.pagination{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:5px;margin:14px 0 4px}
.pagination button{min-width:32px;height:30px;padding:0 9px;border:1px solid var(--line);border-radius:5px;background:var(--surface-2);color:var(--text-2);font-size:.66rem}
.pagination button.active{border-color:var(--blue);background:var(--blue);color:var(--on-accent)}
.pagination button:disabled{cursor:not-allowed;opacity:.35}
.page-gap,.page-summary{color:var(--muted);font-size:.68rem}
.page-summary{margin-left:7px}

.recording-row{width:100%}
/* 行は器で、押せるのは中の「開く」と「保存」。器のままだと当たったことが
   分からないので、触れている合図は行に出す。 */
.recording-row:hover{background:var(--surface-2);border-color:var(--line-strong)}
/* .list-row>div{display:grid} のほうが強い。型ではなく組で上書きしないと
   印と保存が縦に積まれ、行の高さが倍になる。.row-marks と同じ手当て。 */
.list-row>.recording-actions{display:flex;align-items:center;justify-content:flex-end;
  flex-wrap:wrap;gap:8px;min-width:0}
/* 拠点の画面の「保存」と同じ見た目。並べて使う人が同じものだと分かる。 */
.download-button{height:30px;padding:0 10px;border:1px solid var(--line);border-radius:4px;
  background:var(--surface-2);color:var(--text-2);font-size:.62rem;white-space:nowrap;
  display:inline-flex;align-items:center}
.download-button:hover{color:var(--text);border-color:var(--line-strong)}
.download-button:disabled{cursor:progress;opacity:.55}
/* 開く部分はボタンだが、見た目は行の一部のまま。器の背景と枠を持たせない。 */
.recording-open{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:center;min-width:0;
  padding:0;border:0;background:none;color:inherit;text-align:left;font:inherit}
/* 時刻と大きさは縦に積む。かつては .list-row>div が効いていたが、開く部分を
   ボタンで包んだことで届かなくなり、「10:01:00 正面ゲート30秒 / 12.9MB」と
   繋がって出た。包みを足したら、中への指定も付け直すこと。 */
.recording-open>div{display:grid;gap:3px;min-width:0}
.recording-thumbnail{width:112px;height:63px;border:1px solid var(--thumb-line);border-radius:4px;background:var(--thumb-bg);object-fit:cover}
/* 取れるまでは枠だけ残す。消すと行の高さが変わって一覧が揺れる。 */
.recording-thumbnail.loading{opacity:.45}
#player-dialog{width:min(900px,calc(100% - 24px));padding:15px;border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--text)}
#player-dialog::backdrop{background:var(--backdrop)}
/* 題名は長くなる。閉じるボタンは縮めず、文字も折り返させない。
   潰れると1文字ずつ縦に並び、押す場所が分からなくなる。 */
.player-heading{display:flex;align-items:start;justify-content:space-between;gap:10px;padding:2px 2px 12px}
.player-heading>div{min-width:0}
/* 再生窓の音の出し入れ。ライブの札と同じ印・同じ手つきにする。
   ブラウザ標準の音量つまみは印の形も置き場所もブラウザ任せで、ライブと
   揃えられない。標準のつまみを隠し、こちらの印だけを出す。
   （対象ブラウザは Chrome と Safari。どちらもこの指定が効く） */
/* ライブに要らない操作だけ消す。**再生・停止と再生位置は消す。** 生の
   映像を止めても繋ぎ直す作りなので、押しても効かない操作になる。映像を
   大きくするのは札の⛶（拡大）と、上の帯の⛶（画面の最大化）が持っている。
   **どちらもブラウザーの全画面ではない**（.stage を見よ）。スピーカーは
   録画の再生窓と同じ位置に残す。 */
.live-card video::-webkit-media-controls-play-button,
.live-card video::-webkit-media-controls-timeline,
.live-card video::-webkit-media-controls-current-time-display,
.live-card video::-webkit-media-controls-time-remaining-display,
.live-card video::-webkit-media-controls-fullscreen-button,
.live-card video::-webkit-media-controls-toggle-closed-captions-button,
/* ⋮（はみ出しの献立）も消す。拠点の画面と同じ扱い。中身はライブに対して
   意味が無いか、押させたくない操作しか入っていない。 */
.live-card video::-webkit-media-controls-overflow-button{display:none}

#player-close{flex:0 0 auto;white-space:nowrap}
.player-heading small{color:var(--cyan);font-size:.57rem;letter-spacing:.13em}
.player-heading h3{margin:4px 0 0;font-size:1rem}
#recording-player{display:block;width:100%;max-height:70vh;background:#000;border-radius:5px}
#player-meta{margin:8px 0 0}

/* 一覧の上の小さな道具立て。「いつ時点か」と「更新」を同じ行に置く。
   拠点の画面は節の見出しの右端に「更新」を置いているが、クラウドの節に
   見出しは無い（画面の名前は上の帯に出る）ので、その代わりの置き場。 */
.list-toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;margin:0 0 8px}
.list-toolbar p{margin:0}

/* 監査 */
/* 見出し行。列は行と同じ幅にする。ずれると読み手が見出しを頼れない。 */
.audit-table-header{display:grid;grid-template-columns:170px minmax(160px,1fr) minmax(140px,1fr) auto;gap:14px;padding:7px 16px;color:var(--muted);font-size:.61rem;font-weight:700;letter-spacing:.04em}
.audit-row{grid-template-columns:170px minmax(160px,1fr) minmax(140px,1fr) auto}
.audit-row time{color:var(--text-2);font-size:.66rem}
/* 失敗の理由には、下線でつないだ長い語や長いメールアドレスが混じる。
   格子の升目は既定で中身より狭くならず、折り返しの機会も無いため、
   その1語が升目からあふれる。手のひらの幅で実測164px、隠れて読めない。 */
.audit-row small{min-width:0;overflow-wrap:anywhere}
.audit-result{padding:3px 7px;border-radius:10px;font-size:.59rem}
.audit-result.success{color:var(--green);background:var(--ok-bg)}
.audit-result.failure{color:var(--red);background:var(--bad-bg)}

/* 管理画面 */
.admin-section{margin-bottom:34px}
.admin-form{margin-top:16px;padding:18px;border:1px solid var(--line);border-radius:6px;background:var(--surface)}
.admin-form h4{margin:0 0 14px;font-size:.85rem}
.admin-fields{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:14px}
.admin-form label{display:grid;gap:6px;color:var(--muted);font-size:.72rem}
.admin-form input,.admin-form select{padding:10px;border:1px solid var(--line);border-radius:5px;background:var(--input-bg);color:var(--text);outline:none}
.admin-form input:focus,.admin-form select:focus{border-color:var(--blue);box-shadow:0 0 0 3px var(--focus-ring)}
.admin-form label small{font-size:.62rem}
/* 決定ボタンは右下。ローカルNVRの対話窓（.dialog-actions）と同じ流儀。 */
.admin-form>button{display:block;margin-left:auto;margin-top:16px;border:0;border-radius:5px;background:var(--blue);color:var(--on-accent);padding:11px 20px;font-weight:700;font-size:.75rem}
.admin-form>button:hover{background:var(--blue-strong)}
.admin-marks{display:flex;flex-wrap:wrap;gap:5px;justify-content:flex-end}
.admin-subheading{margin:22px 0 10px;font-size:.8rem}
.admin-inline-form{display:flex;gap:8px;margin-top:10px}
/* 入力欄が伸びてボタンが右端に付く。ボタンだけ左に浮くのを避ける。 */
.admin-inline-form select,.admin-inline-form input{flex:1;min-width:0;padding:8px;border:1px solid var(--line);border-radius:5px;background:var(--input-bg);color:var(--text)}
.admin-inline-form input:focus{border-color:var(--blue);box-shadow:0 0 0 3px var(--focus-ring);outline:none}
.admin-actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.admin-actions button:disabled{opacity:.45;cursor:not-allowed}
.admin-warning{margin:14px 0 0;padding:9px 12px;border:1px solid var(--warn-line);border-radius:5px;background:var(--warn-bg);color:var(--warn-text);font-size:.68rem;line-height:1.5}
/* 対話窓の外装はここでまとめて指定する。IDごとに書くと、次に足したものが
   また漏れる（実際 site-dialog と password-dialog が漏れ、ブラウザ既定の
   見た目で出ていた）。 */
#site-dialog,#group-dialog,#user-dialog,#secret-dialog,#password-dialog{width:min(620px,94vw);border:1px solid var(--line);border-radius:9px;background:var(--surface);color:var(--text);padding:18px}
#site-dialog::backdrop,#group-dialog::backdrop,#user-dialog::backdrop,#secret-dialog::backdrop,#password-dialog::backdrop{background:var(--backdrop)}

/* 幅だけで判定すると、iPhoneでもPCの画面になる場面がある。
   - 横持ち: iPhone 14 は 750px で、720pxの境界を超える
   - Safariの「デスクトップ用Webサイトを表示」: 980px を名乗る
   どちらも指で操作する端末なので、pointer:coarse で拾う。1024pxまでに
   留めるのは、大きなタブレットやPCの画面まで巻き込まないため。
   PCの窓を狭くした場合は coarse ではないので、従来どおり幅だけで決まる。 */
@media (max-width:720px), (pointer:coarse) and (max-width:1024px){
  main{padding:10px}
  .section-heading{flex-wrap:wrap}
  .recording-filter{grid-template-columns:1fr 1fr}
  /* カメラは名前が長い。2列のうち1列に押し込むと読めないので全幅に置く。
     残りは「開始日時|終了日時」「検索|解除」がそれぞれ1行に収まる。
     ボタンを1/-1のままにすると、2つになった途端に2行を占める。 */
  .recording-filter label:first-child{grid-column:1/-1}
  .audit-row{grid-template-columns:1fr;gap:5px}
  /* 行が1列に畳まれるので、見出しの位置と合わなくなる。拠点の画面と同じく
     畳んだときは隠す。 */
  .audit-table-header{display:none}
  .live-grid{grid-template-columns:1fr}
  .admin-fields{grid-template-columns:1fr}
  .admin-marks{justify-content:flex-start}

  /* 左メニューは畳んでおき、必要なときだけ引き出す。狭い画面で常時
     出していると、中身の幅が残らない。 */
  .mobile-only{display:grid!important}
  .app-shell{display:block}
  .sidebar{transform:translateX(-100%);transition:transform .2s;width:min(280px,84vw)}
  .app-shell.menu-open .sidebar{transform:none}
  .workspace{grid-column:auto}
  .sidebar-footer{padding-bottom:max(14px,env(safe-area-inset-bottom))}
  .topbar{height:auto;min-height:56px;padding:8px 12px;flex-wrap:wrap}
  .topbar h2{font-size:.9rem}
  /* 操作は、収まらなければ次の行へ落とす。押し込めると右端が切れる。 */
  .toolbar{flex:1 1 100%;margin-left:0;justify-content:flex-end;flex-wrap:wrap}

  /* 指で押す部分。細いと隣を触ってしまう。 */
  .toolbar .tool-button,.tab,.camera-chip,.download-button{min-height:44px;white-space:nowrap}
  .tab,.camera-chip{display:inline-flex;align-items:center}
  .tab-bar{flex-wrap:wrap}
  .live-card .bar button{min-height:40px}
  .live-card.focused{inset:10px}
  .pagination button{min-height:44px}

  /* ここから下は、ローカルNVRで同じ症状を潰したときの一式をそのまま持って
     きたもの。画面の外へ出る原因はたいてい「それ以上縮まない箱」なので、
     器を残らず縮むようにしておく。節を足すたびに個別に直すのではなく、
     入れ物をまとめて押さえる。 */
  .app-shell,.workspace,main,.panel,.data-list,.list-row,.live-grid,.live-card,
  .admin-section,.admin-form,.admin-fields,.tab-bar,
  .section-heading,.section-heading>div{width:100%;max-width:100%;min-width:0}
  .topbar{width:100%;max-width:100vw;overflow:hidden}
  .list-row{grid-template-columns:minmax(0,1fr) auto;overflow:hidden}
  .pagination{width:100%;max-width:100%;padding:0 2px}
  .admin-inline-form{flex-wrap:wrap}
  .admin-form input,.admin-form select,
  .admin-inline-form select,.admin-inline-form input{width:100%;min-width:0}
  .login-shell{padding:12px}
  .login-card{width:100%;padding:24px}

  /* 対話窓は幅指定を忘れると中身なりに広がる。IDごとに書くと、次に足した
     ものがまた漏れる。ここで全部まとめて画面内に収める。 */
  dialog,#player-dialog,#user-dialog,#secret-dialog{
    width:calc(100% - 16px);max-width:calc(100vw - 16px)}
}

/* iOSは16pxより小さい入力欄に触れるとページ全体を拡大する。拡大は自動では
   戻らないので、以後ずっと画面からはみ出して見える。ローカルNVRと同じ扱い。 */
@media (hover:none) and (pointer:coarse){
  input,select,textarea{font-size:16px!important}
}


/* ---- 配色の種類 --------------------------------------------------------
 *
 * 管理者が選ぶ。`<html data-theme="…">` で切り替わる。
 * 既定（指定なし）はローカルNVRと同じ暗い配色。
 *
 * 映像の入れ物(--card-*)は、明るい配色でも暗いままにしてある。映像の
 * 周りが明るいと、暗い画面のカメラが見づらくなるため。
 */
:root[data-theme="cream"]{
  --bg:#f7f2e8;--surface:#fffcf6;--surface-2:#efe7d7;--line:#ddd3c0;
  --line-strong:#c4b89f;--text:#2b2620;--text-2:#5f584b;--text-3:#6d6558;
  --muted:#7b7365;--sidebar-bg:#f0e7d6;--topbar-bg:#fffcf6e8;--input-bg:#fffdf8;
  --hover:#eae2d2;--active-bg:#e3d7bd;--shadow:#00000018;--backdrop:#2b262099;
  --login-glow:#efe4cf;--focus-ring:#2563c933;--on-accent:#fffdf8;
  --cyan:#0d6f80;--blue:#2563c9;--blue-strong:#1d4fa8;
  --green:#1d7a58;--amber:#8a5e10;--red:#a83228;
  --warn-line:#cba757;--warn-text:#6f4c0c;--warn-bg:#f1b44c26;
  --info-line:#8fbcc6;--info-text:#0b5b6b;--info-bg:#22b8cf1f;
  --ok-bg:#1d7a5820;--bad-bg:#a832281a;
  --tag-bg:#e5dcc7;--tag-warn-bg:#f0e0bd;
  --thumb-bg:#eee7da;--thumb-line:#d3c8b2;
}

:root[data-theme="paper"]{
  --bg:#f4f6f8;--surface:#ffffff;--surface-2:#e9edf1;--line:#d5dce3;
  --line-strong:#b6c0ca;--text:#1f2933;--text-2:#52606d;--text-3:#5b6572;
  --muted:#616e7c;--sidebar-bg:#eef1f5;--topbar-bg:#ffffffe8;--input-bg:#ffffff;
  --hover:#e3e8ee;--active-bg:#dbe4ef;--shadow:#0f172a14;--backdrop:#1f293399;
  --login-glow:#dde7f2;--focus-ring:#2563c933;--on-accent:#ffffff;
  --cyan:#0e7490;--blue:#2563eb;--blue-strong:#1d4ed8;
  --green:#15803d;--amber:#a16207;--red:#b91c1c;
  --warn-line:#d9b45f;--warn-text:#7a5406;--warn-bg:#f1b44c26;
  --info-line:#9fc6d4;--info-text:#0e6178;--info-bg:#22b8cf1f;
  --ok-bg:#15803d1c;--bad-bg:#b91c1c14;
  --tag-bg:#e2e8f0;--tag-warn-bg:#fdefc8;
  --thumb-bg:#e8edf2;--thumb-line:#cbd5e0;
}

:root[data-theme="slate"]{
  --bg:#1b1f27;--surface:#232833;--surface-2:#2d333f;--line:#3b4250;
  --line-strong:#525b6b;--text:#eef1f6;--text-2:#b6bdc9;--text-3:#7c8698;
  --muted:#98a1b0;--sidebar-bg:#171b22;--topbar-bg:#232833e8;--input-bg:#171b22;
  --hover:#2d333f;--active-bg:#33405a;--shadow:#0009;--backdrop:#000a;
  --login-glow:#2a3550;--focus-ring:#6f9dff33;--on-accent:#ffffff;
  --cyan:#4cc4dc;--blue:#6f9dff;--blue-strong:#5a86e6;
  --green:#4ecb96;--amber:#f0b95c;--red:#f47a72;
  --warn-line:#6f5c33;--warn-text:#e0c286;--warn-bg:#f0b95c14;
  --info-line:#345a63;--info-text:#9ad5e2;--info-bg:#4cc4dc16;
  --ok-bg:#4ecb9616;--bad-bg:#f47a7216;
  --tag-bg:#333b49;--tag-warn-bg:#463c22;
  --thumb-bg:#161a21;--thumb-line:#39414f;
}
