Fix rail rendering under the Android status bar

targetSdk 36 (Android 15+) forces edge-to-edge, so the fixed-position
top rail was drawing behind the status bar clock/battery icons on a
real device (title and DARK/LIGHT toggle both obscured) — invisible on
desktop since there's no inset there. Add env(safe-area-inset-top) via
a --rail-h variable used everywhere the rail's height was hardcoded as
42px, plus viewport-fit=cover so the inset actually gets populated.

Verified on a physical Galaxy Z Fold over adb.
This commit is contained in:
Dongubak
2026-08-23 15:11:05 +09:00
parent 31cfed7023
commit 2f570681eb
+10 -6
View File
@@ -2,7 +2,7 @@
<html lang="ko"> <html lang="ko">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<!-- user-scalable=no: the canvas has its own pinch-to-zoom (OrbitControls) — <!-- user-scalable=no: the canvas has its own pinch-to-zoom (OrbitControls) —
letting the OS page-zoom fight it over the same two-finger gesture is worse letting the OS page-zoom fight it over the same two-finger gesture is worse
than losing native page zoom, which this app has no text layout to need anyway. --> than losing native page zoom, which this app has no text layout to need anyway. -->
@@ -22,6 +22,9 @@ try {
Hierarchy comes from hairlines, scale and space alone. Hierarchy comes from hairlines, scale and space alone.
The only color in the app is marker identity (viewport + its legend). ── */ The only color in the app is marker identity (viewport + its legend). ── */
:root{ :root{
/* rail height + the device's own status-bar/notch inset (0 on desktop) —
every fixed element anchored below the rail must offset by this, not 42px */
--rail-h:calc(42px + env(safe-area-inset-top));
--bg:#0a0b0c; /* viewport ground, neutral near-black */ --bg:#0a0b0c; /* viewport ground, neutral near-black */
--panel:#0c0d0f; --panel:#0c0d0f;
--line:rgba(255,255,255,0.10); --line:rgba(255,255,255,0.10);
@@ -64,9 +67,10 @@ try {
/* ── Top rail: identity, session telemetry ── */ /* ── Top rail: identity, session telemetry ── */
#rail{ #rail{
position:fixed; top:0; left:0; right:0; height:42px; z-index:11; position:fixed; top:0; left:0; right:0; height:var(--rail-h); z-index:11;
padding-top:env(safe-area-inset-top); /* status bar/notch — the 42px content stays below it */
background:var(--panel); border-bottom:1px solid var(--line); background:var(--panel); border-bottom:1px solid var(--line);
display:flex; align-items:center; justify-content:space-between; padding:0 16px; gap:16px; display:flex; align-items:center; justify-content:space-between; padding-left:16px; padding-right:16px; gap:16px;
} }
#rail .mark{ display:flex; align-items:baseline; gap:11px; min-width:0; } #rail .mark{ display:flex; align-items:baseline; gap:11px; min-width:0; }
/* drawer toggle — only shown under the small-screen media query below */ /* drawer toggle — only shown under the small-screen media query below */
@@ -103,7 +107,7 @@ try {
/* ── Docked instrument column ── */ /* ── Docked instrument column ── */
#panel{ #panel{
position:fixed; top:42px; left:0; bottom:0; z-index:10; width:284px; position:fixed; top:var(--rail-h); left:0; bottom:0; z-index:10; width:284px;
background:var(--panel); border-right:1px solid var(--line); background:var(--panel); border-right:1px solid var(--line);
display:flex; flex-direction:column; overflow-y:auto; display:flex; flex-direction:column; overflow-y:auto;
} }
@@ -236,7 +240,7 @@ try {
#loading.empty .load-live{ display:none; } #loading.empty .load-live{ display:none; }
#loading.empty .prompt{ display:flex; pointer-events:auto; } #loading.empty .prompt{ display:flex; pointer-events:auto; }
/* the "no cloud yet" state must not block the dashboard — you can go live without a file */ /* the "no cloud yet" state must not block the dashboard — you can go live without a file */
#loading.empty{ top:42px; left:284px; background:transparent; pointer-events:none; } #loading.empty{ top:var(--rail-h); left:284px; background:transparent; pointer-events:none; }
#loading .glyph{ font-size:10px; color:var(--faint); letter-spacing:0.5em; text-indent:0.5em; } #loading .glyph{ font-size:10px; color:var(--faint); letter-spacing:0.5em; text-indent:0.5em; }
/* ── drag & drop overlay ── */ /* ── drag & drop overlay ── */
@@ -259,7 +263,7 @@ try {
} }
#panel.open{ transform:translateX(0); } #panel.open{ transform:translateX(0); }
#panelBackdrop{ #panelBackdrop{
display:block; position:fixed; top:42px; left:0; right:0; bottom:0; z-index:9; display:block; position:fixed; top:var(--rail-h); left:0; right:0; bottom:0; z-index:9;
background:var(--overlay); opacity:0; pointer-events:none; transition:opacity .18s ease; background:var(--overlay); opacity:0; pointer-events:none; transition:opacity .18s ease;
} }
#panelBackdrop.show{ opacity:1; pointer-events:auto; } #panelBackdrop.show{ opacity:1; pointer-events:auto; }