/* ==========================================================================
   viewer.css — /3d_viewer (3D Viewer)
   Loaded AFTER new_preuploader.css; the page shares the /workspace header and
   theme tokens by putting both classes on body (class="new_preuploader viewer").
   Every rule here is scoped under body.new_preuploader.viewer so specificity
   beats both single.css and the shared file regardless of load order.
   Light theme: no visual change from the single.css look.
   Dark theme (body.theme-dark): the --np tokens from new_preuploader.css.
   CSP: style-src 'self' — keep all styles in this file, no inline styles.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Page tokens (body colour re-pinned: new_preuploader.css makes it white)
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer {
    --vw-text: #000000;
    --vw-text-soft: #021235;
    --vw-panel-border: #95b1df3b;
    --vw-button-bg: #22222211;
    --vw-button-active-bg: #ffffff30;
    --vw-control-bg: #22222219;
    --vw-switch-bg: #ffffff;
    --vw-switch-hover-bg: #eef3fb;
    --vw-switch-border: #95b1df;
    --vw-switch-text: #333333;
    --vw-switch-active-bg: #3e64ad;
    --vw-switch-active-text: #ffffff;
    color: var(--vw-text);
}
body.new_preuploader.viewer.theme-dark {
    --vw-text: var(--np-text);
    --vw-text-soft: var(--np-text-soft);
    --vw-panel-border: var(--np-border-color);
    --vw-button-bg: rgba(255, 255, 255, 0.06);
    --vw-button-active-bg: rgba(255, 255, 255, 0.14);
    --vw-control-bg: rgba(255, 255, 255, 0.06);
    --vw-switch-bg: var(--np-row-bg);
    --vw-switch-hover-bg: rgba(255, 255, 255, 0.10);
    --vw-switch-border: var(--np-border-color);
    --vw-switch-text: var(--np-text);
    --vw-switch-active-bg: var(--np-accent);
    --vw-switch-active-text: var(--np-accent-contrast);
}

/* ルート要素の背景 (single.css の --main-background を np トークンに置換) */
body.new_preuploader.viewer #pointcloud_viewer {
    /* 背景は new_preuploader.css の body::before (固定レイヤー) が描く */
    background-image: none;
}
@media (max-width: 680px) and (orientation: portrait) {
    body.new_preuploader.viewer:not(.theme-dark) {
        --np-page-bg: linear-gradient(#1764ff, #99c2bc);
    }
}
/* single.css の "#pointcloud_viewer header p { padding-right: 5.5rem }" は
   旧ヘッダー (タイトル中央寄せ) 用。共通ヘッダーでは不要 */
body.new_preuploader.viewer .np_header #site_title {
    padding-right: 0;
}
/* 旧ヘッダー (#viewer_site_header, 下余白なし) より共通ヘッダーの方が
   下余白が大きいので、キャンバス上の margin をその分詰める */
body.new_preuploader.viewer #pointcloud_viewer #viewer_canvas_layout {
    margin-top: 2rem;
}
/* 使い方 (ヘッダー直下)。非表示時にレイアウトが変わらないよう余白 0 */
body.new_preuploader.viewer .np_main.vw_top {
    padding-bottom: 0;
}
body.new_preuploader.viewer .vw_howto {
    text-align: left;
    margin-bottom: 1.4rem;
}
body.new_preuploader.viewer .vw_howto p {
    margin: 0 0 0.7rem;
    line-height: 1.5;
}
/* 対応形式の一覧: ファイルタイプごとに 1 行 */
body.new_preuploader.viewer .vw_howto_formats {
    margin: 0 0 0.9rem;
    padding-left: 1.4rem;
    line-height: 1.5;
}
body.new_preuploader.viewer .vw_howto_formats li {
    margin: 0.1rem 0;
}

/* --------------------------------------------------------------------------
   Canvas / controls (both themes via tokens)
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer #three_canvas,
body.new_preuploader.viewer #pointcloud_viewer #three_canvas {
    border-color: var(--vw-panel-border);
}
/* 空のキャンバス (最初の画面・データセットの Display 前)。/manual_pipeline の
   空キャンバスと同じ色・形 (manual_pipeline.css と同じ指定): 変換行 (np_row)
   と同じ半透明の下地 + 細い bevel 縁 + 角丸。ライト / ダークとも同じトークン */
body.new_preuploader.viewer .empty_three_canvas {
    background: var(--np-row-bg);
    border: var(--np-row-border);
    border-color: var(--np-bevel-colors);
    border-radius: var(--np-radius);
}
/* 空のキャンバス上のゲートの Display ボタン。角は /manual_pipeline と同じ 7px */
body.new_preuploader.viewer .model_load_button {
    border-radius: 7px;
}
/* ファイルを開くボタン。下地・縁・角は /manual_pipeline のダウンロードボタン
   (manual_pipeline.css の mp_download_button。本ページの vw_download_button
   も同じ) と同じ: 変換行 (np_row) と同じ半透明の下地 + 細い bevel 縁
   (ライト 3px outset / ダーク 3px solid の明暗 4 辺) + 角 7px、ホバーで
   明るく、押下中は縁を凹ませる */
body.new_preuploader.viewer .upload_panel {
    background-color: var(--np-row-bg);
    border: var(--np-row-border);
    border-color: var(--np-bevel-colors);
    border-radius: 7px;
}
body.new_preuploader.viewer .upload_panel:hover {
    background-color: var(--vw-button-active-bg);
}
body.new_preuploader.viewer .upload_panel:active {
    border-color: var(--np-bevel-colors-inset);
}
/* ファイルを開くボタンのアイコン (開いたフォルダ、OpenFolderIcon)。色は他ページ
   共用の .upload_image_button (single.css の upload.png) と同じ (ダークは下の
   明るい色)。inline-block だと下に行の余白 (約 3.4px) が付き、ボタンの内側で
   絵柄が上寄りになるので block にし、枠の高さ 38px (絵柄の上下 11px ずつ) で
   ボタンの内側の上下中央に置く */
body.new_preuploader.viewer .vw_open_folder_icon {
    display: block;
    width: 45px;
    height: 38px;
    cursor: pointer;
    fill: #444647;
}
body.new_preuploader.viewer #file_choice + #file_choice_label::before {
    background-color: var(--vw-button-bg);
    border-color: var(--vw-panel-border);
}
/* ファイルを開くボタンの行 (vw_open_row): ボタンを従来どおり中央に保ったまま、右に
   「この端末で開ける大きさ」の i ツールチップ (DisplayLimitsTip、他ページと同じ
   InfoTip の見た目) を置く。左に i と同じ幅 (18px) の空きを取って釣り合わせる。
   ボタンの上下の余白 8px は single.css の #pointcloud_upload_block_layout と同じ */
body.new_preuploader.viewer .vw_open_row {
    display: grid;
    grid-template-columns: 18px auto 18px;
    column-gap: 0.5rem;
    justify-content: center;
    align-items: center;
}
body.new_preuploader.viewer .vw_open_row .upload_panel {
    grid-column: 2;
    margin: 8px 0;
}
body.new_preuploader.viewer .vw_open_row .np_run_options_wrap {
    grid-column: 3;
}
/* ツールチップの中身: 「Files up to about」の小見出しの下の表は区切り線を重ねない。
   注記 (メモリの出どころ・ローカルファイルは判定しない) は折り返す */
body.new_preuploader.viewer .vw_limits_subtitle {
    margin-top: 0.4rem;
}
body.new_preuploader.viewer .vw_limits_subtitle + .np_run_options_times {
    margin-top: 0;
    padding-top: 0;
    border-top: 0;
}
body.new_preuploader.viewer .vw_limits_note {
    display: block;
    margin-top: 0.4rem;
    color: var(--np-dialog-dim);
    font-size: 0.75rem;
    line-height: 1.3;
    white-space: normal;
}
/* ファイル選択ボタンの下の「端末のメモリ」の選択 (DeviceMemorySelect)。ボタンの行から
   少し離して中央に置く。select の下地・縁・文字は操作ボタン (elevation_button) と同じ
   変数。開いた一覧 (option) はダイアログの地色・文字色で読めるようにする */
body.new_preuploader.viewer .vw_memory_row {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.45rem;
    margin-top: 0.9rem;
    font-size: 0.85rem;
    color: var(--vw-text);
}
body.new_preuploader.viewer .vw_memory_select {
    padding: 0.15rem 0.35rem;
    border: 1px solid var(--vw-panel-border);
    border-radius: 6px;
    background-color: var(--vw-control-bg);
    color: var(--vw-text);
    font: inherit;
    cursor: pointer;
}
body.new_preuploader.viewer .vw_memory_select option {
    background-color: var(--np-dialog-bg);
    color: var(--np-dialog-text);
}
body.new_preuploader.viewer .common_button {
    color: var(--vw-text);
    background-color: var(--vw-button-bg);
    border-color: var(--vw-panel-border);
}
body.new_preuploader.viewer .common_button:active {
    background: var(--vw-button-active-bg);
}
body.new_preuploader.viewer .elevation_button,
body.new_preuploader.viewer .move_stride_slider,
body.new_preuploader.viewer .brightness_panel,
body.new_preuploader.viewer .keyframe_route_panel,
body.new_preuploader.viewer .region_edit_panel {
    background-color: var(--vw-control-bg);
    border-color: var(--vw-panel-border);
    color: var(--vw-text);
}
/* Brightness のパネルの照明の入り切り・既定に戻すボタンと、Keyframes・Delete region のパネルのボタン
   (操作ボタンと同じ下地・縁、ホバーで明るく。押せないときは明るくしない) */
body.new_preuploader.viewer .brightness_button,
body.new_preuploader.viewer .keyframe_route_button,
body.new_preuploader.viewer .keyframe_route_add,
body.new_preuploader.viewer .region_edit_button,
body.new_preuploader.viewer .region_edit_choice,
body.new_preuploader.viewer .region_edit_delete {
    background-color: var(--vw-button-bg);
    border-color: var(--vw-panel-border);
    color: var(--vw-text);
}
body.new_preuploader.viewer .brightness_button:hover,
body.new_preuploader.viewer .keyframe_route_button:hover:enabled,
body.new_preuploader.viewer .keyframe_route_add:hover:enabled,
body.new_preuploader.viewer .keyframe_route_loop[aria-pressed='true'],
body.new_preuploader.viewer .region_edit_button:hover:enabled,
body.new_preuploader.viewer .region_edit_choice:hover:enabled,
body.new_preuploader.viewer .region_edit_delete:hover:enabled,
body.new_preuploader.viewer .region_edit_toggle[aria-pressed='true'] {
    background-color: var(--vw-button-active-bg);
}
body.new_preuploader.viewer .viewer_model_switch_button {
    background: var(--vw-switch-bg);
    border-color: var(--vw-switch-border);
    color: var(--vw-switch-text);
}
body.new_preuploader.viewer .viewer_model_switch_button:hover {
    background: var(--vw-switch-hover-bg);
}
body.new_preuploader.viewer .viewer_model_switch_button.viewer_model_active {
    background: var(--vw-switch-active-bg);
    border-color: var(--vw-switch-active-bg);
    color: var(--vw-switch-active-text);
}

/* --------------------------------------------------------------------------
   Dark theme only
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer.theme-dark .inner_load_circle {
    border-top-color: var(--np-accent);
}
body.new_preuploader.viewer.theme-dark #file_choice:focus + #file_choice_label::before {
    border-color: var(--np-accent);
    box-shadow: 0 0 5px var(--np-accent);
}
/* ファイルを開くボタンの下地が暗い (ダウンロードボタンと同じ) ので、
   アイコンはボタンの文字と同じ明るい色 */
body.new_preuploader.viewer.theme-dark .vw_open_folder_icon {
    fill: var(--vw-text);
}

/* サイズゲート / ロード中のオーバーレイ (.overlay_center) は全画面 fixed で
   ヘッダー (メニュー・テーマ切替) のクリックまで遮っていた。余白部分は
   クリックを透過し、中身 (ゲート・スピナー) だけ受けるようにする */
body.new_preuploader.viewer .overlay_center {
    pointer-events: none;
    /* 全画面 fixed (中央寄せ) だとサイズ警告の文面が右サイドパネル (生成物の
       切替ボタン) に被ってクリックを奪う。キャンバス領域 (#canvas_inner,
       position: relative) の内側に限定して中央に出す */
    position: absolute;
    inset: 0;
    width: auto;
    height: auto;
    transform: none;
}
body.new_preuploader.viewer .overlay_center > * {
    pointer-events: auto;
}
/* モデル表示中に別の種類を選んだときのゲートのダイアログ (ModelLoadDialog)。
   キャンバス上のゲート用の余白 (single.css の .model_load_gate の padding、
   サイズ下の margin) はダイアログのタイトル・ボタン行の余白と重なるので外す */
body.new_preuploader.viewer .vw_load_dialog .model_load_gate {
    padding: 0;
}
body.new_preuploader.viewer .vw_load_dialog .model_load_size {
    margin: 0;
}
/* 表示できない掲示 (短いラベルと黄色の ! のツールチップ) */
body.new_preuploader.viewer .vw_load_dialog .model_load_too_large {
    margin: 0.6rem 0 0;
}
/* Keyframes のパネルの開くボタンの左上に重ねる、ファイルを読めなかった赤の ! と、Delete region のパネルの
   ダウンロードボタンの左上に重ねる、ファイルを作れなかった赤の ! (ボタンより小さく) */
body.new_preuploader.viewer .keyframe_route_file_error .np_run_options_icon,
body.new_preuploader.viewer .region_edit_file_error .np_run_options_icon {
    width: 12px;
    height: 12px;
    font-size: 9px;
}
/* キーフレームの編集のダイアログ (viewer_controls.js の KeyframeEditDialog)。背景を塞がない (open の属性) ので、
   中央寄せ (np_dialog の inset 0 + margin auto) と幅をやめ、JS が Keyframes のパネルの左などに置く
   (top / left)。行が多いときは一覧だけを縦に流す。行は キーフレームの名前 (押すとその見え方へ)・点滅・上へ・
   下へ・(入れるボタンの列)・消す。点滅は名前の組にして上へ・下へと少し離し、消すは押し間違えないよう少し
   離す。行の間の入れるボタンは、2 つの行の境目に重ねて入れる列に置く。行の間には区切りの線 */
body.new_preuploader.viewer dialog.np_dialog.keyframe_edit_dialog {
    inset: auto;
    margin: 0;
    width: auto;
    max-width: calc(100vw - 16px);
    max-height: calc(100vh - 16px);
    overflow: auto;
    padding: 0.8rem 1rem 1rem;
    z-index: 1000;
}
body.new_preuploader.viewer .keyframe_edit_dialog .np_dialog_title {
    font-size: 1rem;
    margin-bottom: 0.7rem;
}
body.new_preuploader.viewer .keyframe_edit_dialog .np_dialog_buttons {
    margin-top: 0.9rem;
}
/* 一覧は列をそろえる格子: 名前 (いちばん長い名前の幅)・点滅・上へ・下へ・(入れるボタンの列)・消す。行は
   そのまま格子の行に並べ (display: contents)、行の間 (keyframe_edit_gap) は全部の列にまたがる。列は広げない
   (入れるボタンを右から測って置くため、消すの右端を格子の右端に保つ) */
body.new_preuploader.viewer .keyframe_edit_list {
    display: grid;
    grid-template-columns: max-content repeat(5, auto);
    justify-content: start;
    align-items: center;
    column-gap: 0.3rem;
    max-height: min(60vh, 26rem);
    overflow-y: auto;
}
body.new_preuploader.viewer .keyframe_edit_row {
    display: contents;
}
body.new_preuploader.viewer .keyframe_edit_list > .np_dialog_note {
    grid-column: 1 / -1;
}
/* 名前は字だけのボタン (乗せると色が付く)。列の幅いっぱいに広げ、すぐ右の点滅の位置を行どうしでそろえる */
body.new_preuploader.viewer .keyframe_edit_label {
    height: 1.6rem;
    padding: 0 0.35rem;
    border: 0;
    border-radius: 5px;
    background: transparent;
    color: var(--np-dialog-text);
    font: inherit;
    font-size: 0.9rem;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
}
body.new_preuploader.viewer .keyframe_edit_label:hover {
    background: var(--np-accent);
    color: var(--np-accent-contrast);
}
body.new_preuploader.viewer .keyframe_edit_button.keyframe_edit_blink {
    margin-right: 0.5rem;
}
body.new_preuploader.viewer .keyframe_edit_button,
body.new_preuploader.viewer .keyframe_edit_insert_column {
    width: 1.6rem;
    height: 1.6rem;
}
body.new_preuploader.viewer .keyframe_edit_button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: var(--np-control-border);
    border-radius: 5px;
    background: transparent;
    color: var(--np-dialog-text);
    cursor: pointer;
}
body.new_preuploader.viewer .keyframe_edit_button svg {
    width: 0.85rem;
    height: 0.85rem;
    fill: currentColor;
}
body.new_preuploader.viewer .keyframe_edit_button:hover:enabled {
    background: var(--np-accent);
    border-color: var(--np-accent);
    color: var(--np-accent-contrast);
}
body.new_preuploader.viewer .keyframe_edit_button:disabled {
    opacity: var(--np-disabled-opacity);
    cursor: default;
}
body.new_preuploader.viewer .keyframe_edit_button.keyframe_edit_remove {
    margin-left: 1.1rem;
}
body.new_preuploader.viewer .keyframe_edit_gap {
    grid-column: 1 / -1;
    position: relative;
    height: 0.5rem;
}
body.new_preuploader.viewer .keyframe_edit_gap .keyframe_edit_button {
    position: absolute;
    top: 50%;
    /* 入れるボタンの列の中央: 右から 消す (1.6rem)・その左の間 (列の間 0.3rem + 離す 1.1rem)・
       列 (1.6rem) とボタン (1.3rem) の差の半分 */
    right: calc(1.6rem + 0.3rem + 1.1rem + 0.15rem);
    width: 1.3rem;
    height: 1.3rem;
    transform: translateY(-50%);
}
body.new_preuploader.viewer .keyframe_edit_gap .keyframe_edit_button svg {
    width: 0.7rem;
    height: 0.7rem;
}
/* 行の間の区切りの線 (行の左端から消すボタンの右端まで)。入れるボタンの所だけ、両側を 0.2rem あけて途切れ
   させる (地がグラデーションなので、線の上にボタンを塗って隠すのではなく、左右の 2 本に分ける)。入れるボタン
   の右端は右から 3.15rem (上の right)、左端はそこから 1.3rem */
body.new_preuploader.viewer .keyframe_edit_gap::before,
body.new_preuploader.viewer .keyframe_edit_gap::after {
    content: '';
    position: absolute;
    top: 50%;
    border-top: var(--np-control-border);
}
body.new_preuploader.viewer .keyframe_edit_gap::before {
    left: 0;
    right: calc(1.6rem + 0.3rem + 1.1rem + 0.15rem + 1.3rem + 0.2rem);
}
body.new_preuploader.viewer .keyframe_edit_gap::after {
    right: 0;
    width: calc(1.6rem + 0.3rem + 1.1rem + 0.15rem - 0.2rem);
}

/* 横長表示: 使い方 / テーマ切替の 2 つを右へ寄せ、テーマ切替の右端を
   空のキャンバス (.empty_three_canvas: 幅 480px + 枠 0.4vh×2、
   #viewer_canvas_layout の margin-left 5.5rem 分だけ中央から右寄り)
   の右端に揃える。ヘッダーの flex からは外し、行の高さ内で縦中央 */
/* 1100px 未満ではユーザー名と衝突するため通常配置 (中央寄り) のまま */
@media only screen and (min-width: 1100px) {
    body.new_preuploader.viewer .np_header {
        position: relative;
    }
    body.new_preuploader.viewer .np_header_controls {
        display: flex;
        align-items: center;
        position: absolute;
        top: 1rem;
        height: calc(100% - 3.5rem);
        right: calc(50% - 2.75rem - 240px - 0.4vh);
    }
    /* モデル表示中はヘッダーの下の padding が 1.5rem (下の「モデル表示中」の指定) */
    body.new_preuploader.viewer:has(#viewer_canvas_layout.model_displayed) .np_header_controls {
        height: calc(100% - 2.5rem);
    }
    /* ユーザー名はテーマ切替の右側に残る幅 (右パディング 1rem + 間隔 1rem を
       除く) を上限にし、長い名前は省略記号で切る (重なり防止) */
    body.new_preuploader.viewer .np_header #user_login {
        max-width: calc(50% - 2.75rem - 240px - 0.4vh - 2rem);
        min-width: 0;
    }
    body.new_preuploader.viewer .np_header #user_name {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

/* --------------------------------------------------------------------------
   モデル表示中 (#viewer_canvas_layout.model_displayed) はキャンバスを
   ウィンドウの横幅いっぱい (左右 1rem の余白) に広げる。初期状態の空の
   キャンバス (.empty_three_canvas) は従来サイズのまま。描画バッファは
   placement.js が要素の実寸から決めるので縦横比は歪まない。
   ヘッダーとの間も詰める: ヘッダーの下の padding 2.5rem → 1.5rem、キャンバスの上の
   margin 2rem → 0 (合計 4.5rem → 1.5rem。空のキャンバスのときは従来のまま)。
   縦長表示 (<=880px) は single.css の既存ルールのまま
   -------------------------------------------------------------------------- */
@media only screen and (min-width: 881px) {
    body.new_preuploader.viewer #pointcloud_viewer #viewer_canvas_layout.model_displayed {
        width: 100%;
        box-sizing: border-box;
        padding: 0 1rem;
        margin: 0;
    }
    /* ヘッダーはキャンバスの入れ物の外なので、モデル表示中かは :has で見る */
    body.new_preuploader.viewer:has(#viewer_canvas_layout.model_displayed) .np_header {
        padding-bottom: 1.5rem;
    }
    body.new_preuploader.viewer .model_displayed #canvas_inner {
        flex: 1 1 0;
        min-width: 0;
    }
    /* ローカル表示: 操作パネル (Initial Position / Point size / Move stride =
       .elevation_buttons)。881〜1580px では縦長表示 (<=880px) と同じく
       キャンバスの上・右寄せに置く (従来の margin-top 10rem は無くす) */
    body.new_preuploader.viewer .model_displayed .three_canvas_button,
    body.new_preuploader.viewer .model_displayed .three_viewer_buttons {
        display: flex;
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }
    body.new_preuploader.viewer .model_displayed .three_canvas_button > .elevation_buttons,
    body.new_preuploader.viewer .model_displayed .three_viewer_buttons > .elevation_buttons {
        order: 0;
        flex: 0 0 auto;
        margin: 0 0.3rem 0.6rem auto;
        align-items: stretch;
    }
    /* ローカル表示: 高さは従来の 90vh のまま横幅だけ広げる */
    body.new_preuploader.viewer #pointcloud_viewer .model_displayed #three_canvas {
        flex: 1 1 0;
        min-width: 0;
        width: 100%;
        max-width: none;
        max-height: none;
        height: 90vh;
        box-sizing: border-box;
        margin: 0;
    }
    /* dataset 表示: 右サイドパネルの残り幅いっぱい。高さは、下の縁がウィンドウの下端から左と
       同じ 1rem になるまで (縦スクロールなし): 100vh − ヘッダー 6.1875rem (99px = padding 1rem +
       1.5rem (モデル表示中、上の指定) + 中身) − ファイル名の行 1.355rem (0.85rem × 1.3 + 0.25rem) −
       下の余白 1rem (キャンバスの上の margin は 0)。881〜1580px は入れ物が縦の flex で、上の
       flex: 1 1 0 (ローカル表示の高さの詰め) だと height が効かず読み込み時の大きさのままになるので
       外す。幅に対して高すぎないよう 70vw を上限に */
    body.new_preuploader.viewer #pointcloud_viewer .dataset_mode.model_displayed #three_canvas {
        flex: 0 0 auto;
        height: min(calc(100vh - 8.55rem), 70vw);
    }
}

/* ロード完了マーカー (<output>, aria-live / テスト用)。flex 行の中で幅を取って
   キャンバスを狭めていたので、読み上げ用に DOM には残しつつ視覚的に隠す */
body.new_preuploader.viewer .viewer_model_loaded_marker {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
}

/* 1581px 以上: 操作パネルは dataset 表示のサイドパネルと同じく
   キャンバスの右側に縦積み */
@media only screen and (min-width: 1581px) {
    body.new_preuploader.viewer .model_displayed .three_canvas_button,
    body.new_preuploader.viewer .model_displayed .three_viewer_buttons {
        flex-direction: row;
        align-items: flex-start;
    }
    body.new_preuploader.viewer .model_displayed .three_canvas_button > .elevation_buttons,
    body.new_preuploader.viewer .model_displayed .three_viewer_buttons > .elevation_buttons {
        order: 1;
        margin: 0 0 0 1.4rem;
    }
}

/* --------------------------------------------------------------------------
   dataset 表示の右サイドパネル: 選択中の生成物のダウンロード・変換
   (DatasetModelActions)。ファイルタイプの切替ボタンの下 (モデル表示中は
   操作ボタン類 .elevation_buttons の下) に置く。ボタンの見た目は
   /manual_pipeline の完了スロット (manual_pipeline.css の mp_download_button /
   mp_convert_button) と同じ: ダウンロードは四角 (角 7px) で変換行 (np_row)
   と同じ半透明の下地 + 細い bevel 縁、変換は一回り小さい丸枠。グリフ色は
   本ページの文字色 (np_icon_button の --np-text はライトで白)
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer .vw_model_actions {
    display: flex;
    flex-direction: column;
    /* 上のまとまり (切替ボタン群、モデル表示中は操作ボタン群) とは別の
       まとまりと分かるよう、切替ボタン群 → 操作ボタン群の間隔 (2rem) より
       広く空ける */
    margin-top: 3rem;
}
/* ダウンロード・変換・i ツールチップの行。上の工程選択ボタン
   (.viewer_model_switch_button) と同じ幅を基準に、両端をボタンの縁から
   ボタンの文字の始まり (枠 1px + 左右の余白 0.8rem) まで内側へ寄せて並べる
   (横長表示は行も工程選択ボタンもサイドパネルの幅いっぱい。縦長表示は下の
   880px 以下で同じ幅を指定する) */
body.new_preuploader.viewer .vw_model_action_buttons {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    box-sizing: border-box;
    padding: 0 calc(0.4rem + 1px);
}
/* ボタンの下: ダウンロード・変換の対象 (選択中のファイルタイプ名) */
body.new_preuploader.viewer .vw_model_action_target {
    margin: 0.4rem 0 0;
    font-size: 0.85rem;
    color: var(--vw-text-soft);
    overflow-wrap: anywhere;
}
body.new_preuploader.viewer .vw_download_button,
body.new_preuploader.viewer .vw_download_button:hover:enabled {
    flex: 0 0 auto;
    border-radius: 7px;
    color: var(--vw-text);
    background: var(--np-row-bg);
    border: var(--np-row-border);
    border-color: var(--np-bevel-colors);
}
body.new_preuploader.viewer .vw_download_button:hover:enabled {
    background: var(--vw-button-active-bg);
}
body.new_preuploader.viewer .vw_download_button:active:enabled {
    border-color: var(--np-bevel-colors-inset);
}
body.new_preuploader.viewer .vw_convert_button {
    width: 2.2rem;
    height: 2.2rem;
    color: var(--vw-text);
}
body.new_preuploader.viewer .vw_convert_button svg {
    width: 1.05rem;
    height: 1.05rem;
    stroke-width: 1.5;
}

/* 開始した変換の行 (ボタンの下)。/workspace の変換行 (np_convert_row) を
   サイドパネルの幅 (切替ボタン群で決まる 9.5rem 前後) に収まるよう組み直す:
   1 段目 = マーク + 概要 + i ツールチップ (右端)、2 段目 = 状態 (変換中・
   キャンセルのときだけ)、最後の段 = 料金 (左) + ダウンロード・削除 (右)。i を
   ダウンロードの左に置くと最後の段が料金と詰まるので 1 段目に置く。
   操作列 (np_row_actions) は
   display: contents で中身を行のグリッドへ直接並べ、段は order で決める
   (状態 → 料金・ボタンの順に自動配置)。変換行でパネルを広げない
   (contain: inline-size。幅はパネルに合わせて伸びる) */
body.new_preuploader.viewer .vw_model_conversions {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    contain: inline-size;
    margin-top: 0.5rem;
}
body.new_preuploader.viewer .vw_model_conversions .np_row.np_convert_row {
    grid-template-columns: auto minmax(0, 1fr) auto auto;
    margin-left: 0;
    padding: 0.3rem 0.5rem;
    gap: 0.3rem 0.4rem;
    text-align: left;
}
body.new_preuploader.viewer .vw_model_conversions .np_row_actions {
    display: contents;
}
body.new_preuploader.viewer .vw_model_conversions .np_convert_mark {
    grid-column: 1;
    grid-row: 1;
}
body.new_preuploader.viewer .vw_model_conversions .np_convert_summary {
    grid-column: 2 / 4;
    grid-row: 1;
    font-size: 0.8rem;
}
/* i ツールチップ (ConversionInfo) は 1 段目の右端 (削除ボタンの列の中央) */
body.new_preuploader.viewer .vw_model_conversions
    .np_row_actions > .np_run_options_wrap {
    grid-column: 4;
    grid-row: 1;
    justify-self: center;
}
/* 文字・アイコン・料金は本ページの文字色 (np の --np-text / --np-text-dim は
   ライトで白系 = 本ページの黒文字と揃わず、パネル下側の明るい背景で読めない) */
body.new_preuploader.viewer .vw_model_conversions .np_convert_mark,
body.new_preuploader.viewer .vw_model_conversions .np_convert_summary,
body.new_preuploader.viewer .vw_model_conversions .np_convert_price {
    color: var(--vw-text-soft);
}
body.new_preuploader.viewer .vw_model_conversions .np_status_col,
body.new_preuploader.viewer .vw_model_conversions .np_row_actions > .np_status {
    grid-column: 1 / -1;
    order: 1;
    justify-self: start;
}
/* チップとバーを左に寄せ、入らなければチップの下へバーを折り返す (np_convert_status_row) */
body.new_preuploader.viewer .vw_model_conversions .np_status_col {
    flex-direction: row;
    justify-content: flex-start;
    gap: 0.35rem;
}
body.new_preuploader.viewer .vw_model_conversions .np_run_progress_percent,
body.new_preuploader.viewer .vw_model_conversions .np_status_canceled {
    color: var(--vw-text-soft);
}
body.new_preuploader.viewer .vw_model_conversions .np_status {
    font-size: 0.75rem;
    padding: 0.15rem 0.5rem;
}
body.new_preuploader.viewer .vw_model_conversions .np_convert_price {
    grid-column: 1 / 3;
    order: 2;
    justify-self: start;
}
/* 失敗の行: エラーの ! (ConversionFailure の compact) は最後の段の料金の右端。料金と同じ列に重ねて
   置くので段も指定する。パネルが最も狭い 9.5rem でも料金と離れるよう、列の隙間へ少し出す */
body.new_preuploader.viewer .vw_model_conversions .np_convert_failure {
    grid-column: 1 / 3;
    grid-row: 2;
    justify-self: end;
    margin-right: -0.22rem;
}
body.new_preuploader.viewer .vw_model_conversions
    .np_convert_row[data-conversion-status='failed'] .np_convert_price {
    grid-row: 2;
}
/* まだダウンロードしていない行の NEW。パネルが 9.5rem 前後だと最後の段は料金とボタンで埋まり、
   1 段目の i の左では概要がほとんど切れるので、状態のチップ (変換中・キャンセル) と同じ段の左に置く。
   880px 以下 (幅 18rem) は最後の段のダウンロードの左 (下の @media) */
body.new_preuploader.viewer .vw_model_conversions .np_row_actions > .np_new_badge {
    grid-column: 1 / -1;
    order: 1;
    justify-self: start;
}
/* ボタンは ConversionRow の並び (ダウンロード → 削除) で末尾の 2 つ */
body.new_preuploader.viewer .vw_model_conversions
    .np_row_actions > button:nth-last-of-type(2) {
    grid-column: 3;
    order: 2;
}
body.new_preuploader.viewer .vw_model_conversions
    .np_row_actions > button:last-of-type {
    grid-column: 4;
    order: 2;
}
body.new_preuploader.viewer .vw_model_conversions .np_icon_button {
    width: 2rem;
    height: 2rem;
    color: var(--vw-text);
}
body.new_preuploader.viewer .vw_model_conversions .np_icon_button svg {
    width: 1rem;
    height: 1rem;
}
/* 削除して Undo を待っている変換行 (「Deleted · Undo」の行) も本ページの文字色
   (np の --np-text-dim はライトで白系 = パネル下側の明るい背景で読めない)。パネルは幅が
   狭く 1 行では名前がほとんど切れるので、名前を 2 行まで折り返し、Undo はその下の右端に置く
   (普通の変換行と同じく 2 段) */
body.new_preuploader.viewer .vw_model_conversions .np_row.np_deleted_row {
    flex-wrap: wrap;
    row-gap: 0.3rem;
    color: var(--vw-text-soft);
}
body.new_preuploader.viewer .vw_model_conversions .np_deleted_text {
    flex: 1 0 100%;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    font-size: 0.8rem;
    line-height: 1.3;
    white-space: normal;
    overflow-wrap: anywhere;
}
body.new_preuploader.viewer .vw_model_conversions .np_deleted_sep {
    display: none;
}
body.new_preuploader.viewer .vw_model_conversions .np_deleted_undo {
    margin-left: auto;
}
/* 横長表示のサイドパネルの Move stride (モデル表示中の操作ボタン群の最後)。
   single.css の .move_stride_slider は margin-left: auto の右寄せ (ローカル
   表示の操作パネル用。そこではパネルの幅 = スライダーの幅) で、サイドパネルの
   幅に伸びた操作ボタン群の中では右に寄るので、Initial Position・工程選択
   ボタンと同じ左端に揃える (縦長表示は single.css の横並び・中央寄せのまま) */
@media only screen and (min-width: 881px) {
    body.new_preuploader.viewer .viewer_side_panel .move_stride_slider,
    body.new_preuploader.viewer .viewer_side_panel .brightness_panel,
    body.new_preuploader.viewer .viewer_side_panel .keyframe_route_panel,
    body.new_preuploader.viewer .viewer_side_panel .region_edit_panel {
        margin-left: 0;
        align-self: flex-start;
    }
}
/* dataset 表示のサイドパネルの「端末のメモリ」の選択 (DeviceMemorySelect を入れ物 vw_side_memory
   に入れて置く。ローカル表示ではファイル選択ボタンの下に出るもの)。
   - 横長表示: パネル (ボタン類の列) をキャンバスと同じ高さまで伸ばし (align-self: stretch)、
     入れ物を margin-top: auto で列の下端 = キャンバスの下端の高さに寄せる。上のまとまりとの間は
     入れ物の padding で最小 2rem (列の中身がキャンバスより高いときは列が伸び、空きは 2rem)。
     名前の下に select を縦に積んで (横に並べるとパネルがボタンより広がりキャンバスが狭まる)
     左端をボタンに揃える
   - 縦長表示 (≤880px): パネルがキャンバスの下で中央寄せなので、ページの一番下に、ローカル表示と
     同じ横並びで画面の中央に */
@media only screen and (min-width: 881px) {
    body.new_preuploader.viewer #pointcloud_viewer .dataset_mode > .viewer_side_panel {
        align-self: stretch;
    }
}
body.new_preuploader.viewer .vw_side_memory {
    margin-top: auto;
    padding-top: 2rem;
}
body.new_preuploader.viewer .vw_side_memory .vw_memory_row {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.3rem;
    margin-top: 0;
    margin-bottom: 10px;
}
@media only screen and (max-width: 880px) {
    body.new_preuploader.viewer .vw_side_memory {
        padding-top: 1.2rem;
    }
    body.new_preuploader.viewer .vw_side_memory .vw_memory_row {
        flex-direction: row;
        align-items: center;
        gap: 0.45rem;
    }
}
/* 880px 以下はサイドパネルがキャンバスの下で中央寄せ (single.css)。
   ボタン・変換行も中央に置き、変換行は /manual_pipeline の工程スロットと
   同じ幅 (18rem) にする。ダウンロード・変換・i ツールチップの行は横長表示と
   同じ幅 = 横長表示の工程選択ボタンの幅 (single.css の .viewer_model_switch の
   min-width 9.5rem) から同じだけ内側へ寄せた幅 (border-box に上の余白。
   データセット名やファイルタイプ名が長いと横長表示はパネルごと広がるので、
   そのときは横長表示の方が広い) */
@media only screen and (max-width: 880px) {
    body.new_preuploader.viewer .vw_model_actions {
        margin-top: 1.2rem;
        align-items: center;
    }
    body.new_preuploader.viewer .vw_model_action_buttons {
        width: 9.5rem;
    }
    body.new_preuploader.viewer .vw_model_conversions {
        contain: none;
        width: min(18rem, 92vw);
    }
    /* NEW は最後の段の料金の右端 (ダウンロードの左)。失敗の行のエラーの ! と同じ置き方で、料金と同じ
       列に重ねるので料金の段も指定する (完了の行に状態の段は無い) */
    body.new_preuploader.viewer .vw_model_conversions .np_row_actions > .np_new_badge {
        grid-column: 1 / 3;
        grid-row: 2;
        justify-self: end;
    }
    body.new_preuploader.viewer .vw_model_conversions
        .np_convert_row[data-conversion-status='done'] .np_convert_price {
        grid-row: 2;
    }
}

/* --------------------------------------------------------------------------
   Dark theme — bevel borders. single.css の border は幅が vh 依存で、
   outset の暗い側の色はブラウザが自動生成する (暗色はほぼ黒に落ちる)。
   ダークでは px 幅の solid + 明示色の 4 辺 (new_preuploader.css の
   --np-bevel-colors) に置き換え、PC / スマホで同じ見え方にする。
   ライトは single.css の見た目を現状維持
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer.theme-dark #three_canvas,
body.new_preuploader.viewer.theme-dark #pointcloud_viewer #three_canvas {
    border: 3px solid var(--np-border-color);   /* 0.4vh outset */
    border-color: var(--np-bevel-colors);
}
body.new_preuploader.viewer.theme-dark #file_choice + #file_choice_label::before,
body.new_preuploader.viewer.theme-dark .common_button,
body.new_preuploader.viewer.theme-dark .elevation_button,
body.new_preuploader.viewer.theme-dark .move_stride_slider,
body.new_preuploader.viewer.theme-dark .brightness_panel,
body.new_preuploader.viewer.theme-dark .keyframe_route_panel,
body.new_preuploader.viewer.theme-dark .region_edit_panel {
    border: 2px solid var(--np-border-color);   /* 0.2〜0.3vh outset */
    border-color: var(--np-bevel-colors);
}

/* テクスチャを端末の予算に合わせて縮めた・読めないテクスチャがあったときの掲示 (キャンバス領域
   #canvas_inner の左上。両方のときは横に並べる)。短いラベルと黄色の ! のツールチップ (文は
   ツールチップの中)。操作の邪魔をしないようクリックは透過し、! だけ押せるようにする */
body.new_preuploader.viewer .vw_texture_notice {
    position: absolute;
    left: 0.8rem;
    top: 0.8rem;
    display: flex;
    flex-wrap: wrap;
    gap: 0.2rem 1rem;
    margin: 0;
    padding: 0.3rem 0.6rem;
    border-radius: 0.4rem;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    font-size: 0.8rem;
    line-height: 1.3;
    pointer-events: none;
    z-index: 2;
}
body.new_preuploader.viewer .vw_texture_notice .np_error_tip {
    pointer-events: auto;
}
body.new_preuploader.viewer .vw_texture_notice .np_error_tip_label {
    color: #fff;
}
/* 読み込み中の丸と、その下の進み具合 (段階的に読む gaussian の取得の %) */
body.new_preuploader.viewer .vw_loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.6rem;
}
body.new_preuploader.viewer .vw_load_progress {
    margin: 0;
    padding: 0.2rem 0.6rem;
    border-radius: 0.4rem;
    background: rgba(0, 0, 0, 0.45);
    color: #fff;
    font-size: 0.8rem;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   開いているファイルの名前 (.vw_file_name。ThreeInjectionCanvas = placement.js が
   /3d_viewer のときだけキャンバスと同じ入れ物に出す)。キャンバスの上の左端に置く:
   - 881〜1580px: 操作ボタン類 (.elevation_buttons、右寄せ) の下・キャンバスのすぐ上の行
   - 1581px 以上 (操作ボタン類がキャンバスの右): 名前だけの行をキャンバスの上に
   - 880px 以下は single.css の縦積みのまま (入れ物の先頭 = 一番上の左)
   長い名前は … で切る (title 属性に全体)
   -------------------------------------------------------------------------- */
body.new_preuploader.viewer .vw_file_name {
    margin: 0 0 0.25rem;
    padding: 0 0.1rem;
    max-width: 100%;
    box-sizing: border-box;
    align-self: flex-start;
    font-size: 0.85rem;
    line-height: 1.3;
    color: var(--vw-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
@media only screen and (min-width: 881px) {
    body.new_preuploader.viewer .model_displayed .vw_file_name {
        order: 1;
    }
    body.new_preuploader.viewer #pointcloud_viewer .model_displayed #three_canvas {
        order: 2;
    }
}
@media only screen and (min-width: 1581px) {
    body.new_preuploader.viewer .model_displayed .three_canvas_button,
    body.new_preuploader.viewer .model_displayed .three_viewer_buttons {
        flex-wrap: wrap;
    }
    body.new_preuploader.viewer .model_displayed .vw_file_name {
        order: 0;
        flex: 0 0 100%;
    }
    body.new_preuploader.viewer #pointcloud_viewer .model_displayed #three_canvas {
        order: 0;
    }
}
