:root {
  --sal: env(safe-area-inset-left);
  --sar: env(safe-area-inset-right);
  --sat: env(safe-area-inset-top);
  --sab: env(safe-area-inset-bottom);
}

* {
  /* 遊戲全畫面必備的 UI 鎖定 */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
}

/* 唯一不吃上面那組鎖的東西：文字輸入欄。
   `*` 的 user-select/touch-callout 也會套到 @pixi/ui 在 focus 時丟進
   document.body 的隱藏 <input>（見 libs/pixi-text-field 的 TextField）以及各個 DOM overlay 表單，
   長按因此不會進入選字模式 → 手機端完全叫不出「貼上」氣泡。

   只放行真的會打字的型別：checkbox / radio / button / file 沒有可選取的文字，
   解鎖它們只是白白把手勢還給瀏覽器。`:not([type])` 不可少——@pixi/ui 的
   `document.createElement("input")` 不帶 type，TextField 也只在
   fieldOptions.type 有值時才補，所以擷取欄可能整場都沒有 type 屬性。

   注意這裡**不動 `touch-action`**：長按選字與拖曳選取把手不需要它，而在 iOS
   上 `touch-action: auto` 會替欄位開回雙指／雙擊縮放——那正是「輸入模式下頁面
   尺寸變更」的另一條路徑。已在 Android 模擬器（Chrome 109）實測 touch-action:
   none 之下長按選單、Select all、拖曳選取把手（[0,11] → [0,4]）與
   Copy→Paste 往返全部正常。 */
input:is(
    :not([type]),
    [type="text"],
    [type="tel"],
    [type="password"],
    [type="number"],
    [type="email"],
    [type="search"],
    [type="url"]
  ),
textarea {
  -webkit-user-select: auto;
  user-select: auto;
  -webkit-touch-callout: default;
}

/* iOS Safari 對「focus 當下 computed font-size < 16px」的欄位會自動放大整頁
   ——那就是「輸入模式下頁面尺寸變更」在 iOS 上的形態。`@pixi/ui` 的
   Input.createInputField 只寫 position/size/opacity/border/background，從不設
   font-size，所以它建立的擷取用 <input> 吃 UA 預設約 13.3px。

   必須是 CSS 不能是 JS：createInputField 在 `document.body.appendChild(input)`
   之後**同步**呼叫 `input.focus()`，而且要到 focus 之後才 `this.input = input`
   ——所以 TextField._startEditing 拿到元素時已經太遲，寫 inline style 補不回來。
   規則放在樣式表，元素一 append 就是 16px。

   `body > input` 精準命中「直接掛在 body 的隱藏輔助 input」：@pixi/ui 的擷取欄、
   idPictureRow 的 display:none 檔案選取器。promo / wallet deposit /
   personal-data 的可見欄位都包在各自的 root 容器內，不受影響。
   ⚠ 這是「魔法路徑」，相依於 @pixi/ui 把 input append 到 body。TextField.test.ts
   有一則測試守住這個前提，升版改了結構會先炸在那裡。真的炸了怎麼辦：
   1. 先看新版有沒有給 class/id——**目前沒有**（createInputField 只寫 inline
      style，不碰 className/id），所以今天沒有比這更穩的鉤子；有了就換過去。
   2. 都沒有的話，TextField 可以自己覆寫 createInputField() 接管建立流程，但
      連 focus 時序也得一起承擔（focus 是在 append 之後同步發生的）。 */
body > input {
  font-size: 16px;
}

/* TextField 編輯中顯示的是真的 <input>（Pixi 字藏起來），但密碼欄的 type 故意
   留 text——`type="password"` 會引來 Chrome 的「要更新密碼嗎？」提示（見
   TextField._startEditing）。遮罩改由 CSS 做，`data-secure` 跟著 `secure`／
   眼睛切換。Firefox 138 起才支援 -webkit-text-security；本專案手機端只有
   Safari／Chrome。 */
input[data-secure] {
  -webkit-text-security: disc;
}

/* 有內容的密碼欄不給選取。選取工具列與右鍵選單只有在「有選取內容」時才會給
   「複製／剪下」，所以有值時關掉 user-select，那兩顆就不會出現；空欄位維持可選，
   長按才叫得出「貼上」氣泡——那正是上面那條 `input:is(…)` 規則存在的理由。
   `data-has-value` 由 TextField._startEditing 跟著值同步（見該處註解）。

   為什麼不直接用原生 `type="password"`（瀏覽器自己就不給密碼欄複製）：那會把
   Chrome 密碼管理員的「要更新密碼嗎?」提示帶回來，而且換不到任何好處——`@pixi/ui`
   的 input 是 focus 才建、blur 就丟，帳密兩欄不並存，Chrome 無從配對憑證，存了也
   不會回填。詳見 tasks/active/app-ops-infrastructure-260805.md。

   ⚠ 這條只擋得掉**長按進選字模式**那條路。2026-09-17 在 Pixel 3a（Android 13、
   Chrome 109）實測：長按只剩 Paste / Select all，但按下 Select all 選取照樣成立，
   Cut / Copy / Paste / Read aloud 全部回來——IME 的 Select all 指令不看 user-select。
   那條路由 `libs/pw-guards` 的 select 守衛收合選取來擋，兩者缺一不可。

   `libs/pw-guards` 也照擋 copy/cut/dragstart 事件，職責不重複：事件守衛管
   「按下去拿不到東西」，這條與 select 守衛管「按鈕不要出現」。

   代價：欄位已經有字時沒辦法長按貼上覆蓋，要先清空。 */
input[data-password][data-has-value] {
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

html,
body {
  overflow: hidden !important;
  height: 100% !important;
  margin: 0;
  padding: 0;
  background-color: #000000;
  position: fixed;
}

/* 頁面托底：海島橫版底圖鋪滿整個視窗，canvas 直接壓在上面。
   直版欄以外的區域(桌機上是左右兩條)本來是 renderer 的清除色，現在改成透出這張圖。

   用登入頁的橫版底圖，跟大廳直版的 `Background_Straightl01.png`（2026-09-23 起
   MainScreen 也改吃這張共用檔，原本獨立的 `BG_Lobby.png` 已移除）是同一個海島
   場景(沙灘＋木棧道)，所以側邊看起來像同一個畫面延伸出去，而不是另一張不相干的圖。

   兩個前提，缺一就看不到：
   1. `engine.init` 必須帶 `backgroundAlpha: 0`(見 src/main.ts)——canvas 是滿版的，
      不透明的 renderer 會整片蓋住這裡。
   2. 手機直版下畫布本來就填滿螢幕，這張圖不會露出來；這是桌機/寬視窗才看得到的裝飾。

   ⚠ **這條路徑已經無聲斷過兩次，不是假設性的風險。** `assets/**` 是 AssetPack 從
   `raw-assets/` 產生的(public/assets 在 .gitignore 裡)：素材一旦改名或搬家，不會有
   任何建置錯誤或主控台訊息，只會靜默變回純黑底。

   2026-09-16 `3cb0d5fb`(整併 raw-assets 目錄)把這張圖從 `sprites/login/` 搬到
   `sprites/background/`，Pixi 那邊毫髮無傷——程式用的是 `Texture.from("檔名.png")`
   這種 bare-filename alias，跟路徑無關；只有這裡的 CSS 斷掉。

   2026-09-17：這裡曾經寫成**帶開頭斜線的絕對路徑**(`/assets/...`)。dev/正式站是每個
   版本各自一個子資料夾(`index.html` 用 `<base href="./<version>/">` 指過去)，帶開頭
   斜線的路徑會蓋掉 base、直接打站台根目錄，該版本子資料夾底下的檔案就 404。**這裡必須
   用不帶開頭斜線的相對路徑**——CSS 的 `url()` 是相對這份 style.css 自己的位置解析，不受
   `<base href>` 影響，跟著版本子資料夾走才會對。

   所以動 `raw-assets/sprites/background/Background_Horizontal01.png` 的位置或檔名時，
   **必須一起改這裡**；改這裡的路徑寫法時，**不要加開頭斜線**。真的想根治，就得把托底
   改成 Pixi 的滿版 sprite(用 bare alias，找不到會直接報錯)，代價是它就不再是「網頁的
   背景」而是 app 內容的一部分。 */
body {
  /* 兩層：上層是壓暗用的漸層，下層才是圖。
     漸層是橢圓放射狀、中央最深往外淡出——中央那塊正好被直版畫布蓋住，所以實際看到的
     是「緊貼畫布邊緣最暗、往螢幕兩側逐漸變亮」的霧化黑邊，同時整張圖的亮度整體被壓下去，
     不會跟畫布裡的內容搶目光。

     三個 alpha 就是全部的調整鈕：0.28＝貼齊畫布處、0.22＝中段、0.16＝螢幕最外緣。
     數字越大越暗。三個一起加減＝整體亮度；只動第一個＝霧化黑邊的濃度。
     (2026-09-16 由使用者用即時滑桿實際拉出來的值，不是估的。)
     22% 那個位置停點大致對齊直版欄的邊緣(1280px 寬時欄寬約 500px)，但欄寬會隨視窗變，
     所以它是近似值不是精準對齊——這是裝飾用的托底，不需要也做不到跟著欄寬跑。 */
  background-image:
    radial-gradient(
      ellipse 70% 90% at 50% 50%,
      rgba(0, 0, 0, 0.28) 22%,
      rgba(0, 0, 0, 0.22) 42%,
      rgba(0, 0, 0, 0.16) 100%
    ),
    url("assets/sprites/background/Background_Horizontal01.webp");
  /* 兩個值＝兩層，順序對應上面的 background-image(漸層在前、圖在後)。

     漸層層 `100% 100%` + `center center`：固定貼齊視窗，霧化黑邊的中心才會落在畫布
     中央。**不能讓它跟著圖一起放大**，否則暗環中心被推到視窗下方。

     圖層 `auto 120%` + `center top`：高度放大到視窗的 1.2 倍並靠上對齊，等於只取原圖
     上緣約 83%——木棧道落到視窗外，只留天空、雲與一線海平面。120 越大天空佔比越高。
     (原圖 1920×1080，木棧道約在 81% 高度處開始。)

     ⚠ 用 `auto 150%` 而不是 `cover`：`cover` 永遠會讓某一邊剛好貼齊，一般桌機比例下
     貼齊的是高度，於是整張圖(含木棧道)一定全露、`background-position` 也就完全沒有
     可位移的空間。代價是超寬螢幕(寬高比 > 2.67)兩側會露出 body 的黑底。 */
  background-position:
    center center,
    center top;
  background-size:
    100% 100%,
    auto 120%;
  background-repeat: no-repeat;
}

/* 主畫面卡片陰影(`#game-shadow`,index.html)。真的 CSS box-shadow,由
   MainScreen.resize() 把它的 left/top/width/height 對齊 Pixi §0 design canvas
   的方框(取代原本 Pixi 端每幀重跑一次 GPU 高斯模糊的 BlurFilter 做法——box-shadow
   由瀏覽器 compositor 處理,只有這個 div 的幾何值真的變動(即 resize 當下)才會
   重算,不是每幀)。

   `#pixi-container > canvas` 的 `position: relative` 不是多餘的——canvas 預設
   `position: static`,而 CSS 疊層順序(CSS2.1 附錄 E)規定:任何「有定位」元素
   (position 非 static,即使 z-index 是 auto)一定畫在所有「無定位、正常流」元素
   之上,不管 DOM 先後。`#game-shadow` 要疊在 canvas 下面,勢必得是「有定位」
   元素(box-shadow 才能貼著任意矩形位置),那就只能讓 canvas 也加入「有定位」
   這一組,兩者才會回到用 DOM 順序(`#game-shadow` 排在 `#pixi-container` 前面)
   決定疊層。這裡沒有加 top/left,對版面沒有任何位移影響。 */
#pixi-container > canvas {
  position: relative;
}

#game-shadow {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;
  display: none;
  pointer-events: none;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
}

/* Webview iframe — overlaid on PixiJS canvas */
#webview-iframe {
  position: fixed;
  left: 0;
  width: 100%;
  border: none;
  z-index: 10;
  touch-action: auto;
  -webkit-user-select: auto;
  user-select: auto;
}

/* ── Boot splash: instant pre-Pixi loading screen (see src/app/bootSplash.ts) ── */
#boot-splash {
  position: fixed;
  inset: 0;
  z-index: 5; /* above the static canvas; below #webview-iframe (z-index 10) */
  display: flex;
  align-items: center;
  justify-content: center;
  /* ⚠ 這裡原本寫「MATCHES the engine clear color so handoff doesn't shift」——那句
     已經不成立。engine 現在是 `backgroundAlpha: 0`(見 src/main.ts)，清除色不再被畫
     出來，canvas 背後是 body 的天空托底，所以交接會是「深灰 → 淡出 → 藍天」，有一次
     看得見的顏色跳動。2026-09-16 經使用者裁定**維持現狀**：不為了無縫而把 splash 也
     鋪成天空(黃色徽章壓在亮藍天上可讀性會變差)。要改的話見 style.css 的 body 托底。 */
  background: #1e1e1e;
  /* The fill has already reached 100% by the time this runs (bootSplash.ts waits
     for it), so the fade only has to get out of the way. */
  transition: opacity 300ms ease;
}
#boot-splash.is-hidden {
  opacity: 0;
  pointer-events: none;
}
.boot-splash__inner {
  position: relative;
  /* matched to the Pixi LAKI badge (182px art * 0.8); verify in browser smoke */
  width: 146px;
  height: 146px;
  transform-origin: center;
}
/* The DIM badge. Dimmed here (no JS) so the first painted frame already shows
   the logo — .boot-fill then paints the LIT part on top. See app/bootWave.ts. */
.boot-logo {
  display: block;
  width: 100%;
  height: auto;
  opacity: 0.22;
}
/* Carries the liquid AND the percentage — the readout is drawn twice in here
   (dimmed, then full inside the wave clip) so it darkens with the badge instead
   of floating at full brightness over it. Styling lives in app/bootWave.ts. */
.boot-fill {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
