/* ================================================================
           Design System — "Scaleless Digital Cluster"
           黒基調 / 路線色(--theme-color)が全体に波及 / M PLUS 1(可変ウェイト)
           JSが更新するのは --theme-color / --theme-glow / --theme-bg のみ。
           ================================================================ */

        /* ── 新ゴ Pr5(ローカルアセット・4ウェイトで全階層をカバー) ──
           font-display: block — ローカル同梱フォントのため読込は高速。代替フォントで
           一瞬でも表示させると letter-spacing/scaleX で精密調整したレイアウトが崩れて見えるため、
           フォント確定まで（わずかに）非表示にする方を選択する。 */
        /* 可変フォント1本で 100〜900 を連続的に賄う（旧: 新ゴ4ウェイトを weight 範囲で切替） */
        @font-face { font-family: "Rail Sans"; src: url("fonts/MPLUS1-subset.woff2") format("woff2"); font-weight: 100 900; font-display: block; }

        /* #FontWeightMap: 新ゴ Pr5 の4ウェイトを M PLUS の可変軸へ写した値。
           実測（漢字「一」の横画）で 新ゴM 0.1070em / DB 0.1320 / B 0.1620 / H 0.1790 em。
           実機の描画インク量で合わせ込んだ結果、500→600 / 600→690 / 700→770 で
           新ゴとの差が ±3% 以内に収まる。
           **800帯（速度の巨大数字・路線名）だけは意図的に据え置く**。
           視角125分（可読限界の25倍）で可読性の余地が無い一方、wght900 では
           「6」のカウンター（字の中の抜き）が 9.9%→6.4% まで潰れ、OLEDの発光滲みと
           drop-shadow のグローが乗ると 6/8/0 の判別が落ちるため。 */
        :root {
            --bg-color: #030408;
            --surface: rgba(17, 21, 28, 0.72);
            --border: rgba(255, 255, 255, 0.07);
            --border-highlight: rgba(255, 255, 255, 0.14);
            --text-main: #F4F6FA;
            --text-sub: #8A93A3;
            /* #DimContrast: 旧 #454D5A は背景 #030408 に対しコントラスト比 2.40 で
               WCAG AA(4.5) を割っていた。通過済み駅・GPS値・NEXTラベルに使われる。
               色相217°・彩度を保ったまま明度だけ上げて 4.51 に到達させた最小値。
               --text-sub(6.62) / --text-main(18.94) との階層は維持される。 */
            --text-dim: #6A778B;

            --theme-color: #0a84ff;
            --theme-glow: rgba(10, 132, 255, 0.4);
            --theme-bg: rgba(10, 132, 255, 0.15);
            /* #LineTextContrast: 文字に使う路線色（明度だけ補正。updateThemeColor が設定） */
            --theme-text: #0a84ff;
            --line-text: #0a84ff;

            /* ── 路線カラー（常に路線固有の色を保持。停車時も緑に上書きされない） ── */
            --line-color: #0a84ff;
            --line-glow: rgba(10, 132, 255, 0.4);
            --line-bg: rgba(10, 132, 255, 0.15);

            /* ── フォント: 新ゴ Pr5(全UI共通・ウェイトで階層化) ── */
            --font-family: "Rail Sans", "Noto Sans JP", "Roboto", sans-serif;
            --font-display: var(--font-family);
        }

        * {
            box-sizing: border-box; margin: 0; padding: 0;
            font-family: var(--font-family);
            -webkit-tap-highlight-color: transparent;
            user-select: none; -webkit-user-select: none;
            /* ダブルタップズーム遅延防止 */
            touch-action: manipulation;
        }

        body {
            background-color: var(--bg-color);
            background-image:
                radial-gradient(ellipse 130% 55% at 50% -15%, var(--line-bg), transparent 60%),
                radial-gradient(ellipse 90% 45% at 110% 110%, rgba(255, 255, 255, 0.035), transparent 65%);
            color: var(--text-main);
            height: 100dvh; min-height: 100dvh;
            display: flex; justify-content: center;
            padding: calc(var(--safe-area-top, 0px) + 10px) 14px calc(var(--safe-area-bottom, 0px) + 14px);
            -webkit-font-smoothing: antialiased;
            overflow: hidden;
            overscroll-behavior: none;
        }

        .app-container { width: 100%; max-width: 480px; display: flex; flex-direction: column; gap: 12px; height: 100%; position: relative; overflow: hidden;}

        /* ── 起動時フリッカー防止（Preload/Reveal ゲーティング） ──
           フォント確定 + セーフエリア注入完了の両方が揃うまで .app-container を隠し、
           揃った時点で一気にフェードインする。文字崩れ・パディングジャンプの瞬間を見せない。 */
        html.preload .app-container { visibility: hidden; opacity: 0; }
        .app-container { opacity: 1; transition: opacity 0.25s ease; }

        /* #PowerSave: バックグラウンド時（visibilitychange=hidden）のアニメ停止。
           画面が見えていない間だけ適用されるためUX影響ゼロ。
           （画面が見えていない間は WebView がそもそも描かないので、節電の本体は #DecorTick・
           #NoBackdropBlur・#NoArrivePulse・#TileFilterBake の「見えている間」の対策） */
        .power-save .tab-view { animation-play-state: paused; }

        /* #NoBackdropBlur: 旧実装は全パネルに backdrop-filter: blur(28px) saturate(180%) を掛けていた。
           パネルの後ろは body の淡い放射グラデーションだけで、ぼかしても見た目はほぼ変わらないのに、
           パネルの中身が描き変わるたびに背面のぼかしを計算し直していた（電力対策 2026-10-18）。 */
        .glass-panel {
            background:
                linear-gradient(180deg, rgba(255, 255, 255, 0.035) 0%, rgba(255, 255, 255, 0.008) 45%, rgba(255, 255, 255, 0) 100%),
                var(--surface);
            border: 1px solid var(--border); border-radius: 28px;
            box-shadow: 0 24px 60px rgba(0, 0, 0, 0.65), inset 0 1px 0 rgba(255, 255, 255, 0.07);
            transform: translateZ(0); -webkit-transform: translateZ(0); will-change: transform; -webkit-font-smoothing: subpixel-antialiased;
        }

        /* #IdleScreenOff: 長時間停止の警告バナー。非表示時はdisplay:noneでgapも消える */
        .idle-warn-banner {
            flex-shrink: 0; align-items: center; justify-content: center;
            padding: 9px 16px; border-radius: 14px;
            border: 1px solid rgba(255, 159, 10, 0.4); background: rgba(40, 26, 5, 0.55);
            color: #ff9f0a; font-size: 0.78rem; font-weight: 690; text-align: center;
            font-variant-numeric: tabular-nums;
        }

        /* ── Top Bar ─────────────────────────────────────────────────── */
        .top-bar { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 0 6px; flex-shrink: 0; min-height: 30px; }
        .brand { display: flex; align-items: center; gap: 9px; min-width: 0; flex: 1; }
        .line-color-bar { width: 4px; align-self: stretch; background: var(--line-color); border-radius: 999px; box-shadow: 0 0 10px var(--line-glow); transition: background 0.4s; flex-shrink: 0; }
        .app-title {
            font-size: 1.02rem; font-weight: 800; letter-spacing: 0.02em; color: var(--text-main);
            white-space: normal; word-break: keep-all; overflow-wrap: break-word;
            line-height: 1.2;
            /* B-1: 2行上限＋ellipsis。JSのfitLineText()（強制レイアウト5回）を削除 */
            display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
            overflow: hidden;
        }
        .gps-status {
            display: flex; flex-direction: column; align-items: center; gap: 1px; flex-shrink: 0;
            font-variant-numeric: tabular-nums; line-height: 1.15;
            padding: 5px 8px; border-radius: 999px;
            background: rgba(255, 255, 255, 0.035);
            border: 1.5px solid var(--gps-ring, rgba(255,255,255,0.07));
            box-shadow: 0 0 8px var(--gps-glow, transparent), inset 0 0 6px var(--gps-glow, transparent);
            transition: border-color 0.6s ease, box-shadow 0.6s ease;
        }
        .gps-label { font-size: 0.50rem; font-weight: 690; letter-spacing: 0.06em; color: var(--gps-label-color, var(--text-dim)); transition: color 0.6s ease; }
        .gps-value { font-size: 0.60rem; font-weight: 690; color: var(--gps-value-color, var(--text-sub)); transition: color 0.6s ease; }

        /* ── Hero: Digital Cluster ───────────────────────────────────── */
        .hero-card { padding: 18px 22px 16px; flex-shrink: 0; position: relative; overflow: hidden; }
        .hero-card::before {
            content: ''; position: absolute; top: 0; left: 18%; right: 18%; height: 1px;
            background: linear-gradient(90deg, transparent, var(--line-color) 50%, transparent);
            opacity: 0.85; pointer-events: none;
        }
        .hero-card::after {
            content: ''; position: absolute; top: -55px; left: 50%; transform: translateX(-50%);
            width: 300px; height: 110px; border-radius: 50%;
            background: radial-gradient(ellipse at center, var(--line-glow), transparent 70%);
            opacity: 0.22; pointer-events: none;
        }

        .speed-row { display: flex; justify-content: space-between; align-items: flex-end; gap: 8px; position: relative; z-index: 1; }
        .speed-cluster { display: flex; align-items: baseline; gap: 6px; flex-shrink: 0; }
        /* Flexレイアウトが使う「箱」（固定幅）と、視覚的に圧縮描画する内側要素（transform）を分離。
           transform はレイアウトサイズに影響しないため、箱側で幅を固定しないと桁数変化や
           scaleX の圧縮率とレイアウト位置がズレ、km/h との間に不自然な隙間が生じる。 */
        .speed-number-slot {
            /* text-align:right は内容が箱より広い場合に効果を持たず左詰めのまま描画されるため、
               Flexboxのjustify-content:flex-end（オーバーフローをstart側=左に逃がす仕様）を使用する。
               こうすることで scaleX の transform-origin:right の基準点が確実にスロット右端と一致する。 */
            display: flex;
            justify-content: flex-end;
            align-items: baseline;
            flex-shrink: 0;
            width: 9rem;         /* 3桁圧縮後の視覚幅に合わせた固定値（rem=ルート基準、Chrome実測で調整） */
        }
        .speed-number {
            font-family: var(--font-display);
            /* #FontMetrics: 新ゴの数字は 0.815em 幅で、-0.08em の詰めと scaleX(0.80) で
               1桁あたり 0.588em に収めていた。M PLUS の数字は 0.620em と元から細いので、
               同じ詰め・圧縮をかけると 0.432em(26%細)になり大型数字の存在感が失われる。
               詰めをほぼ外し圧縮を緩めて (0.620-0.01)*0.95 = 0.580em ≒ 旧 0.588em に合わせる。
               3桁でも 0.580*3*80px = 139px < スロット 9rem(144px) で収まる。 */
            font-size: 5rem; font-weight: 800; line-height: 1; letter-spacing: -0.01em;
            font-variant-numeric: tabular-nums;
            background: linear-gradient(180deg, #FFFFFF 25%, #AEB9C9 100%);
            -webkit-background-clip: text; background-clip: text;
            -webkit-text-fill-color: transparent;
            filter: drop-shadow(0 0 22px var(--theme-glow));
            display: inline-block;
            padding-right: 0.1em;
            margin-right: 0rem; /* 実機フォント描画差分（ヒンティング等）に対する安全マージン。km/hとの重なり防止 */
            transform: scaleX(0.95); transform-origin: right center; /* 圧縮の基準点をスロット右端に固定 */
        }
        .speed-unit {
            font-family: var(--font-display);
            font-size: 0.95rem; font-weight: 690; letter-spacing: 0.08em;
            color: var(--text-sub);
            flex-shrink: 0;
        }

        .dist-block { text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; position: relative; z-index: 1; flex-shrink: 0; }
        /* #DimContrast: 'NEXT' は 9.9dp＝視距離350mmで視角15分と最小の文字。
           快適読解域(20〜30分)を下回るぶんコントラストで補い、--text-sub(6.62) を使う。 */
        .dist-cap { font-family: var(--font-display); font-size: 0.62rem; font-weight: 690; letter-spacing: 0.3em; color: var(--text-sub); text-transform: uppercase; }
        /* #Elev: ヒーローカード NEXT 上の海抜・勾配（レイアウト安定のため visibility 切替） */
        .elev-grade { font-family: var(--font-display); font-size: 0.95rem; font-weight: 770; letter-spacing: 0.03em; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; margin-bottom: 0.15rem;
            /* #RowHeightLock: #ElevFit がフォントを縮めると行箱も縮み、カードが
               210px→203px と 7px 跳ねる。最大フォント基準で行箱を固定する。 */
            min-height: 1.16rem; line-height: 1.16rem; }
        /* #StopFlash: 停車カウンタ(控えめ)と発車時の停車時間フラッシュ(緑)。
           route-info 上に absolute 配置しヒーローカードの高さを一切変えない */
        .route-info { position: relative; }
        /* #StopFlash: 2レイヤー構造。カウンタ(白)は上方向へブラーアウトし、
           入れ替わりに停車時間フラッシュ(緑)が下からブラー解除しつつ浮上する */
        /* 区切り線と「到着」バッジの中間に来るよう line-height:1 で行箱を締めて配置 */
        .stop-overlay { position: absolute; top: 7px; height: 1em; pointer-events: none; font-size: 0.72rem; letter-spacing: 0.06em; font-variant-numeric: tabular-nums; line-height: 1; }
        .stop-overlay .so-count { position: absolute; top: 0; left: 0; transform: translateX(-50%); white-space: nowrap; opacity: 0; line-height: 1; }
        /* #DepartFlash: カプセルは常に駅名行の中に absolute で置く。
           左端(left)は JS が実測して与え、幅は内容なり（width:max-content）。
           次駅テキストの左端より右へは決して出さない。 */
        .depart-flash { position: absolute; left: 0; right: auto; top: 0; display: flex; pointer-events: none; }
        /* #DepartFlashFit: 段階2。カプセルが前駅名のスロットまで使うとき、
           前駅名はカプセル内に取り込まれて重複するのでフェードして譲る
           （カードを縦に伸ばすより、重複情報を消すほうが実害が小さい） */
        .hero-card.so-flash-live .st-name-small { opacity: 0; }
        /* #StopFlash: 縦積み構成。
           停車中: 元サイズの「到着」バッジはそのまま残し、直下に経過時間(白)を添える。
           発車: バッジ/矢印を透明化し、同エリアに『◯駅停車』(上段)+『◯分◯秒』(下段・大)の
           緑2段フラッシュがモーフ表示 → 4秒後フェードで ▶▶▶ 復帰 */
        .hero-card.so-live .animated-arrow::after { opacity: 0; }
        /* #DepartPill: 発車を検知して到着カウンタを畳んだ後も is-stopped は発車
           ヒステリシスぶん約4秒残る。so-live を外した瞬間に ::after の opacity が
           0→1 へ遷移し、カウンタを伴わない素の「到着」ピルだけが約2秒露出していた
           （実測: 発車 +2.03s に露出 → +4.06s に df-live が乗って消滅）。
           カウンタを畳むゲート(#DepartLive)とそろえてピルも畳む。 */
        .hero-card.dep-live .animated-arrow::after { opacity: 0; }
        /* #DepartFlashFit: 発車カプセルは矢印の中心に重ねて置く設計だが、
           背景が 94% 不透明なので下の矢印が透けて「25秒」と重なって見える
           （実測 412dp で顕著）。到着カウンタ側(so-live)には既に同種の規則が
           あるので、発車側にも入れる。位置決めが矢印の offsetLeft/offsetWidth に
           依存するため display:none ではなく opacity で消す。 */
        /* updateRouteUI が毎フレーム inline で opacity:0.9 を書くため !important が要る */
        .hero-card.df-live .animated-arrow { opacity: 0 !important; }
        .animated-arrow::after { transition: opacity 0.4s ease; }
        /* フラッシュ表示中も前後の駅名は完全表示（非表示・減光禁止・被り禁止）。
           長駅名時は駅名側もコンパクト化(縮小/2段)して中央にカプセル分の空きを作り、
           カプセルは実測ギャップ内に限定配置する */
        .st-name-small, .st-name-large { transition: opacity 0.35s ease; }
        .df-cap.compact { padding: 3px 9px 4px; gap: 1px; }
        .df-cap.compact .fl-time { font-size: 0.85rem; }
        .df-cap.compact .fl-sub { font-size: 0.5rem; margin-left: 4px; }
        .df-cap.compact .fl-station { letter-spacing: 0.06em; margin-right: 0; }
        /* 統一フレーム: 到着バッジと同じ書体・色文法の角丸カプセルに2段縦積み */
        /* 停車カプセル: 到着+経過時間の縦積み。色はテーマ変数でなく緑固定
           (発車過渡でテーマがオレンジへ戻る1秒間の変色防止)。背景は濃い不透明 */
        .so-count { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 6px 16px 7px; border-radius: 14px; border: 1px solid rgba(48, 209, 88, 0.55); background: rgba(9, 20, 13, 0.94); transition: opacity 0.3s ease, transform 0.4s ease, filter 0.4s ease; }
        .so-count .soc-badge { font-size: 0.72rem; font-weight: 770; letter-spacing: 0.28em; margin-right: -0.28em; color: #30d158; line-height: 1.2; }
        .so-count .soc-time { font-size: 0.84rem; font-weight: 690; color: var(--text-main); letter-spacing: 0.06em; font-variant-numeric: tabular-nums; line-height: 1.1; }
        .so-count.on { opacity: 1; }
        .so-count.out { opacity: 0; transform: translateX(-50%) translateY(-6px); filter: blur(3px); }
        /* #DepartFlash: 発車フラッシュカプセル(専用コンテナ内・flex中央) */
        /* #DepartFlashFit: flex-shrink:0 は必須。潰れることを許すと offsetWidth が
           コンテナ幅と一致してしまい「必要幅の実測」ができなくなる（＝入るか否かの
           判定が常に真になる）。カプセルは潰れず、入らなければ下段へ移る。 */
        .df-cap { display: flex; flex-direction: column; align-items: center; gap: 3px; padding: 6px 16px 7px; border-radius: 14px; width: max-content; flex-shrink: 0; border: 1px solid rgba(48, 209, 88, 0.55); background: rgba(9, 20, 13, 0.94); color: #30d158; box-shadow: 0 0 20px rgba(48, 209, 88, 0.18); opacity: 0; transition: opacity 0.45s ease; }
        .df-cap.on { opacity: 1; animation: soFlashIn 0.55s cubic-bezier(0.22, 1, 0.36, 1); }
        /* 駅名の最大幅はJSが固定pxで与える(親width:max-contentとの%循環で幅が潰れるため%指定禁止) */
        .df-cap .fl-station { font-size: 0.72rem; font-weight: 770; letter-spacing: 0.2em; margin-right: -0.2em; opacity: 0.92; line-height: 1.2; white-space: nowrap; display: block; text-align: center; }
        .df-cap .fl-time { display: flex; align-items: baseline; font-size: 1.05rem; font-weight: 770; letter-spacing: 0.04em; font-variant-numeric: tabular-nums; text-shadow: 0 0 15px rgba(48, 209, 88, 0.55); line-height: 1.1; }
        .df-cap .fl-sub { font-size: 0.6rem; font-weight: 770; letter-spacing: 0.14em; margin-left: 7px; opacity: 0.8; text-shadow: none; }
        /* #NameFit: 長駅名の段階対応。縮小しても収まらない場合は2段折返し */
        /* text-wrap:balance で2行を均等長に折る（「…末広/町）」のような孤立折り防止。未対応WebViewでは無視され従来折り） */
        .two-line { white-space: normal !important; overflow: hidden; text-overflow: clip !important; line-height: 1.14 !important; align-self: center; word-break: break-all; max-height: 2.35em; text-wrap: balance; }
        @keyframes soFlashIn {
            0% { opacity: 0; transform: scale(0.9); filter: blur(3px); }
            60% { opacity: 1; filter: blur(0); }
            100% { opacity: 1; transform: scale(1); filter: blur(0); }
        }
        /* #GraphSpanBar: 常設レンジバー。タイトル廃止・横幅いっぱい・ワンタップ切替 */
        .span-bar { width: 100%; display: flex; }
        .span-bar .sub-tab { flex: 1 1 0; text-align: center; white-space: nowrap; padding: 6px 0; font-size: 0.82rem; }
        /* #LegStats: グラフ内の駅間 表定速度オーバーレイ（下端・区間ブラケット付き） */
        .graph-leg-labels { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
        /* 速度線（緑）と重なっても読めるよう白＋暗い縁取り。左右の余白は JS 側で clamp */
        /* #LegAvgLine: 縦位置は JS が top で平均速度の高さに置く（bottom を残すと高さが伸びる） */
        /* #LegMarks: 平均速度の基準時刻（発車/到着、通過駅は通過時刻）を示す縦の補助線 */
        .graph-leg-mark { position: absolute; top: 0; bottom: 0; width: 0; border-left: 1px dashed rgba(255, 255, 255, 0.35); }
        .graph-leg { position: absolute; border-top: 1px solid rgba(255, 255, 255, 0.3); text-align: center; font-size: 0.6rem; font-weight: 690; color: #ffffff; text-shadow: 0 0 3px rgba(0, 0, 0, 0.95), 0 1px 2px rgba(0, 0, 0, 0.9); padding-top: 2px; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; }
        /* #LegLabel3Step 段階②: 数値と単位を2行に分ける。行間を詰めてグラフを侵さない。 */
        .graph-leg.two-line { white-space: normal; line-height: 1.02; }
        .graph-leg.two-line .lg-v { display: block; }
        .graph-leg.two-line .lg-u { display: block; font-size: 0.82em; opacity: 0.85; }
        .dist-val { font-family: var(--font-display); font-size: 1.95rem; font-weight: 770; font-variant-numeric: tabular-nums; line-height: 1; color: var(--text-main); transition: color 0.4s, text-shadow 0.4s; letter-spacing: -0.01em; transform: scaleX(1.0); transform-origin: right center; /* #FontMetrics: M PLUS の数字は新ゴより細いため詰め・圧縮を緩める */
            /* #RowHeightLock: line-height:1 のため #DistOverflow の縮小がそのまま
               行の高さを縮め、NEXT が 10km を跨ぐたびにカードが 7px 跳ねていた
               （実測 石勝線: 21.07km で 203px / 9.99km で 210px）。
               最大フォント(1.95rem)ぶんの高さで固定する。display は変えない
               （flex 化するとレイアウトの検証点が増えるだけで利得がない）。 */
            min-height: 1.95rem; }
        .dist-val span { font-family: var(--font-display); font-size: 0.85rem; font-weight: 690; color: var(--text-sub); margin-left: 2px; letter-spacing: 0; }
        /* #DistOverflow: 新幹線等で NEXT が2桁km以上（"20.50"=5文字〜）になると
           1.95rem 固定ではカード右端からはみ出すため、桁数に応じて縮小する */
        .dist-val.long  { font-size: 1.5rem; }
        .dist-val.xlong { font-size: 1.2rem; }

        .route-info { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-top: 15px; padding-top: 13px; border-top: 1px solid var(--border); position: relative; z-index: 1;
            /* #RowHeightLock: align-items:baseline はフォントサイズでベースライン位置が
               変わるため、#NameFit の縮小が入ると行箱が 1px 変動しカード全体が跳ねる
               （実測 209px ⇔ 210px）。最大値で固定する。 */
            min-height: 3.25rem; box-sizing: border-box; }
        /* 左右等幅カラム化: 駅名の長短に関わらず ▶▶▶ が常にカード中央に来る */
        .st-name-small { flex: 1 1 0; min-width: 0; font-size: 1.05rem; font-weight: 600; color: var(--text-sub); transition: opacity 0.4s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        /* #ArrowFont: M PLUS は ▶(U+25B6) を持つが ▷(U+25B7) を持たない。flowArrows は
           ▷▷▷ → ▶▷▷ → ▶▶▷ → ▶▶▶ と切り替わるので、既定の書体のままだと1コマの中で
           ▶ が M PLUS・▷ が端末標準と混在し、三角の大きさと太さが揃わない。
           この要素だけ書体を端末標準に固定して4コマとも同じ出どころにする。 */
        .animated-arrow { font-family: "Noto Sans JP", sans-serif; font-size: 1.15rem; color: var(--theme-text); letter-spacing: 2px; opacity: 0.9; transform: translateY(2px); transition: color 0.4s; text-shadow: 0 0 12px var(--theme-glow); flex-shrink: 0; }
        /* #DecorTick: 4コマの切替は JS（_decorTick）が 0.3秒ごとに data-arrow を書き換える。
           旧実装の CSS 無限アニメ（flowArrows 1.2s step-end）と同じ間隔・同じ順序 */
        .animated-arrow::after { content: attr(data-arrow); }
        /* #RowHeightLock: min-height が無いと、'方向判定中...' のように長い文字列を
           #NameFit が縮小したときだけ行箱が低くなり、駅名が入った瞬間に 13px 跳ねる。
           高さ固定は line-height で行う。display:flex にするとテキストが匿名フレックス
           アイテムになり scrollWidth が溢れを返さなくなるため、#NameFit の縮小判定
           (scrollWidth > clientWidth) が永久に偽になる。実測では14文字の駅名でも
           scrollWidth==clientWidth==115px で一度も縮小せず、justify-content:flex-end の
           ぶん溢れが左側で切られて「方向判定中...」の先頭2文字が消えていた。
           （起動直後に必ず1回発生していた）。1.6rem の通常行と同じ高さで固定する。 */
        .st-name-large { flex: 1 1 0; text-align: right; font-size: 1.6rem; font-weight: 800; letter-spacing: 0; transition: color 0.4s, text-shadow 0.4s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; min-height: 2.1rem; line-height: 2.1rem; }

        .progress-wrap { margin-top: 14px; position: relative; z-index: 1; }
        /* #ShortGlide: 測位ごとの補間（進捗バーの width・路線タブの点と線の top/height・グラフの線と面の d）は
           0.1秒。旧 0.3秒では動くたびに 0.3秒ぶん毎コマ描いていた（グラフタブ 29→14コマ/秒、Chrome 実測）。
           1秒あたりの移動はバーで中央値 3〜7px・速い場面で 17px 程度なので、0.1秒でも跳びは目立たない
           （電力対策 2026-10-18 ユーザー判断） */
        .progress-bg { height: 3px; background: rgba(255, 255, 255, 0.07); border-radius: 999px; }
        .progress-bar {
            position: relative; height: 100%; width: 0%; border-radius: 999px;
            background: var(--line-color);
            background: linear-gradient(90deg, color-mix(in srgb, var(--line-color) 30%, transparent), var(--line-color));
            box-shadow: 0 0 10px var(--line-glow);
            transition: width 0.1s linear, background 0.4s ease;
        }
        .progress-bar::after {
            content: ''; position: absolute; right: -3px; top: 50%; transform: translateY(-50%);
            width: 7px; height: 7px; border-radius: 50%;
            background: #FFFFFF;
            box-shadow: 0 0 8px var(--line-color), 0 0 16px var(--line-glow);
        }

        /* #StoppedFrom: 旧 opacity:0.25 では前駅のコントラスト比が 1.38 まで落ちていた。
           停車中こそ「どこから来たか」を確認したい場面なので基準を満たしたい。
           ただし --text-sub のまま不透明度を上げると 0.85 まで必要で、控えめにする
           設計意図が消える。基準色を --text-main に上げて不透明度で控える方式にすると
           0.55 で比 5.86 に到達し、走行中(6.62)よりわずかに引っ込んだ階調になる。
           強調は次駅（緑・800・25.6dp）が担うため階層は崩れない。 */
        .hero-card.is-stopped .st-name-small { color: var(--text-main); opacity: 0.55; }
        .hero-card.is-stopped .st-name-large { color: var(--theme-text); text-shadow: 0 0 24px var(--theme-glow); }
        .hero-card.is-stopped .dist-val { color: var(--theme-text); text-shadow: 0 0 18px var(--theme-glow); }
        .hero-card.is-stopped .animated-arrow::after {
            content: "到着"; animation: none;
            font-family: var(--font-family);
            font-size: 0.72rem; font-weight: 770; letter-spacing: 0.28em;
            color: var(--theme-text);
            background: var(--theme-bg);
            border: 1px solid var(--theme-glow);
            border-radius: 999px;
            padding: 4px 8px 4px 12px;
            display: inline-block; line-height: 1;
        }

        /* ── Main Tabs ───────────────────────────────────────────────── */
        .main-tabs {
            position: relative; display: flex; flex-shrink: 0;
            background: rgba(0, 0, 0, 0.38);
            border: 1px solid var(--border);
            border-radius: 15px; padding: 4px; margin: 0;
        }
        .main-tab {
            flex: 1; display: flex; align-items: center; justify-content: center; gap: 7px;
            padding: 9px 0; position: relative; z-index: 1;
            font-size: 0.83rem; font-weight: 690; letter-spacing: 0.04em;
            color: var(--text-sub); border-radius: 11px; cursor: pointer;
            transition: color 0.25s ease, background 0.25s ease;
        }
        .main-tab::before {
            content: ''; width: 15px; height: 15px; flex-shrink: 0;
            background: currentColor; opacity: 0.85;
            -webkit-mask: var(--tab-icon) center / contain no-repeat;
            mask: var(--tab-icon) center / contain no-repeat;
        }
        .main-tab[data-target="view-route"] { --tab-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' d='M12 7.8v8.4'/%3E%3Ccircle cx='12' cy='4.8' r='2.4' fill='white'/%3E%3Ccircle cx='12' cy='19.2' r='2.4' fill='white'/%3E%3C/svg%3E"); }
        .main-tab[data-target="view-speed"] { --tab-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='white' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round' d='M3 18l5.5-7.5 4.5 3.5L21 6'/%3E%3C/svg%3E"); }
        .main-tab[data-target="view-map"] { --tab-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='white' stroke-width='2' d='M12 21.5c-4.3-4-6.8-7.3-6.8-10.7a6.8 6.8 0 1 1 13.6 0c0 3.4-2.5 6.7-6.8 10.7z'/%3E%3Ccircle cx='12' cy='10.5' r='2.3' fill='white'/%3E%3C/svg%3E"); }
        .main-tab.active { background: rgba(255, 255, 255, 0.1); color: var(--text-main); }
        @supports selector(:has(*)) {
            .main-tab.active { background: transparent; }
            .main-tabs::before {
                content: ''; position: absolute; top: 4px; bottom: 4px; left: 4px;
                width: calc((100% - 8px) / 3); box-sizing: border-box; border-radius: 11px;
                background: linear-gradient(180deg, rgba(255, 255, 255, 0.13), rgba(255, 255, 255, 0.05));
                border: 1px solid rgba(255, 255, 255, 0.09);
                box-shadow: 0 3px 10px rgba(0, 0, 0, 0.45);
                transition: transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
            }
            .main-tabs:has(.main-tab:nth-child(2).active)::before { transform: translateX(100%); }
            .main-tabs:has(.main-tab:nth-child(3).active)::before { transform: translateX(200%); }
        }

        .tab-view { display: none; flex-direction: column; animation: fadeIn 0.25s ease; flex: 1; overflow: hidden; height: 100%; }
        .tab-view.active { display: flex; }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

        .forecast-card, .analysis-card, .map-card { padding: 0; flex: 1; display: flex; flex-direction: column; overflow: hidden; position: relative; height: 100%;}

        /* --- Timeline --- */
        .timeline-scroll { flex: 1; overflow-y: auto; position: relative; scrollbar-width: none; scroll-behavior: smooth; height: 100%;}
        .timeline-scroll::-webkit-scrollbar { display: none; }
        .timeline-container { position: relative; padding: 24px 20px 24px 0; display: flex; flex-direction: column;}

        .tl-base-line { position: absolute; left: 35px; top: 24px; bottom: 24px; width: 2px; border-radius: 1px; background: linear-gradient(180deg, transparent, rgba(255, 255, 255, 0.09) 6%, rgba(255, 255, 255, 0.09) 94%, transparent); }
        .tl-active-line {
            position: absolute; left: 35px; width: 2px; border-radius: 1px;
            background: var(--line-color);
            background: linear-gradient(180deg, var(--line-color), color-mix(in srgb, var(--line-color) 25%, transparent));
            box-shadow: 0 0 8px var(--line-glow);
            transition: top 0.1s linear, height 0.1s linear, background 0.4s ease;
        }
        .tl-train-dot {
            position: absolute; left: 36px; transform: translate(-50%, -50%);
            width: 15px; height: 15px; border-radius: 50%;
            background: #FFFFFF;
            box-shadow: 0 0 14px var(--line-color), 0 0 30px var(--line-glow);
            z-index: 10; transition: top 0.1s linear, box-shadow 0.4s;
        }
        /* #DecorTick: 波紋は JS（_decorTick）が 4.8秒ごとに ping-a / ping-b を付け替えて1回ずつ出す。
           停車中は出さない。旧実装は 1.6秒の無限アニメで、路線タブを開いている間は描画が途切れなかった。
           休みの間は輪を消しておく（波紋の終わりと同じ見え方） */
        .tl-train-dot::after {
            content: ''; position: absolute; inset: -4px; border: 2px solid var(--line-color); border-radius: 50%; opacity: 0; transition: border-color 0.4s;
        }
        .tl-train-dot.ping-a::after { animation: pingA 1.6s cubic-bezier(0, 0, 0.2, 1) 1; }
        .tl-train-dot.ping-b::after { animation: pingB 1.6s cubic-bezier(0, 0, 0.2, 1) 1; }
        @keyframes pingA { 0% { transform: scale(1); opacity: 1; } 75%, 100% { transform: scale(2); opacity: 0; } }
        @keyframes pingB { 0% { transform: scale(1); opacity: 1; } 75%, 100% { transform: scale(2); opacity: 0; } }

        .tl-item { display: flex; align-items: center; height: 65px; position: relative; z-index: 2; margin-bottom: 8px; flex-shrink: 0;}
        .tl-node-col { width: 72px; display: flex; justify-content: center; align-items: center; flex-shrink: 0; }
        .tl-node { width: 11px; height: 11px; border-radius: 50%; background: var(--bg-color); border: 2px solid var(--text-sub); transition: all 0.3s;}

        .tl-content { flex: 1; min-width: 0; display: flex; justify-content: space-between; align-items: center; gap: 8px; height: 100%; padding: 0 14px; border-radius: 16px; border: 1px solid transparent; transition: all 0.3s;}
        .tl-name { font-size: 1.15rem; font-weight: 600; color: var(--text-sub); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
            /* #NameFit: transition に font-size を含めてはいけない。含めると
               縮小のたびに 0.3s かけて補間され、getComputedStyle が古い値を返すので
               「縮めても scrollWidth が変わらない」と誤判定し、ループが下限まで走って
               6px の極小表示になる（実測: 11回ループしてもフォントは 22.4px のまま）。
               遷移完了後に極小値が現れ、別の呼び出しがリセットして 1秒ごとに
               「極小 ⇔ 省略(富山大…)」を往復していた。色だけ遷移させる。 */
            transition: color 0.3s;
            /* #NameFit: flex/min-width が無いと要素幅が中身に追従するため、
               フォントを縮めても scrollWidth > clientWidth が解消せず、
               縮小ループが下限まで走って 6px の極小表示になる。さらに
               updateRouteUI 側のキャッシュ破棄と往復して 1秒ごとに
               「極小 ⇔ 省略(富山大…)」を繰り返していた。
               .st-name-large と同じく残り幅を占める固定トラックにする。 */
            flex: 1 1 auto; min-width: 0; }
        .tl-dist-col { flex-shrink: 0; display: flex; align-items: center; }
        .tl-dist { font-family: var(--font-display); font-size: 1.25rem; font-weight: 770; color: var(--text-main); font-variant-numeric: tabular-nums; line-height: 1.1; transition: color 0.4s;}
        /* B-3 の span 分離（.tl-dist-num / .tl-dist-unit）に対応: 縮小スタイルは単位のみに適用。
           数字（.tl-dist-num）は .tl-dist の大きい書式をそのまま継承する。 */
        .tl-dist .tl-dist-unit { font-family: var(--font-display); font-size: 0.8rem; font-weight: 600; color: var(--text-sub); }

        .tl-item.passed .tl-node { border-color: var(--text-dim); background: transparent; }
        .tl-item.passed .tl-name, .tl-item.passed .tl-dist, .tl-item.passed .tl-dist span { color: var(--text-dim); }
        .tl-item.future .tl-name { color: var(--text-main); }
        /* #TlDirPending: 方向判定中の中立表示。前方/後方の区別をせず、全駅を future と同じ明るさで並べる */
        .tl-item.neutral .tl-name { color: var(--text-main); }

        .tl-item.target .tl-node { border-color: var(--theme-color); background: var(--theme-color); box-shadow: 0 0 10px var(--theme-glow); }
        .tl-item.target .tl-content { background: linear-gradient(90deg, var(--theme-bg) 0%, rgba(255, 255, 255, 0.02) 80%); border-color: rgba(255, 255, 255, 0.06); }
        .tl-item.target .tl-name { font-size: 1.4rem; font-weight: 800; color: var(--text-main); }
        .tl-item.target .tl-dist { color: var(--theme-text); font-size: 1.45rem; text-shadow: 0 0 14px var(--theme-glow); }
        /* #TargetGlowStop: 停車中は --theme-* が緑になるため、target（＝次の駅）まで
           緑に光ってしまう。通常の到着では次駅に target が付かないので露見しないが、
           起点側で停車している時（駅でアプリを起動した直後が典型）だけ「次の駅が緑」
           という誤った強調になる。停車中の緑は**到着した駅だけ**の言語にしたいので、
           target 側は色とグローを落とす（サイズ＝情報階層は維持）。 */
        body.is-stopped .tl-item.target:not(.arrived):not(.stopping) .tl-node {
            border-color: var(--text-sub); background: transparent; box-shadow: none;
        }
        body.is-stopped .tl-item.target:not(.arrived):not(.stopping) .tl-content {
            background: rgba(255, 255, 255, 0.02);
        }
        body.is-stopped .tl-item.target:not(.arrived):not(.stopping) .tl-dist {
            color: var(--text-main); text-shadow: none;
        }

        /* #NoArrivePulse: 旧実装はここに box-shadow の無限アニメ（arrivePulse 2s）があった。
           arrived は必ず stopping と一緒に付き、下の .tl-item.stopping .tl-content の
           box-shadow: ... !important がアニメより優先されるので、点滅は一度も画面に出ていなかった。
           見えないアニメのために停車中ずっと毎コマ描き直していたので外した（見た目は同じ） */
        .tl-item.arrived .tl-content { background: var(--theme-bg); border: 1px solid var(--theme-glow); box-shadow: 0 0 18px var(--theme-glow); }
        .tl-item.arrived .tl-name { font-size: 1.4rem; font-weight: 800; color: var(--theme-text); }
        .tl-item.arrived .tl-dist { opacity: 0; }
        .tl-item.stopping .tl-content {
            border: 1px solid var(--theme-color) !important;
            background: var(--theme-bg) !important;
            box-shadow: 0 0 20px var(--theme-glow) !important;
        }
        .tl-item.stopping .tl-name {
            color: var(--theme-text) !important;
        }
        .tl-item.stopping .tl-node { border-color: var(--theme-color); background: var(--theme-color); box-shadow: 0 0 10px var(--theme-glow); }

        /* #Corridor: 路線名切替の境界セパレータ（例: 黒磯の上に「東北本線」） */
        .tl-line-sep {
            display: flex; align-items: center; gap: 10px;
            padding: 0 14px 0 72px; margin: -2px 0 6px 0;
            position: relative; z-index: 2; flex-shrink: 0;
        }
        .tl-line-sep span {
            font-size: 0.72rem; font-weight: 690; letter-spacing: 0.12em;
            color: var(--text-dim); white-space: nowrap;
        }
        .tl-line-sep::before, .tl-line-sep::after {
            content: ''; height: 1px; background: var(--border); flex: 1;
        }

        /* --- View 2: Speed Graph --- */
        .analysis-card { padding: 16px 18px; display: flex; flex-direction: column;}
        .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
        .card-title { font-size: 1rem; font-weight: 770; letter-spacing: 0.01em; color: var(--text-main); }
        .sub-tabs { display: flex; background: rgba(0, 0, 0, 0.4); border: 1px solid var(--border); border-radius: 999px; padding: 3px; }
        .sub-tab { padding: 5px 13px; font-size: 0.8rem; font-weight: 690; color: var(--text-sub); border-radius: 999px; cursor: pointer; transition: all 0.2s ease; font-variant-numeric: tabular-nums; }
        .sub-tab.active { background: rgba(255, 255, 255, 0.13); color: var(--text-main); box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35); }

        .graph-wrapper { position: relative; flex: 1; display: flex; flex-direction: column; margin-top: 5px; margin-bottom: 5px;}
        .graph-y-labels { position: absolute; left: 0; top: 0; width: 35px; height: calc(100% - 25px); pointer-events: none; z-index: 10;}
        .label-y { position: absolute; width: 100%; text-align: right; padding-right: 8px; transform: translateY(-50%); font-family: var(--font-display); font-size: 11px; font-weight: 600; letter-spacing: 0.04em; color: var(--text-sub); font-variant-numeric: tabular-nums;}

        .graph-area { flex: 1; margin-left: 35px; margin-bottom: 25px; position: relative; }
        .graph-area svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: visible; }

        .graph-x-labels { position: absolute; bottom: 0; left: 35px; right: 0; height: 25px; display: flex; align-items: flex-end; pointer-events: none; z-index: 10;}
        .label-x { position: absolute; font-family: var(--font-display); font-size: 11px; font-weight: 600; letter-spacing: 0.04em; color: var(--text-sub); transform: translateX(-50%); white-space: nowrap;}
        .label-x.start { left: 0; transform: translateX(0); }
        .label-x.end { left: 100%; transform: translateX(-100%); }

        .grid-line { stroke: rgba(255, 255, 255, 0.07); stroke-width: 1; stroke-dasharray: 2 5; }
        .grid-axis { stroke: rgba(255, 255, 255, 0.28); stroke-width: 1; }
        .line-path { fill: none; stroke: url(#line-grad); stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; transition: d 0.1s linear; filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.18)); }
        .area-path { fill: url(#graph-grad); opacity: 0.25; transition: d 0.1s linear; }
        #graph-grad stop:first-child { stop-color: var(--theme-color); }

        .graph-station-labels { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 5; }
        .graph-stn-label { position: absolute; top: 0; bottom: 0; font-size: 12px; font-weight: 690; color: rgba(255, 255, 255, 0.92); writing-mode: vertical-rl; text-orientation: upright; /* #StnLabelUpright: mixed は縦書きの既定で、日本語は立てたまま欧文字だけを 90度倒す。「Jヴィレッジ」の J が寝るのはこれが原因（ブラウザ既定の挙動）。 upright なら欧文字・数字も立つ。該当は全8,499駅中41駅（ラテン15: Jヴィレッジ/JR難波/YRP野比 ほか、数字26: 南郷13丁目/空港第2ビル ほか）。 実機実測(枠364px)で最長の「トヨタモビリティ富山Gスクエア五福前（五福末広町）」が 297→300px、増加はどれも3〜12pxで全駅が枠に収まるため折り返しは不要 */ line-height: 1.1; transform: translateX(-50%); white-space: nowrap; text-shadow: 0 0 6px rgba(0, 0, 0, 1); }
        .station-marker-line { stroke: rgba(255, 255, 255, 0.5); stroke-width: 1; stroke-dasharray: 2 4; }

        /* --- View 3: Leaflet Map --- */
        #map { width: 100%; height: 100%; border-radius: 28px; background-color: #07090D; position: absolute; top: 0; left: 0; z-index: 1;}
        .map-card::after { content: ''; position: absolute; inset: 0; border-radius: 28px; border: 1px solid var(--border); pointer-events: none; z-index: 2; }

        /* --- Tooltip for Map --- */
        .station-tooltip {
            background: rgba(13, 16, 22, 0.88); border: 1px solid rgba(255, 255, 255, 0.16);
            color: var(--text-main); font-weight: 600; font-size: 0.72rem; letter-spacing: 0.01em;
            padding: 3px 9px; border-radius: 999px; box-shadow: 0 6px 14px rgba(0, 0, 0, 0.5);
            white-space: nowrap;
        }
        .leaflet-tooltip-left.station-tooltip::before, .leaflet-tooltip-right.station-tooltip::before,
        .leaflet-tooltip-top.station-tooltip::before, .leaflet-tooltip-bottom.station-tooltip::before {
            display: none;
        }
        .leaflet-control-attribution { background: rgba(7, 9, 13, 0.55) !important; color: var(--text-dim) !important; font-size: 9px !important; border-radius: 8px 0 0 0;
          /* #DataAttribution: 出典は CC BY 4.0 の条件なので省略表示にしない。1行に収まらない幅では折り返す */
          white-space: normal !important; line-height: 1.45 !important; max-width: calc(100% - 16px); text-align: right; padding: 2px 8px !important; }
        .leaflet-control-attribution a { color: var(--text-sub) !important; }

        svg.defs-only { position: absolute; width: 0; height: 0; }
