/* =========================================================
   "Nhật ký truyền tin" — không bong bóng, không khung,
   không một chữ hướng dẫn. Nền đen, chữ máy, chấm tròn.
   ========================================================= */

/* Bản TỐI: xanh đen chứ không đen tuyền. Đen tuyệt đối (#05060a cũ) cộng viền xám
   nâu làm cả app trông như màn hình máy cũ — sạm và bẩn. Xanh đen + xám lam thì
   vẫn tối mà trong trẻo, cùng họ màu với cảnh trời cỏ ở cửa hang. */
:root {
  --page: #070a0f;        /* nền TRANG, hai bên khung app trên màn rộng */
  --bg: #0d1117;
  --panel: #141b24;
  --panel-2: #1d2631;
  --line: #27313f;
  --text: #e6edf3;
  --dim: #8b98a8;
  --dimmer: #5b6674;
  --accent: #2dd4bf;      /* xanh ngọc, tươi hơn hẳn xanh lam phát quang cũ */
  --accent-ink: #04211d;
  --mine: #8b98a8;        /* lời mình: lùi về sau */
  --theirs: #e6edf3;      /* lời họ: nổi lên trước */
  --danger: #ff7b72;
  --ok: #3fdd97;
  --warn: #e3b341;
  /* Tim: hồng sen sáng. Đỏ thuần trên nền đen là tối om với người khó phân biệt màu
     (mắt họ nhận rất ít ánh đỏ); hồng sen có sẵn phần xanh lam nên vẫn SÁNG với mọi
     kiểu mù màu, mà nhìn vẫn ra trái tim. Đổi màu thì sửa đúng hai dòng --heart này. */
  --heart: #ff6bd6;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, "Roboto Mono", monospace;
  /* 3px như trước là góc gần vuông — đó là thứ làm cả app trông cũ. */
  --radius: 10px;

  /* ---------- MỘT NẤC CHỮ ----------
     Mọi cỡ chữ ĐỌC ĐƯỢC trong app đều nhân với biến này: `calc(12px * var(--fs))`.
     Muốn to/nhỏ cả app thì sửa đúng ba con số dưới đây, không phải đi dò 49 chỗ.
     Cảnh vật (mây, hàng cây, con Rùa, cà rốt, emoji trong nút) CỐ Ý không nhân —
     chúng là tranh chứ không phải chữ, phóng theo là vỡ bố cục.
     Ba mức khác nhau vì ba khoảng cách mắt khác nhau: điện thoại cầm gần nhất,
     tablet để xa hơn nên phải bù to nhất, máy tính ngồi thẳng ở giữa. */
  --fs: 1.15;              /* máy tính */
}
/* Tablet cao hẳn hơn hai mức kia: màn rộng nên chữ nhỏ trôi giữa một khoảng trống
   lớn, mắt phải dí vào mới đọc — mà tablet lại là thứ để xa nhất trong ba loại.
   Rộng chữ thì cột thanh bên cũng phải nới theo, không thì "Đăng xuất" cấn mép. */
@media (min-width: 721px) and (max-width: 1024px) {
  :root { --fs: 1.35; }
  .app { grid-template-columns: 90px 1fr; }
  .me-text { left: 90px; }
  .logout-btn { max-width: 84px; }
}
@media (max-width: 720px)  { :root { --fs: 1.12; } }                          /* điện thoại */
/* Bản ĐẤT — MẶC ĐỊNH. Gõ đúng chìa là chui vào hang, mà hang thì nằm dưới lòng
   đất: nên trong hang phải là màu đất, ăn thẳng với cảnh cắt dọc ở cửa hang.
   Nhấn màu cà rốt 🥕 — thứ duy nhất sáng rực trong một cái hang. */
:root[data-theme="earth"] {
  --page: #241a11;        /* đất sâu, hai bên khung app trên màn rộng */
  /* Khoang chat sáng hơn hẳn phần còn lại (#33241a của bản trước tối quá, ngồi đọc
     lời nhắn như ngồi trong hầm). Thanh bên thì sẫm hơn khoang chat — hai mảng phải
     tách nhau ra, không thì cả app là một khối nâu liền. */
  --bg: #4b3624;
  --panel: #382718;
  --panel-2: #5d462f;
  --line: #6a5138;
  --text: #f7ecda;
  /* Sáng hơn bản trước hẳn một bậc: trên nền nâu, chữ phụ mà mờ nữa thì đọc không
     ra — nhất là ngoài trời với màn điện thoại. */
  --dim: #d4c2ab;
  --dimmer: #a68f76;
  --accent: #f0a04b;      /* cà rốt 🥕 — thứ sáng rực nhất trong một cái hang */
  --accent-ink: #2b1a0b;
  --mine: #c0aa8f;
  --theirs: #f7ecda;
  --danger: #ff8f7a;
  --ok: #a8d06a;
  --warn: #f0c74b;
  --heart: #ff85c8;
}
/* Bản SÁNG: trắng và xám lam, không còn tông giấy ngả vàng (#f6f5f1 cũ) — nền be
   cộng chữ nâu đen là màu của giấy cũ, nhìn đục và nặng. */
:root[data-theme="light"] {
  --page: #eaeff5;
  --bg: #ffffff;
  --panel: #f4f7fa;
  --panel-2: #e9eff6;
  --line: #dde5ee;
  --text: #101828;
  --dim: #667085;
  --dimmer: #98a2b3;
  --accent: #0d9488;
  --accent-ink: #ffffff;
  --mine: #667085;
  --theirs: #101828;
  --danger: #d92d20;
  --ok: #12b76a;
  --warn: #dc6803;
  --heart: #db2777;   /* nền sáng thì tim phải đậm mới nổi */
}

* { box-sizing: border-box; }
/* iPhone hiểu "chạm hai cái liên tiếp" là lệnh phóng to trang. Thả tim hai phát là
   màn hình nhảy zoom. Trước đây chỉ dán lên nút và ô nhập, nên chạm hai lần vào
   khoảng trống — nền hang, khung lời nhắn, cảnh Rùa–Thỏ — vẫn phóng to như thường.
   pan-x pan-y = cuộn thì cứ cuộn, còn phóng to (cả double-tap lẫn hai ngón) thì thôi. */
html, body { touch-action: pan-x pan-y; }
button, input, textarea { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
[hidden] { display: none !important; }
/* ĐANG DỌN MÁY: trang chắc chắn sắp nạp lại (xem wipeThenReload trong app.js), nên
   cấm chạm vào bất cứ thứ gì. Không cấm thì người ta kịp chạm vào hang, bàn phím bật
   lên, rồi cú nạp ập tới cất nó đi — nhìn ra ngoài đúng như "bật rồi tắt keyboard miết".
   !important vì đây là cái chốt cuối, phải thắng mọi luật pointer-events khác trong
   file này (.me-text, .msg .del, .lock-go…); nó chỉ sống được vài trăm mili giây rồi
   cả trang biến mất. */
:root.wiping, :root.wiping * { pointer-events: none !important; }
/* Mờ đi một nhịp: cho mắt biết máy đang làm việc, đừng bấm nữa. */
:root.wiping body { opacity: .5; transition: opacity .18s ease; }
html, body { height: 100%; overflow: hidden; }   /* chỉ .messages được cuộn, trang thì không */
body {
  margin: 0;
  font: calc(13.5px * var(--fs))/1.65 var(--mono);
  background: var(--page);
  color: var(--text);
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
svg { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
::selection { background: var(--accent); color: var(--accent-ink); }

/* ---------- bố cục: một dải chấm + một cột log ---------- */
/* Ghim app đúng bằng vùng NHÌN THẤY. --vh/--vtop do fitViewport() trong app.js đo bằng
   visualViewport: iOS không co trang khi bàn phím bật lên mà đẩy cả trang trượt lên,
   nên nếu chỉ tin vào 100dvh thì đầu phòng chat trôi mất, ô nhập chui sau bàn phím. */
/* Trên màn hình rộng thì KHÔNG giãn hết cỡ: dòng chữ kéo dài cả mét đọc rất mệt,
   mà app này vốn là một cột nhật ký. Chốt ở 768px rồi canh giữa — màn 768 trở
   xuống thì y như cũ, không đổi gì.
   Đổi số này thì phải đổi CẢ BA chỗ cùng lúc: .app, .lock, và mấy @keyframes
   `min(100vw, 768px)` của Rùa–Thỏ + mây (chúng chạy hết bề ngang cảnh, lệch số là
   hai con vật biến mất trước khi tới hang, hoặc chạy hụt ra ngoài rồi mới quay lại). */
.app {
  display: grid; grid-template-columns: 78px 1fr;
  position: fixed; left: 0; right: 0; top: var(--vtop, 0px);
  height: var(--vh, 100dvh);
  max-width: 768px; margin-inline: auto;
  background: var(--bg);
  /* Trên màn rộng: một tấm thẻ nổi trên nền trang, thay cho hai vạch kẻ dọc mảnh
     như cũ. Đúng khổ 768 trở xuống thì bóng chạy hết ra ngoài, không ai thấy. */
  box-shadow: 0 0 0 1px var(--line), 0 20px 60px rgba(8, 15, 26, .14);
}

/* ---------- dải danh bạ ---------- */
.sidebar {
  display: flex; flex-direction: column; align-items: center;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: 14px 0 10px;
}
/* min-height: 0 để danh sách hang (nằm trong đây) chịu co lại và tự cuộn khi nhiều
   hang, thay vì phình ra đẩy hàng nút ▤ 🔔 ◐ xuống dưới đáy màn. */
.side-head { display: flex; flex-direction: column; align-items: center; gap: 10px; width: 100%; min-height: 0;
             position: relative; }
/* Cột dọc máy tính: mình trên cùng, rồi +, rồi tới danh sách hang — y như cũ. */
/* (Cột dọc máy tính: #btnInvite cũng không còn ở hàng nút nữa — xem .hang-tag.) */
.side-list { order: 2; }
/* Chữ "Trò chuyện" chỉ có nghĩa ở màn danh sách nằm ngang. Cột dọc 78px thì không
   nhét nổi, mà cũng không cần: ở đó cái vòng nằm ngay dưới nút +, đọc ra ngay. */
.side-tag { display: none; }
/* Cửa sổ nhìn ra trời + kho báu trong hang: chỉ có ở màn danh sách nằm ngang
   (điện thoại/tablet). Cột dọc 78px trên máy tính thì không có vách nào để trổ,
   cũng chẳng có nền đất nào để chất của. */
.cave-view, .hang-say { display: none; }
.me { display: contents; }
/* Tên và ID của mình: giấu đi, chỉ hiện khi rê vào dải. */
.me-text {
  /* top: 0 chứ không phải một con số đếm từ đỉnh cột: mốc là .side-head, nên tấm
     tên luôn ngang hàng với cái avatar dù trên đầu cột có thêm bớt nút gì. Đã dính
     một lần lúc đưa ☰ lên đầu cột — tấm tên đứng lại trên trời. */
  position: absolute; left: 78px; top: 0; z-index: 20;
  background: var(--panel); border: 1px solid var(--line);
  padding: 8px 12px; border-radius: var(--radius);
  display: flex; flex-direction: column; gap: 4px; min-width: 190px;
  opacity: 0; pointer-events: none; transition: opacity .18s;
}
.sidebar:hover .me-text, .me-text:focus-within { opacity: 1; pointer-events: auto; }

.avatar {
  width: 34px; height: 34px; flex: none; border-radius: 50%;
  display: grid; place-items: center;
  font: 600 19px var(--mono); color: var(--accent-ink);   /* emoji Rùa/Thỏ đầy hình tròn */
  background: var(--accent); user-select: none; line-height: 1;
}
.avatar.sm { width: 26px; height: 26px; font-size: 15px; }

.name-input {
  border: 0; background: transparent; color: var(--text);
  font: 600 calc(13px * var(--fs)) var(--mono); padding: 0; width: 100%; outline: none;
}
.id-chip {
  display: flex; align-items: center; gap: 6px;
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--dim); font: calc(11px * var(--fs)) var(--mono); text-align: left;
}
.id-chip:hover { color: var(--accent); }
.dot { width: 6px; height: 6px; flex: none; border-radius: 50%; background: var(--dimmer); }
.dot.on { background: var(--ok); box-shadow: 0 0 6px var(--ok); }
.dot.off { background: var(--danger); }

/* Danh bạ = cột chấm. Không tên, không dòng xem trước. */
/* flex: 0 1 auto (không phải flex: 1): danh sách chỉ cao bằng nội dung, nên hàng
   nút ▤ 🔔 ◐ Đăng xuất nằm NGAY DƯỚI danh sách thay vì bị đẩy tuốt xuống đáy cột.
   Một hang thì danh sách có đúng một chấm — để nó giãn hết cột là bốn cái nút trôi
   xuống tận cùng màn hình, xa hẳn mọi thứ khác.

   KHÔNG có overflow ở đây, và đó là chuyện CỐ Ý — đã từng có `overflow-y: auto`, nó
   nuốt mất tấm tên (chủ nhà bắt được 20/08/2026 trên tablet: rê chuột vào chấm ở
   thanh bên mà chẳng thấy gì, tưởng tấm tên chui xuống dưới nền phòng chat). Đo ra:
   tấm tên rộng 238px đứng ở x=272 trong khi khung danh sách hết ở x=283 — thò ra
   227px và bị cắt sạch. Một khung có overflow thì CẮT cả con cháu position:absolute
   của nó, dù cái đó có z-index cao tới đâu; mà chặn cuộn một chiều thôi thì CSS
   không cho (đặt một chiều khác `visible` là chiều kia tự thành `auto`).
   Bỏ được vì danh sách này NHIỀU NHẤT LÀ HAI DÒNG, không phải "chắc là ít": mọi cú
   `upsertPeer` trong app.js chỉ sinh ra đúng hai thân phận — cái hang đang giữ
   (`hangId`, mà một máy chỉ giữ một hang) và Thỏ AI. Ngày nào app cho giữ nhiều hang
   thì việc phải làm là kéo tấm tên RA NGOÀI khung này (position: fixed, toạ độ tính
   bằng JS), chứ đừng cắm lại overflow — cắm lại là con lỗi này quay về y nguyên. */
.side-list { flex: 0 1 auto; min-height: 0; width: 100%; padding: 14px 0; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.peer {
  position: relative; border: 0; background: transparent; cursor: pointer;
  padding: 0; width: 34px; height: 34px; border-radius: 50%;
  display: grid; place-items: center; font: inherit; color: inherit;
}
.peer .avatar { transition: transform .15s, box-shadow .15s; }
.peer:hover .avatar { transform: scale(1.12); }
.peer.active .avatar { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--accent); }
/* Tên chỉ bật ra khi rê chuột. Nó THÒ HẲN ra ngoài thanh bên, nằm đè lên phòng chat —
   nên khung cha (.side-list) tuyệt đối không được có overflow, xem ghi chú ở trên. */
.peer-body {
  position: absolute; left: 44px; top: 50%; transform: translateY(-50%);
  background: var(--panel-2); border: 1px solid var(--line);
  padding: 5px 9px; border-radius: var(--radius); white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity .15s; z-index: 20;
}
.peer:hover .peer-body { opacity: 1; }
.peer-top { display: flex; gap: 10px; align-items: baseline; }
.peer-name { font-size: calc(12px * var(--fs)); color: var(--text); }
.peer-time, .peer-last { font-size: calc(10.5px * var(--fs)); color: var(--dim); }
.peer-last { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.badge {
  position: absolute; top: -1px; right: -1px;
  min-width: 14px; height: 14px; padding: 0 3px; border-radius: 7px;
  background: var(--accent); color: var(--accent-ink);
  font: 700 9px/14px var(--mono); text-align: center;
  border: 2px solid var(--panel);
}
.side-empty { font-size: calc(18px * var(--fs)); color: var(--dimmer); padding-top: 8px; }

/* order: -1 — ☰ nằm ở GÓC TRÁI TRÊN, trên cả cái avatar của mình, y như cái
   hamburger của mọi website trên điện thoại. Trước nó đứng dưới danh sách hang,
   giữa lưng chừng cột, và khay thì trổ NGƯỢC LÊN trùm qua danh bạ. Trong HTML nó
   vẫn nằm cuối (<footer>) vì màn danh sách nằm ngang xếp khác — đổi chỗ bằng order
   chứ đừng khiêng thẻ đi, băng chữ .hang-say đang dựa vào thứ tự đó.
   width: 100% để khay lấy mép trái của cột làm mốc, không phải mép nút. */
.side-foot { display: flex; flex-direction: column; gap: 2px; align-items: center; position: relative;
             order: -1; width: 100%; margin-bottom: 6px; }

/* ---------- ☰ và cái khay của nó ----------
   Một nút thay cho cả hàng ký hiệu ▤ 🔔 ◐ + chữ "Đăng xuất" (xem index.html).
   Luật của khay, và đây là chỗ đáng giá nhất: MỖI DÒNG CÓ CHỮ. Ký hiệu trơ thì
   người ta phải đoán, mà đoán sai ở cái nút "Đăng xuất" là văng ra màn nhập chìa
   giữa lúc đang nhắn dở. Dòng nào có trạng thái (chuông đang bật/tắt, đang dùng
   nền màu nào) thì nói luôn trạng thái ở dòng phụ. */
/* ---------- LỐI TẮT Ở CHÂN CỘT DỌC ----------
   `margin-top: auto` hút hết khoảng trống còn lại của cột, dán cả cụm xuống đáy —
   xa hẳn danh sách hang ở trên đầu, nên mắt đọc ra hai vùng khác nhau chứ không phải
   một danh sách dài. Ngón tay cũng đỡ với: đáy màn gần hơn giữa màn.
   Cụm này CHỈ sống ở cột dọc; khối @media 1024px bên dưới tắt nó đi, vì ở đó thanh bên
   đã nằm ngang thành một hàng điều hướng chật cứng, chen thêm sáu nút vào là vỡ. */
.side-quick {
  margin-top: auto; width: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
/* Ký hiệu trên, CHỮ DƯỚI. Chữ mới là thứ giữ cho cụm này khác hẳn cái hàng ▤ 🔔 ◐
   ngày trước — xem lời dặn trong index.html. Cột chỉ rộng 78px nên chữ 9.5px và cả
   nút 62px: vừa đủ cho "Ly nước" nằm gọn một dòng, không gãy chữ. */
.quick {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  width: 62px; padding: 7px 2px;
  background: none; border: 0; border-radius: var(--radius);
  color: var(--dim); font: inherit; cursor: pointer;
}
.quick:hover, .quick:focus-visible { background: var(--panel-2); color: var(--text); outline: none; }
.quick:focus-visible { box-shadow: inset 0 0 0 1px var(--accent); }
.quick-ico { font-size: calc(17px * var(--fs)); line-height: 1; }
.quick-ten { font: 600 calc(9.5px * var(--fs)) var(--mono); letter-spacing: .01em; white-space: nowrap; }

.menu-btn { font-size: calc(19px * var(--fs)); }
.menu-btn[aria-expanded="true"] { color: var(--accent); background: var(--panel-2); }

.menu-tray {
  position: absolute; z-index: 40;
  /* Trổ XUỐNG, vào đúng khoảng trống dưới ☰ — cột này vốn chỉ có danh bạ ở trên
     đầu rồi bỏ không quá nửa. Trổ lên thì khay trùm qua danh bạ, mà tay cũng phải
     đi ngược đường vừa bấm. */
  top: calc(100% + 8px); left: 0;
  width: max-content; min-width: 232px; max-width: min(300px, calc(100vw - 24px));
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 12px 34px rgb(0 0 0 / .34);
  animation: trayUp .14s ease-out;
}
@keyframes trayUp { from { opacity: 0; transform: translateY(-6px); } }
@media (prefers-reduced-motion: reduce) { .menu-tray { animation: none; } }

.tray-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; text-align: left;
  background: none; border: 0; border-radius: var(--radius); cursor: pointer;
  color: var(--text); font: inherit;
}
.tray-row:hover, .tray-row:focus-visible { background: var(--panel-2); outline: none; }
.tray-row:focus-visible { box-shadow: inset 0 0 0 1px var(--accent); }
.tray-ico { flex: none; width: 26px; font-size: calc(17px * var(--fs)); line-height: 1; text-align: center; }
.tray-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tray-text b { font: 600 calc(12.5px * var(--fs)) var(--mono); letter-spacing: .01em; }
/* Dòng phụ nói TRẠNG THÁI ĐANG LÀ GÌ, không phải nói bấm vào sẽ ra gì — người ta
   nhìn khay này để biết máy đang thế nào trước, rồi mới quyết có đổi hay không. */
.tray-text i { font-style: normal; color: var(--dim); font-size: calc(10.5px * var(--fs)); }
.tray-line { height: 1px; margin: 4px 6px; background: var(--line); }
/* Đăng xuất đứng riêng dưới vạch kẻ, và chỉ ĐỎ LÊN khi ngón tay đã tới nơi — đỏ
   sẵn thì cả khay trông như một cái cảnh báo. */
.tray-row.danger:hover, .tray-row.danger:focus-visible { color: var(--warn, #e5484d); }
.tray-row.danger:hover .tray-text i { color: color-mix(in srgb, var(--warn, #e5484d) 70%, var(--dim)); }
/* Nút Đăng xuất có VIỀN bao quanh, mấy nút kia thì trơ ký hiệu — để cùng một khoảng
   cách thì viền của nó chạm sát cái ◐ bên cạnh, nhìn chật. Cho nó thở ra một chút. */
.logout-btn { margin-left: 10px; padding: 4px 12px; }
/* 28×26 với chữ 13px là quá bé: trên điện thoại ngón tay chạm hụt, mà nhìn cũng
   không ra ký hiệu gì. Mốc chạm tối thiểu nên là 40px. */
.link-btn {
  width: 40px; height: 38px; background: none; border: 0; cursor: pointer;
  color: var(--dim); font: 19px/1 var(--mono); border-radius: var(--radius);
  display: grid; place-items: center;
}
.link-btn:hover { color: var(--accent); background: var(--panel-2); }
/* Nút chữ giữa mấy nút ký hiệu: phải tự nới ra, không bị bó trong khung 28px. */
/* Cột thanh bên rộng 78px — chữ phải vừa lọt, không thì cấn mép trái. */
.logout-btn {
  width: auto; max-width: 72px; height: auto; padding: 5px 6px; margin-top: 6px;
  font-size: calc(10px * var(--fs)); letter-spacing: 0; white-space: nowrap;
  border: 1px solid var(--line);
}
.logout-btn:hover { border-color: var(--accent); }

/* ---------- cột log ---------- */
.chat { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; background: var(--bg); }

/* ---------- thả tim ---------- */
.hearts { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 4; }
/* Tim tô bằng màu accent + quầng sáng: nổi nhờ ĐỘ SÁNG chứ không nhờ sắc đỏ,
   nên nền đen hay nền giấy đều rõ, và người khó phân biệt màu vẫn thấy bình thường. */
.heart-fly { position: absolute; bottom: 64px; line-height: 1; animation: fly 1.7s ease-out forwards; }
.heart-fly.ink {
  color: var(--heart);
  text-shadow: 0 0 12px color-mix(in srgb, var(--heart) 65%, transparent);
}
@keyframes fly {
  0%   { transform: translate(0, 0) scale(.5); opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translate(var(--dx, 0px), -46vh) scale(1.2) rotate(10deg); opacity: 0; }
}
.btn-heart {
  flex: none; align-self: flex-end; width: 34px; height: 40px; padding: 0;
  background: none; border: 0; cursor: pointer; font-size: 21px; line-height: 1;
  color: var(--heart); opacity: .85; transition: transform .1s, opacity .15s;
}
.btn-heart:hover { opacity: 1; }
.btn-heart:active { transform: scale(.82); }
/* Chỉ ♥ (ký tự chữ) mới ăn màu; emoji thì để nguyên hình vẽ của nó. */
.btn-heart:not(.ink) { color: inherit; }

/* Khay chọn icon thả: treo ngay trên nút ♥, ở góc phải khung soạn tin. */
.react-pick {
  position: absolute; right: 56px; bottom: 62px; z-index: 30;
  display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px;
  padding: 7px; border-radius: 14px;
  background: var(--panel-2); border: 1px solid var(--line);
  box-shadow: 0 10px 26px rgba(0, 0, 0, .28);
}
.react-pick button {
  width: 34px; height: 32px; padding: 0; cursor: pointer;
  border: 0; border-radius: 9px; background: none;
  font: 17px/1 var(--mono); color: inherit;
}
.react-pick button.ink { color: var(--heart); }

/* Ô khắc câu thần chú, nằm ngay dưới cái nút vừa bấm trong hộp ▤. */
.spell-box {
  display: flex; flex-wrap: wrap; gap: 7px; margin: 2px 0 4px;
  padding: 9px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--line);
}
.spell-box input {
  flex: 1; min-width: 140px; padding: 8px 11px;
  border: 1px solid var(--line); border-radius: 999px;
  background: var(--bg); color: var(--text); font: calc(12px * var(--fs)) var(--mono); outline: none;
}
.spell-box input:focus { border-color: var(--accent); }
.spell-box .btn { width: auto; margin: 0; padding: 8px 15px; }
.spell-note { width: 100%; margin: 0; font: calc(11px * var(--fs))/1.5 var(--mono); color: var(--dim); }
.react-pick button:hover { background: var(--panel); }
/* flex: none cho ba thanh cố định — nếu không, danh sách tin phình ra là đẩy
   đầu phòng chat lên trên và ô nhập xuống dưới màn hình. */
.chat-head, .callbar, .composer { flex: none; }
.chat-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; border-bottom: 1px solid var(--line);
}
.chat-head .avatar { display: none; }       /* đã có chấm ở dải bên trái rồi */
.chat-title { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px; }
/* Tên hang là thứ đầu tiên người ta liếc để biết mình đang ở đâu — nó phải là chữ
   ĐẬM MÀU CHÍNH, không phải chữ mờ. Trước để --dim (xám nhạt) cỡ 12px, trên điện
   thoại ngoài nắng gần như không đọc ra. */
.chat-title strong {
  font: 700 calc(13px * var(--fs)) var(--mono); letter-spacing: .1em; text-transform: uppercase;
  color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sub { font-size: calc(11.5px * var(--fs)); color: var(--dim); }
.sub.on { color: transparent; width: 6px; height: 6px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 6px var(--ok); overflow: hidden; flex: none; }
.sub.wait { color: transparent; width: 6px; height: 6px; border-radius: 50%; background: var(--warn); overflow: hidden; flex: none; }
.sub.bad { color: var(--danger); font-size: calc(11px * var(--fs)); }
/* "đang thì thầm…" phải là CHỮ. Nếu để dính class .on/.wait thì nó bị bóp thành
   cái chấm 6px trong suốt — gõ mãi mà bên kia không thấy gì. */
.sub.typing { color: var(--accent); font-size: calc(11px * var(--fs)); }
.crowd { flex: none; margin-left: auto; font-size: calc(11px * var(--fs)); color: var(--dim); letter-spacing: .06em; cursor: default; }
.back { display: none; }

/* ---------- thanh gọi (chỉ hiện khi có chuông / đang nói) ---------- */
.callbar {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 18px; background: var(--panel);
  border-bottom: 1px solid var(--line);
}
.call-ico { font-size: 15px; line-height: 1; flex: none; }
.callbar.ring .call-ico { animation: ringpulse 1.1s ease-in-out infinite; }
.call-text { flex: 1; min-width: 0; display: flex; align-items: baseline; gap: 10px; }
.call-text strong {
  font: 600 calc(12px * var(--fs)) var(--mono); letter-spacing: .12em; text-transform: uppercase;
  color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.call-text .sub { color: var(--accent); flex: none; }
.btn-icon.ok { color: var(--ok); }
.btn-icon.ok:hover { color: var(--ok); background: var(--panel-2); }
#btnCallMute { font-size: 13px; }
@keyframes ringpulse { 50% { opacity: .2; } }

/* Khu chat hai phía: Rùa 🐢 một bên, Thỏ 🐇 một bên. */
/* min-height:0 là chốt chặn: thiếu nó thì khối cuộn không chịu co, nó phình theo
   nội dung và đẩy hai thanh trên/dưới văng khỏi màn hình. */
.messages {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 22px 18px 8px; display: flex; flex-direction: column; gap: 3px;
}
.msg {
  position: relative; display: flex; gap: 8px; align-items: flex-end;
  max-width: 82%; margin: 0;
}
.msg.in  { align-self: flex-start; }
.msg.out { align-self: flex-end; flex-direction: row-reverse; }
.msg.gap { margin-top: 12px; }
.msg.pending { opacity: .45; }

/* Nút 🔊 trong bong bóng phòng Thỏ. Nhạt sẵn, đậm lên khi rê vào — nó là món thêm,
   đừng để nó tranh chỗ với chữ. Trên máy cảm ứng thì luôn hiện rõ hơn một chút vì
   không có chuyện "rê vào". */
.noi-btn {
  margin-left: 7px; padding: 0; border: 0; background: none; cursor: pointer;
  font-size: calc(12px * var(--fs)); line-height: 1; opacity: .4;
  vertical-align: baseline;
}
.noi-btn:hover, .noi-btn:focus-visible { opacity: 1; outline: none; }
@media (hover: none) { .noi-btn { opacity: .7; } }

/* ---------- Thỏ mời chọn cảnh (màn chào phòng Thỏ) ----------
   Mấy viên thuốc xếp quấn dòng, cái đang chọn thì viền và chữ sáng lên. Cố ý để NHẠT
   hơn nút "Gửi": đây là lời mời, không phải việc bắt buộc làm trước khi được nói. */
.tho-canh-khung { display: flex; flex-direction: column; align-items: center; gap: 7px; margin: 14px auto 0; max-width: 430px; }
.tho-canh-nhan { margin: 0; color: var(--dim); font-size: calc(11px * var(--fs)); }
.tho-canh-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.tho-canh-chip {
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  padding: 6px 11px; border-radius: 999px; cursor: pointer;
  font: calc(11.5px * var(--fs))/1.3 var(--mono);
  transition: border-color .15s, color .15s;
}
.tho-canh-chip:hover, .tho-canh-chip:focus-visible { border-color: var(--accent); outline: none; }
.tho-canh-chip.on { border-color: var(--accent); color: var(--accent); }

/* ---------- BÚT ĐỎ của Thỏ AI ----------
   CÂU ĐÃ SỬA không ở đây: nó được thay thẳng vào bong bóng, chỗ câu sai vừa đứng
   (xem dapBut trong app.js). Còn lại đây đúng một thứ — DÒNG GIẢNG, đứng ngay dưới
   bong bóng của MÌNH (align-self: flex-end, cùng phía với bong bóng out).
   Cố tình LÀM NHỎ và nhạt: người ta vào đây để tám chuyện, lời giảng là ghi chú bên
   lề — liếc thì thấy, không liếc thì câu chuyện vẫn chảy. Đỏ chót cỡ chữ chat là
   mỗi câu sai thành một tiếng quát. */
.fix-line {
  align-self: flex-end; max-width: 82%;
  margin: 2px 29px 0 0;                      /* 29px = chừa đúng chỗ con Rùa bên phải */
  padding: 6px 10px;
  display: flex; flex-direction: column; gap: 3px;
  border-right: 2px solid var(--warn, #e5484d);
  border-radius: 4px 12px 12px 4px;
  background: color-mix(in srgb, var(--warn, #e5484d) 9%, transparent);
  text-align: right;
}
/* Dòng giảng, viết bằng tiếng người ta đang dùng để nhắn: nhạt, nhỏ, là chú thích
   cho câu đúng vừa nằm lên bong bóng ở trên. */
.fix-line i {
  font-style: normal; font-size: calc(10.5px * var(--fs)); line-height: 1.5;
  color: var(--dim); overflow-wrap: anywhere;
}

/* Chữ trong bong bóng ĐỔI ngay trước mắt lúc bút đỏ về (câu sai → câu đúng). Nháy một
   cái viền đỏ rồi thôi: không nháy thì nó lặng lẽ đổi sau lưng người ta, vừa giật mình
   vừa dễ tưởng mình gõ đúng sẵn từ đầu. Dấu ✎ cuối dòng thì ở lại mãi. */
@keyframes vua-sua {
  from { box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn, #e5484d) 60%, transparent); }
  to   { box-shadow: 0 0 0 3px transparent; }
}
.msg .bubble.vua-sua { animation: vua-sua 1.4s ease-out; }
@media (prefers-reduced-motion: reduce) { .msg .bubble.vua-sua { animation: none; } }

/* ---------- BÀI HỌC nhặt từ câu của Thỏ ----------
   Soi gương với dòng bút đỏ nhưng đứng bên TRÁI (cùng phía bong bóng của Thỏ), và KHÔNG
   dùng màu báo lỗi: đây không phải chỗ nào sai, đây là thứ đáng mang đi dùng lại. Cũng
   nhỏ và nhạt như bút đỏ — bày to bằng chữ chat là câu chuyện thành buổi giảng bài. */
.hoc-line {
  align-self: flex-start; max-width: 82%;
  margin: 2px 0 0 29px;                      /* 29px = chừa đúng chỗ con Thỏ bên trái */
  padding: 6px 10px;
  display: flex; flex-direction: column; gap: 3px;
  border-left: 2px solid var(--accent);
  border-radius: 12px 4px 4px 12px;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  text-align: left;
}
.hoc-line i {
  font-style: normal; font-size: calc(10.5px * var(--fs)); line-height: 1.5;
  color: var(--dim); overflow-wrap: anywhere;
}
/* Nút chữ trong bong bóng (dịch · học): cùng khung với 🔊 nhưng là CHỮ, nên phải nhỏ
   hơn một nấc và có viền để đọc ra là bấm được. Đang bật thì sáng lên. */
.dich-btn {
  font: 600 calc(9.5px * var(--fs))/1 var(--mono);
  border: 1px solid currentColor; border-radius: 999px;
  padding: 2px 5px; vertical-align: middle;
}
.dich-btn.on { opacity: 1; color: var(--accent); }

/* con vật hai bên */
.msg .who { flex: none; font-size: 21px; line-height: 1.2; user-select: none; }
.who.flip { display: inline-block; transform: scaleX(-1); }   /* Thỏ quay mặt về phía bong bóng */
.msg + .msg .who { visibility: hidden; }          /* cùng người nói liền nhau → ẩn con vật lặp */
.msg.gap .who, .msg:first-child .who { visibility: visible; }

.msg .bubble {
  min-width: 0; white-space: pre-wrap; overflow-wrap: anywhere;
  padding: 7px 12px; border-radius: 14px;
}
.msg.in  .bubble { background: var(--panel-2); color: var(--text);       border-bottom-left-radius: 4px; }
.msg.out .bubble { background: var(--accent);  color: var(--accent-ink); border-bottom-right-radius: 4px; }
/* Tông ĐẤT: cả nền lẫn --panel-2 đều là nâu, hai màu nâu cạnh nhau thì bong bóng
   của bạn hang gần như tàng hình. Đổi riêng nó sang màu giấy sáng — lời của họ nổi
   hẳn lên như một mẩu giấy dán trên vách đất, còn lời mình vẫn màu cà rốt.
   Chỉ đụng đúng cái bong bóng: --panel-2 còn dùng cho ô soạn tin, thông báo, nút
   rê chuột… đổi thẳng biến là mấy chỗ đó thành chữ sáng trên nền sáng. */
:root[data-theme="earth"] .msg.in .bubble { background: #efdcbb; color: #33240f; }
:root[data-theme="earth"] .msg.in .bubble .meta { color: #6f5a3c; }
:root[data-theme="earth"] .msg.in a { color: #8a4b06; }
.msg.locked .bubble { color: var(--dimmer); font-style: italic; }
.msg.in  a { color: var(--accent); text-decoration: none; border-bottom: 1px solid currentColor; }
.msg.out a { color: var(--accent-ink); text-decoration: underline; }

/* Giờ nằm GỌN cuối dòng chữ (tin ngắn không bị kéo cao). */
.msg .body { display: inline; }
/* 10px + --dimmer là mờ chồng mờ, trên điện thoại nhìn ra một vệt xám không đọc
   được giờ giấc gì. Nhích lên 10.5px và dùng --dim. */
.msg .meta {
  display: inline-block; margin-left: 8px; font-size: calc(10.5px * var(--fs));
  white-space: nowrap; user-select: none; color: var(--dim);
  transform: translateY(1px);
}
.msg.out .meta { color: color-mix(in srgb, var(--accent-ink) 62%, transparent); }

.msg .del {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: 16px; height: 16px; padding: 0; line-height: 14px;
  border: 0; border-radius: 3px; background: transparent;
  color: var(--dimmer); font: 14px var(--mono); cursor: pointer;
  opacity: 0; pointer-events: none; transition: opacity .12s;
}
.msg.out .del { left: -22px; }
.msg.in  .del { right: -22px; }
.msg:hover .del { opacity: 1; pointer-events: auto; }
.msg .del:hover { color: var(--danger); }
@media (hover: none) { .msg .del { opacity: .35; pointer-events: auto; } }

/* Bong bóng "..." đang gõ (dùng cho hang giả cho tự nhiên). */
.typing-row { display: flex; gap: 8px; align-self: flex-start; align-items: flex-end; max-width: 82%; }
.typing-row .who { flex: none; font-size: 18px; line-height: 1.3; }
.typing-bubble {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 12px; border-bottom-left-radius: 4px;
  padding: 10px 12px; display: flex; gap: 4px;
}
.typing-bubble span {
  width: 6px; height: 6px; border-radius: 50%; background: var(--dim);
  animation: typingdot 1.2s infinite;
}
.typing-bubble span:nth-child(2) { animation-delay: .18s; }
.typing-bubble span:nth-child(3) { animation-delay: .36s; }
@keyframes typingdot {
  0%, 60%, 100% { opacity: .25; transform: translateY(0); }
  30% { opacity: 1; transform: translateY(-3px); }
}

/* Không kẻ vạch hai bên nữa: trong khung chat đã có sẵn viền đầu phòng, viền ô soạn
   tin, viền bong bóng — thêm hai vạch chạy hết bề ngang cho mỗi ngày thì màn hình
   toàn gạch. Chỉ còn chữ, canh giữa. */
.daysep {
  display: flex; align-items: center; justify-content: center;
  font-size: calc(10px * var(--fs)); color: var(--dimmer); letter-spacing: .18em; text-transform: uppercase;
  margin: 20px 0 10px;
}
/* Hang trống. Trước đây là một dấu chấm giữa khoảng trắng mênh mông, nhìn như
   app hỏng; giờ là cái hang có Rùa ngồi đợi trong đó. */
.empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; gap: 4px; }
/* Cùng cảnh đó nhưng nằm TRONG danh sách lời nhắn (do showEmptyNote dựng ra). */
.hang-empty {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 42px 0 10px;
}
.empty-art { margin-bottom: 14px; }
.empty-burrow {
  position: relative; display: block; width: 340px; height: 190px;
}
.empty-burrow::before {                  /* ụ đất */
  content: ''; position: absolute; inset: 0;
  background: var(--panel-2);
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.empty-mouth {                           /* miệng hang tối om */
  position: absolute; left: 50%; bottom: 0; width: 190px; height: 128px;
  transform: translateX(-50%);
  background: var(--bg); border: 1px solid var(--line); border-bottom: 0;
  border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.empty-turtle {                          /* Rùa ngồi trong miệng hang, thở ra thở vào */
  position: absolute; left: 50%; bottom: 2px;
  transform: translateX(-50%); font-size: 110px; line-height: 1;
  animation: empty-breathe 3.6s ease-in-out infinite;
}
@keyframes empty-breathe {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%      { transform: translateX(-50%) translateY(-3px); }
}
.empty-line { margin: 0; font: calc(13px * var(--fs)) var(--mono); color: var(--dim); }
.empty-sub { margin: 0; font: calc(11.5px * var(--fs)) var(--mono); color: var(--dimmer); }

/* ---------- màn chào phòng Thỏ AI ----------
   Cố tình TRỐNG TRẢI: một con thỏ, một câu, và (nếu chưa có chìa) đúng một ô để dán.
   Đây là chế độ học — người ta vào để gõ câu đầu tiên, không phải để đọc hướng dẫn. */
/* Tự dựng bố cục chứ không mượn .hang-empty: class `empty-note` chỉ là cái móc để
   appendMsg() dọn màn chào đi khi có lời nhắn đầu tiên, nó KHÔNG mang kiểu dáng nào. */
.tho-chao {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin: auto; padding: 20px 16px; text-align: center;
}
.tho-chao-art { font-size: 74px; line-height: 1; user-select: none; }
.tho-chao-o {
  width: min(320px, 78%); margin-top: 4px;
  padding: 9px 12px; text-align: center;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--panel); color: var(--text);
  font: calc(12px * var(--fs)) var(--mono); outline: none;
}
.tho-chao-o:focus { border-color: var(--accent); }
.tho-chao-link {
  font: calc(11.5px * var(--fs)) var(--mono);
  color: var(--accent); text-decoration: none;
}
.tho-chao-link:hover { text-decoration: underline; }
@media (prefers-reduced-motion: reduce) { .empty-turtle { animation: none; } }

/* Ô soạn: khung bo tròn nền dịu cho thân thiện. */
.composer {
  display: flex; gap: 10px; align-items: flex-end;
  padding: 12px 14px calc(14px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--line);
}
.composer .caret { color: var(--accent); font: 700 22px var(--mono); line-height: 1.9; user-select: none; }
.composer textarea {
  flex: 1; resize: none; max-height: 40vh; min-height: 24px;
  padding: 9px 15px; border: 1px solid var(--line); border-radius: 20px;
  outline: none; background: var(--panel-2);
  color: var(--text); font: inherit; caret-color: var(--accent);
}
.composer textarea:focus { border-color: var(--accent); }
/* Nút gửi mang chữ "Gửi", nền accent cho nổi. Enter vẫn gửi được như thường. */
.btn-send {
  flex: none; align-self: flex-end;
  height: 40px; padding: 0 16px; border: 0; border-radius: 999px;
  background: var(--accent); color: var(--accent-ink); cursor: pointer;
  font: 600 calc(12.5px * var(--fs))/1 var(--mono); letter-spacing: .04em;
  display: grid; place-items: center;
  box-shadow: 0 1px 6px color-mix(in srgb, var(--accent) 45%, transparent);
  transition: transform .1s, filter .15s;
}
.btn-send:hover { filter: brightness(1.08); }
.btn-send:active { transform: scale(.88); }
.btn-send:disabled { opacity: .4; cursor: default; }

/* ---------- màn khoá: đen tuyền, một dấu chấm ---------- */
.lock {
  position: fixed; left: 0; right: 0; top: var(--vtop, 0px); height: var(--vh, 100dvh);
  max-width: 768px; margin-inline: auto;      /* cùng khổ với app, xem ghi chú ở .app */
  z-index: 200;
  /* MỘT CỘT canh giữa: tên trò → cửa hang → dòng nhắc → chặng đường Rùa–Thỏ →
     mấy lối phụ, cả cụm nằm giữa màn hình.
     (Đã thử chia 3 hàng lưới để ghim con đường đúng tâm màn hình — nhưng nó đẩy
     cửa hang lên sát mép trên, cả cảnh mất cân. Cụm nằm giữa nhìn thuận mắt hơn.) */
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* Trời quang dần xuống chân trời. Cả khối này cố tình KHÔNG theo theme của app:
     màn khoá là thứ người ta nhìn trước khi biết app có theme gì, nên giữ một
     bộ mặt duy nhất. */
  /* Trời một buổi chiều nắng: xanh no màu trên đỉnh, ấm dần xuống chân trời rồi
     mới giao cho vạt cỏ. Bản trước loãng gần như trắng (#d8eefb→#fff) nên cả màn
     hình chỉ còn một mảng nhờ nhờ, ổ khoá lẫn vào nền, nhìn như trang chưa tải xong.
     Chốt màu ở 62% cho mềm, không để trời đổ thẳng sang màu ấm — gãy khúc là lộ
     ngay ra đây là mấy cái gradient chồng lên nhau. */
  background: linear-gradient(#8fc9ee 0%, #b6dcf3 26%, #d9ecf7 48%, #eef3f2 62%, #f8f1de 100%);
  cursor: pointer; user-select: none;
  -webkit-tap-highlight-color: transparent;
  /* Ghim cứng: bàn phím điện thoại bật lên cũng không đẩy trôi cái gì. */
  overflow: hidden; overscroll-behavior: none; touch-action: manipulation;
}
/* Mây trôi ngang trời, chậm rãi. Nằm sau mọi thứ, không ăn chạm của ai. */
.sky { position: absolute; inset: 0; overflow: hidden; pointer-events: none; z-index: 0; }
.cloud {
  position: absolute; left: -90px; line-height: 1; opacity: .7;
  animation: drift 96s linear infinite;
}
/* Cả ba đám nằm hẳn TRÊN CAO (5–17%), không đám nào trôi ngang tầm tên trò hay
   cửa hang. Trước đây có đám ở 23% — khổ màn đã bó lại còn 768px nên nó sà xuống
   sát chữ, nhìn như mây mọc trong tên trò. */
.cloud:nth-child(1) { top: 11%; font-size: 38px; animation-delay:   0s; }
.cloud:nth-child(2) { top: 17%; font-size: 26px; animation-delay: -38s; }
.cloud:nth-child(3) { top: 5%;  font-size: 32px; animation-delay: -72s; }
@keyframes drift { to { transform: translateX(calc(min(100vw, 768px) + 130px)); } }

/* Đồi xa: ba cái gò chồng nhau, gò càng xa càng nhạt và càng ngả sang màu trời
   (mù xa). Đáy gò đặt trùng mặt đường nên rặng cây đứng ngay chân đồi.
   Chỉ có ở cửa hang — cảnh trò đua trong hang không có .hills. */
/* bottom: 80px = đúng mép trên con đường (.race 66px + .ground 14px), để chân đồi
   chạm mặt đường chứ không lửng lơ giữa trời. Ba cái gò rời nhau, giữa chúng còn
   thấy trời — dính liền thành một dải là ra mảng màu, không ra quả đồi. */
/* (Không có lớp đồi. Đã thử: đồi sau cửa hang trông hoành tráng thật, nhưng nó
   thêm một dải màu đất nữa vào cảnh vốn đã có cửa gỗ + đường + cỏ + đồng hoa —
   nhìn đục, mà cái đáng nhìn là chặng đường Rùa–Thỏ thì chìm đi. Cảnh này sống
   nhờ SẠCH: trời trống, một cái hang, một con đường.) */

/* Rặng cây chân trời: LÙI RA SAU con đường. Nhỏ và nhạt để mắt hiểu là ở xa —
   để to bằng hai con vật thì trông như cây mọc giữa đường. */
.treeline {
  display: flex; align-items: flex-end; justify-content: space-between;
  width: 100%; padding: 0 8px; margin-bottom: 2px;
  opacity: .62; pointer-events: none;
}
.treeline span { font-size: 15px; line-height: 1; }
.treeline span:nth-child(2n) { font-size: 19px; opacity: .8; }
.treeline span:nth-child(3n) { font-size: 12px; opacity: .55; }


/* Tên trò trên ổ khoá. Chữ có viền sáng mỏng phía sau (text-shadow trắng) để đọc
   được cả khi nền trời đậm — không thì mỗi lần đổi gradient là chữ chìm mất. */
.lock-title {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  margin-bottom: 20px; text-align: center; user-select: none;
}
/* Con Rùa phải TO ngang tầm dòng tên: 44px thì nó bé hơn cả chữ bên dưới, nhìn
   như một hạt trôi trên đầu chứ không ra cái biểu tượng của trò. */
/* Là <button> nhưng phải trông y hệt lúc còn là <span>: bỏ sạch nền, viền, đệm
   của nút. Chỉ giữ con trỏ bàn tay để ai rê chuột qua thì biết nó bấm được. */
.lock-logo {
  /* 62px là cỡ của một cái icon phụ, mà đây là LOGO của cả trò — thứ người ta nhìn
     đầu tiên, trên một màn hình vốn trống trải. Từng nâng lên 88px cho nó ra dáng
     con vật chủ nhà đứng trên nóc hang; to quá thì lại nuốt mất cả cửa hang bên
     dưới, nên rút bớt một phần ba: 88 → 59px (chủ nhà chốt 17/08/2026). */
  font-size: 59px; line-height: 1;
  background: none; border: 0; padding: 0; margin: 0; color: inherit;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  filter: drop-shadow(0 4px 6px rgba(12, 40, 55, .2));
  animation: logo-bob 4.2s ease-in-out infinite;
}
/* Mờ đi chứ KHÔNG co lại: logo-bob đang giữ transform, đặt transform ở đây là
   viết vào chỗ đã có chủ — bấm xuống chẳng thấy gì nhúc nhích. */
.lock-logo:active { opacity: .55; }
@keyframes logo-bob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } }
.lock-title b {
  font: 700 calc(21px * var(--fs))/1.3 var(--mono); letter-spacing: .08em; color: #123f3a;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .75);
}
/* Câu "cửa hang chỉ mở cho ai nhớ chìa" từng nằm ở đây (.lock-title i). Nó đi rồi —
   giờ đứng cuối hàng trong băng chữ trên ổ khoá, xem Bang trong app.js. */

/* Tiền cảnh: mấy bụi cỏ hoa nằm sát đáy, cỡ so le cho khỏi thành hàng rào đều tăm tắp. */
/* z-index: 1 — mảng đất kéo dài (.lock .ground::after) đứng SAU trong DOM nên mặc
   định nó phủ lên, nuốt sạch đám cỏ hoa này. */
.meadow {
  position: absolute; left: 0; right: 0; bottom: 0; height: 92px; z-index: 1;
  display: flex; align-items: flex-end; justify-content: space-around;
  padding: 0 6px 12px; pointer-events: none;
}
.meadow span { font-size: 19px; line-height: 1; opacity: .9; }
.meadow span:nth-child(2n) { font-size: 14px; opacity: .72; margin-bottom: 16px; }
.meadow span:nth-child(3n) { font-size: 24px; margin-bottom: 2px; }
.meadow span:nth-child(5n) { font-size: 16px; margin-bottom: 26px; opacity: .8; }

/* Ổ KHOÁ = CỬA HANG.
   Trước đây chỗ gõ chìa là một viên thuốc trắng lơ lửng giữa trời — chẳng ăn nhập
   gì với cả cái ẩn dụ: gõ chìa là để MỞ CỬA HANG, vậy thì chỗ gõ phải chính là cái
   cửa đó. Ba lớp: gò cỏ (background của .keyhole) → ụ đất (::before) → miệng hang
   tối (::after). Chữ gõ vào rơi thẳng vào lòng hang. */
.keyhole {
  position: relative; z-index: 1;
  width: min(232px, 70vw); height: 138px;
  display: flex; align-items: flex-end; justify-content: center;
  padding-bottom: 24px;
  cursor: pointer;
  /* Nền đất dưới chân hang: không có nó thì cái hang treo lơ lửng giữa trời.
     Cùng màu cam đất với tầng đất mặt ở dưới (.lock .ground) — để màu cỏ xanh như
     trước thì cái hang đứng trên một mảng cỏ, mà cả cảnh dưới nó là đất. */
  background: radial-gradient(64% 34px at 50% 100%, #dda05a 0 58%, rgba(221, 160, 90, 0) 100%) no-repeat;
  filter: drop-shadow(0 12px 20px rgba(12, 40, 55, .18));
  transition: transform .14s ease;
}
/* Ụ đất đắp lên cửa hang. Sáng ở đỉnh, sẫm dần xuống chân — mắt đọc ra khối tròn. */
.keyhole::before {
  content: ''; position: absolute; inset: 0 0 10px; z-index: -2;
  border-radius: 50% 50% 16px 16px / 86% 86% 16px 16px;
  background: radial-gradient(122% 92% at 50% 6%, #e0d1af 0 34%, #c9b58d 70%, #ad9a72 100%);
  border-bottom: 3px solid #9a8760;
}
/* CÁNH CỬA gỗ bịt kín miệng hang: ván dọc, hai đai sắt ngang, viền gỗ sẫm.
   Để trống hoác một cái miệng hang tối thì đó là hang BỎ NGỎ — mà đây là hang có
   khoá, phải nhìn thấy cái cửa đóng thì mới ra chuyện "gõ đúng chìa mới vào". */
.keyhole::after {
  content: ''; position: absolute; left: 50%; bottom: 10px; z-index: -1;
  width: 64%; height: 76%; transform: translateX(-50%);
  border-radius: 50% 50% 6px 6px / 100% 100% 6px 6px;
  border: 3px solid #4f3a22;
  background:
    /* hai đai sắt ngang */
    linear-gradient(#00000000 0, #00000000 24%, #55402799 24%, #55402799 29%, #00000000 29%,
                    #00000000 72%, #55402799 72%, #55402799 77%, #00000000 77%),
    /* khe giữa các tấm ván */
    repeating-linear-gradient(90deg, #00000045 0 2px, #00000000 2px 24px),
    /* thớ gỗ */
    linear-gradient(#9c7247 0, #7d5a37 55%, #67492c 100%);
  box-shadow: inset 0 10px 22px rgba(0, 0, 0, .38);
}
.lock:hover .keyhole { transform: translateY(-3px); }
.lock.has .keyhole { transform: none; }
/* Gõ sai: cả cánh cửa hằn lên một vòng đỏ. Gõ đúng: vòng xanh + ánh sáng hắt ra
   từ trong hang, ngay trước lúc màn khoá tan đi. */
.lock.bad .keyhole::after { box-shadow: inset 0 0 0 3px #b42318, inset 0 10px 22px rgba(0, 0, 0, .38); }
.lock.ok .keyhole::after { box-shadow: inset 0 0 0 3px #0f766e, inset 0 -20px 30px rgba(255, 214, 120, .55); }

/* Ô của nút vào hang. CAO 0 — cái nút nổi ra ngoài dòng chảy, không chiếm một hàng
   riêng: hàng đó chính là khoảng trời trống 78px từng nằm giữa cửa hang và rặng cây,
   làm cái hang trông như treo lơ lửng giữa trời (chủ nhà bắt dẹp 17/08/2026, bảo kéo
   vạt cây lên "sát đít hang"). Giờ mặt đất đỡ ngay dưới chân hang, còn nút → thì
   cưỡi lên đúng cái mép ấy — chỗ ngưỡng cửa, đúng nghĩa "bước vào".
   Cao 0 nên nút hiện hay không cũng không đẩy cảnh Rùa–Thỏ nhúc nhích một pixel.
   Phải có chiều rộng thật: đứa con bên trong là position:absolute nên ô này tự co về 0,
   và cái nút bị bóp thành một cục méo. */
.lock-cta {
  position: relative; display: grid; place-items: center;
  width: min(420px, 88vw); height: 0; z-index: 3;
}

/* Nút vào hang: chỉ hiện khi đã gõ được ít nhất một ký tự.
   Một mũi tên tròn, viền mảnh — cửa hang đang cần yên tĩnh, một cái nút xanh đặc
   chữ trắng nằm giữa màn hình thì hét to hơn cả ổ khoá. */
/* Nút đứng ngay NGƯỠNG CỬA hang, cưỡi lên cái mép giữa ụ đất và rặng cây (xem
   .lock-cta). Nền ĐẶC màu cát chứ không trong suốt như trước: chỗ này giờ là chỗ
   đông đúc — dưới nó là cánh cửa gỗ sẫm, rặng cây và tầng đất — để trong suốt thì
   cái vòng mảnh chìm nghỉm vào vân gỗ, bấm mà không thấy mình bấm vào đâu.
   translateY(9px): rơi khỏi mép dưới cánh cửa, đứng hẳn trên nền cát. */
.lock-go {
  position: absolute; opacity: 0; pointer-events: none;
  width: 40px; height: 40px; padding: 0; border-radius: 50%; cursor: pointer;
  display: grid; place-items: center; transform: translateY(9px);
  border: 1px solid rgba(15, 118, 110, .55); background: #f0e2c4; color: #0f766e;
  box-shadow: 0 2px 7px rgba(60, 40, 15, .28);
  font: 17px/1 var(--mono);
  transition: opacity .3s ease, transform .12s ease, background-color .2s ease, border-color .2s ease;
}
.lock.has .lock-go { opacity: 1; pointer-events: auto; }
.lock-go:hover { background: rgba(15, 118, 110, .08); border-color: #0f766e; }
/* Phải chép lại cả translateY của nút: một phần tử chỉ có MỘT transform, viết mỗi
   scale ở đây là nút nhảy ngược lên 9px đúng lúc ngón tay đang đè xuống. */
.lock-go:active { transform: translateY(9px) scale(.94); }
.lock.busy .lock-go, .lock.ok .lock-go { opacity: .45; pointer-events: none; }

/* BĂNG CHỮ — một khung, mọi lời của màn khoá thay nhau đứng đó (xem Bang trong app.js).
   Thay cho ba thứ cũ: hộp báo vé (.got-ticket), tên hang (.lock-hang) và lời nhắc dưới
   ổ khoá (.lock-hint). Ba cỡ chữ, ba màu, ba chỗ đứng — giờ còn một.

   CHIỀU CAO GHIM CỨNG ở 2 dòng, và đây là chỗ dễ làm hỏng nhất: câu "🎫 Hang Rêu Sáng"
   một dòng, câu vừa-nhận-vé hai dòng. Để ô tự co giãn thì cứ 4 giây cả cửa hang lại
   giật lên giật xuống một lần. Ghim ở 2 chứ không phải 3: mấy câu xoay vòng dài nhất
   cũng chỉ tới 2 dòng, để 3 thì lúc nào cũng thừa một dòng trắng giữa màn hình. */
.lock-say {
  display: grid; place-items: center;
  margin: 0 0 16px; padding: 0 12px;
  height: calc(12.5px * var(--fs) * 1.7 * 2);
  /* KHÔNG nền, KHÔNG viền, KHÔNG bóng. Cái hộp trắng bo góc từng đứng đây bị gạt
     đúng vì nó to và trắng hơn mọi thứ khác trên màn (chủ nhà, 17/08/2026). Chữ trần
     thì bề ngang bám theo chữ, và cửa hang được yên.
     NHẸ TAY: font đều nét như cái này mà lại to, sát nhau, đậm màu thì hai dòng chữ
     đã thành một mảng đặc — nhìn vào là ngán, không đọc (cũng chủ nhà, cùng ngày).
     Nên hạ cỡ, nới dòng ra 1.7, và chỉ đậm màu vừa đủ đọc trên nền trời: đây là câu
     DUY NHẤT chỉ người ta phải làm gì, mờ quá lại hỏng việc theo kiểu khác. */
  color: rgba(12, 30, 40, .7); font: calc(12.5px * var(--fs))/1.7 var(--mono);
  letter-spacing: .02em; text-align: center;
  /* Cùng lý do với .hang-say: câu lỡ dài hơn hai dòng thì cắt, không cho lòi ra đè
     lên ổ khoá bên dưới. */
  overflow: hidden;
  /* pre-line: câu nào tự ngắt dòng được thì ngắt (xem lockKey trong app.js) — mấy câu
     còn lại vẫn tự xuống dòng theo bề ngang như thường. */
  white-space: pre-line;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .75);
  max-width: min(420px, 88vw);
  transition: opacity .26s ease, color .26s ease;
  /* Chạm vào nó không mở được cửa (xem inKeyhole), nên đừng để nó bôi đen thành một
     vệt xanh khi người ta lỡ chạm — trông y như app hỏng. */
  user-select: none; -webkit-user-select: none;
}
/* Chỗ đáng nhớ nhất trong câu — cái chìa đã ghép xong trong câu ví dụ. Xanh của cửa
   hang (cùng màu nút → vào hang), đậm vừa phải: cả câu đang nhạt nên chỉ cần nhích
   một chút là mắt đã dừng đúng chỗ, tô đậm hết cỡ lại thành quát vào mặt người đọc. */
.lock-say .nhan { color: #0f766e; font-weight: 600; }

/* Lúc giao ca giữa hai câu. .26s này phải khớp với Bang.MO_DAN trong app.js: chữ
   được thay đúng lúc khung đang mờ hết, không thì thấy chữ nhảy giữa chừng. */
.lock-say.mo { opacity: 0; }
/* Chuyện hỏng: vé dẫn sang hang khác, hoặc gõ nhầm chìa hang cũ. Phải nhìn ra ngay
   là cảnh báo chứ không phải lời chào — và câu này KHÔNG xoay đi, nó ở lì tới khi
   người ta gỡ được chuyện. */
/* Được phép cao hơn 2 dòng: câu cảnh báo dài nhất tới ba bốn dòng, mà nó ĐỨNG YÊN
   nên có nở ra cũng chẳng làm ai giật mình — cắt cụt nó mới là hỏng thật. */
/* KHÔNG in đậm. Chữ đỏ đã đủ để mắt dừng lại; tô đậm thêm thì cả đoạn thành một
   vệt đỏ đặc, đọc nặng đầu mà chẳng rõ hơn tí nào. */
.lock-say.warn {
  color: #a01d13;
  height: auto; min-height: calc(12.5px * var(--fs) * 1.7 * 2);
}

/* Hai nút phụ dưới cảnh: vé, và trò đua. */
.lock-extras {
  position: relative; z-index: 1; margin-top: 18px;
  display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
}

/* Khối trò chơi trong hang. Chỉ có mặt khi hang chưa có lời nhắn nào. */
.play-box {
  flex: none; padding: 0 14px 14px;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
}
/* ============================================================
   BÀN TIỆC TRONG HANG — thay cho đường đua từng nằm ở đây.

   Vẫn là một khối đất được xén ra như cảnh ở cửa hang, để hai màn ra cùng một thế
   giới; nhưng trong lòng nó không còn đường hầm mà là một cái bàn dài, hai đứa ngồi
   hai đầu. Xem Tiec trong app.js.
   ============================================================ */
.feast-scene {
  position: relative; width: 100%; height: 132px;
  border-radius: 14px; overflow: hidden; cursor: pointer;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .18), 0 6px 16px rgba(0, 0, 0, .18);
  /* Trong lòng đất, ấm đèn: sẫm ở trên (trần hang), sáng dần xuống mặt bàn. */
  background: linear-gradient(#4a341c 0, #6d4d2a 40%, #8a6238 72%, #a1793f 100%);
  -webkit-tap-highlight-color: transparent;
}
/* BẾP LỬA sau lưng bàn ăn. Cả cảnh này là trong lòng đất, sẫm và lạnh — một đốm lửa
   cháy phía sau làm nó thành chỗ hai đứa ngồi cho ấm, chứ không phải cái hầm.
   Nằm z-index 0 (sau cả ghế): nó là NỀN, không được tranh chỗ với mâm cỗ. */
/* CỤM TÊN HANG + DẤU CỘNG, treo trong lòng bàn tiệc.
   Ngọn lửa đứng ở `bottom: 44px` giữa cảnh, nên cụm này ngồi ngay phía trên nó — cùng
   trục dọc, không lệch. z-index 1: đứng trên bếp lửa và quầng sáng (z 0) nhưng vẫn nằm
   dưới mặt bàn và mâm cỗ, nên nó là chữ TRONG phòng chứ không phải nhãn dán đè lên phòng.
   Định vị đặt ở CỤM, không đặt ở từng cái: hai đứa phải đi liền nhau và cả cụm mới là thứ
   được canh giữa — kê riêng thì mỗi cái trôi một nơi khi tên hang dài ngắn khác nhau. */
.hang-tag {
  position: absolute; z-index: 1;
  left: 50%; transform: translateX(-50%);
  bottom: 72px;                      /* lửa ở 44px + chiều cao ngọn lửa, chừa một nhịp thở */
  display: flex; align-items: center; gap: 6px;
  max-width: 92%;
}
/* Nền tối mờ sau chữ: cảnh này sáng tối đổi liên tục theo nhịp lửa thở, chữ trần đặt lên
   đó là lúc đọc được lúc không. `width: max-content` là chỗ dễ hụt — .id-chip vốn là
   display:flex, để nguyên thì nó giãn hết bề ngang cụm, thành thanh dài chứ không ôm chữ. */
.hang-tag .id-chip {
  width: max-content; min-width: 0;
  padding: 2px 9px; border-radius: 999px;
  background: rgba(28, 18, 8, .45);
  color: #f4e3c6;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hang-tag .id-chip:hover { color: #ffd98a; }
/* Dấu + tròn, cùng tông với viên chữ để đọc ra là MỘT cụm. Ô bấm 26px — nhỏ hơn hai cái
   vòng trên hàng đầu (36px) vì nó nằm trong tranh, to bằng là tranh giành chỗ với mâm cỗ. */
.hang-add {
  flex: none; width: 26px; height: 26px; padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: 50%; cursor: pointer;
  background: rgba(28, 18, 8, .45); color: #f4e3c6;
  -webkit-tap-highlight-color: transparent;
}
.hang-add svg { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 2.4; stroke-linecap: round; }
.hang-add:hover { background: var(--accent); color: var(--accent-ink); }

.feast-scene .fire {
  position: absolute; left: 50%; margin-left: -11px; bottom: 44px; z-index: 0;
  font-size: 20px; line-height: 1;
  animation: bep-lua 1.4s ease-in-out infinite;
}
/* Quầng sáng hắt ra từ bếp: to hơn ngọn lửa nhiều lần, mờ dần ra mép. Thở cùng nhịp
   với ngọn lửa nhưng lệch pha một chút, để ánh sáng không đập cứng như đèn nhấp nháy. */
.feast-scene .fire-glow {
  position: absolute; left: 50%; bottom: 20px; z-index: 0;
  width: 190px; height: 110px; margin-left: -95px; pointer-events: none;
  background: radial-gradient(ellipse at 50% 70%,
    rgba(255, 186, 92, .38) 0%, rgba(255, 150, 60, .16) 42%, transparent 72%);
  animation: bep-sang 2.1s ease-in-out infinite;
}
@keyframes bep-lua {
  0%, 100% { transform: scale(1) translateY(0); }
  50%      { transform: scale(1.14) translateY(-2px); }
}
@keyframes bep-sang {
  0%, 100% { opacity: .8; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .feast-scene .fire, .feast-scene .fire-glow { animation: none; }
}

/* ---------- TRUNG THU TRÊN TRẦN HANG ----------
   Trần hang vốn là một mảng nâu sẫm trống trơn chiếm gần nửa khung. Treo đèn lên đó thì
   cái bàn tiệc mới ra bữa tiệc (chủ nhà đặt 21/08/2026: *"sắp đến trung thu rồi… cho có
   không khí tráng lệ"*). Cả khối nằm gọn ở 56px trên cùng — đúng khoảng trống phía trên
   đầu hai đứa (đầu chúng bắt đầu ở y=56), nên không đứa nào phải nhường chỗ cho đứa nào. */
.feast-scene .trung-thu {
  position: absolute; left: 0; right: 0; top: 0; height: 56px; z-index: 2;
  pointer-events: none;   /* chạm vào đâu trong khung cũng là cụng ly, đèn đừng nuốt cú chạm */
}
/* TRĂNG RẰM ở góc phải, ngay trên đầu Thỏ. */
.feast-scene .trang {
  position: absolute; right: 6%; top: 5px; font-size: 22px; line-height: 1;
  filter: drop-shadow(0 0 9px rgba(255, 240, 190, .8));
}
/* Quầng trăng: tính toạ độ từ chính chỗ mặt trăng đứng (right 6% + nửa cái 22px, trừ
   nửa quầng 60px) nên xê dịch mặt trăng thì quầng đi theo, không phải chỉnh hai chỗ. */
.feast-scene .trang-quang {
  position: absolute; right: calc(6% - 19px); top: -14px;
  width: 60px; height: 60px; border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 244, 205, .30) 0%, rgba(255, 226, 150, .12) 45%, transparent 70%);
  animation: trang-tho 4.2s ease-in-out infinite;
}
/* ĐÈN LỒNG treo dây. `transform-origin` đặt ở CHỖ BUỘC DÂY (trên đầu cái đèn 14px), không
   phải giữa cái đèn: đu quanh chính mình thì nó xoay như cái chong chóng, đu quanh nút
   buộc mới ra cái đèn đang lắc. */
.feast-scene .den {
  position: absolute; font-size: 17px; line-height: 1;
  transform-origin: 50% -14px;
  animation: den-du 3.4s ease-in-out infinite;
  filter: drop-shadow(0 0 6px rgba(255, 140, 60, .55));
}
/* SỢI DÂY: một vạch mảnh chạy từ trần xuống tới nắp đèn. Thiếu nó thì mấy cái đèn
   trông như đang bay lơ lửng giữa hang. */
.feast-scene .den::before {
  content: ''; position: absolute; left: 50%; bottom: 100%;
  width: 1px; height: 14px; background: rgba(255, 226, 180, .5);
}
/* Bốn cái, mỗi cái một độ cao và một nhịp: treo bằng nhau và đu cùng phách thì ra một
   cái dây phơi, không ra cái hội. */
.feast-scene .den.d1 { left: 11%; top: 16px; animation-delay: -.2s;  animation-duration: 3.4s; }
.feast-scene .den.d2 { left: 33%; top: 9px;  animation-delay: -1.5s; animation-duration: 4.1s; }
.feast-scene .den.d3 { left: 55%; top: 15px; animation-delay: -.8s;  animation-duration: 3.7s; }
.feast-scene .den.d4 { left: 77%; top: 8px;  animation-delay: -2.1s; animation-duration: 4.4s; }
@keyframes den-du {
  0%, 100% { transform: rotate(-4.5deg); }
  50%      { transform: rotate(4.5deg); }
}
@keyframes trang-tho {
  0%, 100% { opacity: .75; }
  50%      { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .feast-scene .den, .feast-scene .trang-quang { animation: none; }
}

/* MẶT BÀN: chỉ là tấm gỗ. Đồ ăn KHÔNG nằm trong lòng nó mà bày lên trên (xem .dishes)
   — bàn tiệc thì bát đĩa phải đặt trên mặt bàn, y như hai cái ly. */
.feast-scene .table {
  position: absolute; left: 5%; right: 5%; bottom: 12px; height: 17px; z-index: 3;
  border-radius: 5px 5px 3px 3px;
  background: linear-gradient(#c98f4e, #a5702f 55%, #8a5c26);
  box-shadow: 0 3px 0 rgba(0, 0, 0, .25), inset 0 2px 0 rgba(255, 236, 200, .3);
}
/* MÂM CỖ đứng TRÊN mặt bàn. Mỗi món một cái ĐĨA — đĩa vẽ bằng CSS chứ không dùng emoji
   🍽️: emoji đĩa có sẵn dao nĩa hai bên, xếp tám cái cạnh nhau thành một hàng rào dao. */
.feast-scene .dishes {
  position: absolute; left: 29%; right: 29%; bottom: 27px; z-index: 4;
  display: flex; align-items: flex-end; justify-content: space-around;
}
.feast-scene .dishes span {
  font-size: 12px; line-height: 1; padding: 1px 3px 2px;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 70%, #fdf6e8 55%, #dcc9a8 78%, transparent 80%);
  box-shadow: 0 1px 0 rgba(0, 0, 0, .18);
}
.feast-scene .dishes span:nth-child(3n) { font-size: 14px; }
/* Dao nĩa dọn sẵn ở chỗ ngồi mỗi đứa, hai bên mép bàn. */
.feast-scene .cutlery {
  position: absolute; bottom: 25px; z-index: 4; font-size: 13px; line-height: 1; opacity: .9;
}
.feast-scene .cutlery.cut-left  { left: 21%; }
.feast-scene .cutlery.cut-right { right: 21%; transform: scaleX(-1); }

/* HAI ĐỨA ngồi hai đầu bàn, mặt đối mặt, và LÚC NÀO CŨNG NHÚC NHÍCH: đang ăn uống với
   nhau chứ không phải hai bức tượng kê hai đầu (chủ nhà, 17/08/2026).
   Rùa lắc lư chậm, Thỏ nhấp nhổm nhanh hơn — đúng tính hai đứa, và hai nhịp lệch nhau
   thì cảnh mới ra sống động chứ không ra hai cái máy đập cùng phách.
   CẢ HAI đều scaleX(-1): 🐢 vốn quay mặt sang trái (ngồi ghế trái mà để nguyên là quay
   lưng vào Thỏ), 🐇 vốn quay sang phải. Lật cả hai thì Rùa nhìn phải, Thỏ nhìn trái.
   Cú lật PHẢI nằm trong keyframes — đặt transform ở chỗ khác là animation ghi đè mất. */
/* CỠ 54px, không phải 36px như bản đầu (chủ nhà chốt 21/08/2026: *"rùa và thỏ và ghế
   của chúng phải to hơn nữa… chúng trông nhỏ bé như thức ăn"*). Đây là cảnh HAI ĐỨA
   NGỒI VỚI NHAU — hai đứa phải là thứ to nhất trong khung, mâm cỗ chỉ là đồ bày. Để
   36px thì con vật chỉ nhỉnh hơn cái đĩa một chút, mắt đọc ra một bàn ăn chứ không đọc
   ra hai người bạn.
   Nhích ra sát mép (8% → 3%) đúng vì lẽ đó: to lên mà đứng nguyên chỗ cũ thì vai hai
   đứa chờm vào mâm cỗ ở giữa, mà mâm cỗ nằm z-index cao hơn nên nó phủ lên mặt chúng. */
.feast-scene .guest {
  position: absolute; bottom: 22px; z-index: 1;
  font-size: 54px; line-height: 1;
  filter: drop-shadow(0 3px 4px rgba(0, 0, 0, .35));
}
.feast-scene .guest.turtle { left: 3%; animation: tiec-rua 2.6s ease-in-out infinite; }
.feast-scene .guest.rabbit { right: 3%; animation: tiec-tho 1.5s ease-in-out infinite; }
@keyframes tiec-rua {
  0%, 100% { transform: scaleX(-1) translateY(0) rotate(4deg); }
  50%      { transform: scaleX(-1) translateY(-3px) rotate(-4deg); }
}
/* THỎ KHÔNG lật: con 🐇 vốn đã quay mặt sang TRÁI, ngồi ghế bên phải là đúng hướng
   nhìn sang Rùa rồi. Lật thêm một lần nữa là nó quay đi (chủ nhà bắt 17/08/2026).
   Rùa thì ngược lại — 🐢 quay sang trái nên phải lật mới nhìn sang Thỏ được. */
@keyframes tiec-tho {
  0%, 100% { transform: translateY(0); }
  40%      { transform: translateY(-7px); }
  60%      { transform: translateY(-2px); }
}
/* HAI CÁI GHẾ, nằm sau cùng (z 0) nên con vật trông như ngồi lên chứ không ôm ghế.
   To lên theo hai đứa (34px → 50px) và cũng lùi ra mép (7% → 2%): ghế bé hơn người
   ngồi thì cả hai đứa trông như đang lơ lửng, mà đó lại là thứ mắt bắt được ngay. */
.feast-scene .chair {
  position: absolute; bottom: 12px; z-index: 0;
  font-size: 50px; line-height: 1; opacity: .95;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .3));
}
.feast-scene .chair.c-left  { left: 2%; }
.feast-scene .chair.c-right { right: 2%; transform: scaleX(-1); }

/* HAI CÁI LY để NGAY TRƯỚC MẶT mỗi đứa, không phải giữa bàn — ly là của ai nấy cầm. */
.feast-scene .glass {
  position: absolute; bottom: 30px; z-index: 5;
  font-size: 19px; line-height: 1;
  transition: transform .26s cubic-bezier(.34, 1.4, .64, 1);
}
.feast-scene .glass.g-left  { left: 17%; }
.feast-scene .glass.g-right { right: 17%; }
/* CỤNG: hai ly BAY LÊN KHÔNG TRUNG rồi gặp nhau ở giữa bàn, nghiêng vào nhau như hai
   người chạm ly. Buông tay ra là chúng rơi về chỗ cũ — nhờ transition ở trên. */
.feast-scene.cung .glass.g-left  { transform: translate(115%, -26px) rotate(22deg); }
.feast-scene.cung .glass.g-right { transform: translate(-115%, -26px) rotate(-22deg); }
.feast-scene .spark {
  position: absolute; bottom: 62px; left: 50%; margin-left: -9px; z-index: 6;
  font-size: 18px; line-height: 1; opacity: 0; transform: scale(.4);
  transition: opacity .18s ease, transform .18s ease;
}
.feast-scene.cung .spark { opacity: 1; transform: scale(1.15); }
@media (prefers-reduced-motion: reduce) {
  .feast-scene .glass, .feast-scene .spark { transition: none; }
  .feast-scene .guest { animation: none; }
  .feast-scene .guest.turtle { transform: scaleX(-1); }
}


/* Máy thứ ba gõ cửa: dải cảnh báo ngay trên ô soạn tin, không bỏ qua được. */
.knock-ask {
  flex: none; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 14px 8px; padding: 9px 12px; border-radius: 12px;
  background: var(--panel-2); border: 1px solid var(--warn);
  font: calc(11.5px * var(--fs))/1.5 var(--mono); color: var(--text);
}
.knock-ask span { flex: 1; min-width: 180px; }
.knock-ask button {
  padding: 5px 12px; cursor: pointer; border-radius: 999px;
  border: 1px solid var(--line); background: var(--panel); color: var(--text);
  font: calc(11.5px * var(--fs)) var(--mono);
}
#btnKnockYes { background: var(--warn); color: var(--accent-ink); border-color: var(--warn); }
/* Bị thay dồn dập: đổi sang giọng báo động, không còn là chuyện đổi máy bình thường. */
.knock-ask.alarm { border-color: var(--danger); }
.knock-ask.alarm #btnKnockYes { background: var(--danger); border-color: var(--danger); color: #fff; }
.play-box .btn { max-width: 320px; }

/* Lối vào bằng tấm vé, ngay ở màn khoá. z-index: phải nổi trên mảng đất của
   cảnh (.ground::after là phần tử định vị nên mặc định nó phủ lên trên). */
/* Chữ, không phải nút. Cửa hang phải yên — một cái nút bo tròn to đùng ở đây
   kéo hết sự chú ý khỏi ổ khoá, là thứ người ta thật sự cần thấy. */
/* Mấy lối phụ này nằm TRONG LÒNG ĐẤT (dưới vạt cỏ), nên chữ phải sáng — để màu
   than như hồi nền còn là đồng cỏ thì giờ chìm nghỉm vào đất. */
.ticket-cta {
  position: relative; z-index: 1;
  padding: 6px 4px; border: 0; background: none; cursor: pointer;
  font: calc(12px * var(--fs)) var(--mono); color: rgba(255, 244, 224, .82);
  text-decoration: underline; text-decoration-style: dotted;
  text-underline-offset: 4px; transition: color .25s ease;
}
.ticket-cta b { color: #ffd98a; font-weight: 700; }
.ticket-cta:hover { color: #fff8ec; }
/* Đường về hang mình / dựng hang riêng: mỗi cái CHIẾM TRỌN MỘT DÒNG (width: 100%),
   không chen chung hàng với lời mách "tôi có một tấm vé" — lúc cần tới chúng là lúc
   đang hoang mang, phải thấy ngay chứ không phải mò giữa một hàng chữ.
   Không gạch chân: viền bo tròn đã nói đây là nút bấm rồi. */
.back-hang {
  width: 100%; margin-top: 6px; text-align: center;
  text-decoration: none;                       /* không gạch chân, kể cả kiểu chấm */
  padding: 9px 14px; border-radius: 999px;
  border: 1px solid rgba(255, 226, 160, .5);
  background: rgba(255, 226, 160, .12);
  color: #ffe9b8;                              /* vàng kem, nổi rõ trên nền đất */
  font-weight: 600; letter-spacing: .02em;
}
.back-hang:hover { color: #fff; background: rgba(255, 233, 184, .18); border-color: rgba(255, 233, 184, .8); }
/* Lời mách nhỏ, có mũi nhọn chỉ ngược lên cái nút vừa bấm — để mắt nối được
   "tôi bấm cái kia" với "cái này vừa hiện ra". */
.ticket-tip {
  /* Nổi ra ngoài dòng chảy, treo NGAY TRÊN hàng nút. Nằm trong dòng chảy thì mỗi
     lần bật lên là đẩy cả cảnh Rùa–Thỏ trượt lên trên — đúng thứ cần tránh.
     Mở LÊN chứ không mở xuống: hàng nút này nằm gần đáy màn hình, mà .lock có
     overflow:hidden, nên thả xuống dưới là phần đuôi (đúng chỗ có nút chọn ảnh vé)
     bị cắt mất — bấm mãi không thấy chỗ tải ảnh lên đâu.
     Còn max-height: đề phòng màn hình thấp (điện thoại nằm ngang, bàn phím bật) —
     hết chỗ thì cuộn trong lòng lời mách, không tràn ra ngoài. */
  position: absolute; bottom: calc(100% + 11px); left: 50%; transform: translateX(-50%);
  z-index: 3; width: min(360px, 86vw);
  max-height: min(46vh, 300px); overflow-y: auto; overscroll-behavior: contain;
  padding: 13px 16px; border-radius: 12px;
  background: #14161a; color: #f2f1ec;
  font: calc(11.5px * var(--fs))/1.65 var(--mono);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .28);
}
.ticket-tip::after {                     /* mũi nhọn chỉ XUỐNG nút vừa bấm */
  content: ''; position: absolute; bottom: -6px; left: 50%; margin-left: -6px;
  border-left: 6px solid transparent; border-right: 6px solid transparent;
  border-top: 6px solid #14161a;
}
.ticket-tip p { margin: 0 0 7px; }
.ticket-tip p:last-of-type { margin-bottom: 0; }
.ticket-tip b { color: #fff; font-weight: 600; }
/* Căn giữa: đây là VIỆC PHẢI LÀM của cả lời mách, hai dòng chữ bên trên chỉ là dẫn
   chuyện. Nép sát mép trái dưới một khối chữ thì nó lẫn vào như một dòng nữa của
   đoạn văn, ai đọc lướt cũng trượt qua. display:block để margin auto ăn. */
.tip-link {
  display: block; margin: 9px auto 0; padding: 0; border: 0; background: none; cursor: pointer;
  color: #7fe3d8; font: calc(11.5px * var(--fs)) var(--mono); text-decoration: underline;
}
.ticket-tip .err { margin: 7px 0 0; color: #ff9b93; }

/* Ổ KHOÁ trên cánh cửa: một tấm đồng lõm, chìa gõ tới đâu hiện tới đó ở đây.
   Chữ phải màu sáng — để màu than như cũ thì gõ xong chẳng thấy gì trên nền gỗ sẫm. */
.lock .mark {
  position: relative;
  /* Khổ CỐ ĐỊNH, hẹp hơn cánh cửa (cửa rộng 64% ụ đất). Trước đây nó nở theo số
     cà rốt: gõ chìa 14 ký tự là hàng cà rốt dài hơn cả ụ đất, tràn ra hai bên và
     cắt ngang cánh cửa — nhìn như giao diện vỡ. Cà rốt tự co cho vừa, xem paint(). */
  width: 54%; padding: 7px 10px; border-radius: 999px; overflow: hidden;
  background: rgba(24, 16, 9, .55);
  border: 1px solid rgba(255, 232, 186, .28);
  font: 24px/1 var(--mono); color: #f4e7c8; opacity: .9;
  transition: opacity .35s ease, color .35s ease;
  animation: keyhole-breathe 3.4s ease-in-out infinite;
}
/* Hàng cà rốt phải vừa trong tấm khoá. min-height 40px của bản gốc là để chứa dấu
   chấm cỡ 40px hồi ổ khoá còn là một cái chấm trơ; giờ trong đó là cà rốt 15px. */
.lock .mark .pins { min-height: 24px; }
.lock:hover .mark { opacity: 1; }
/* Thở nhè nhẹ: một quầng ấm quanh ổ khoá, đủ để mắt hiểu "bấm vào đây" mà không ồn. */
@keyframes keyhole-breathe {
  0%, 100% { box-shadow: inset 0 2px 5px rgba(0, 0, 0, .5); }
  50%      { box-shadow: inset 0 2px 5px rgba(0, 0, 0, .5), 0 0 0 7px rgba(255, 206, 110, .18); }
}
/* Đang gõ rồi thì thôi mời mọc nữa. */
.lock.has .mark { animation: none; box-shadow: inset 0 2px 5px rgba(0, 0, 0, .5); }
.lock.has .mark { opacity: 1; }         /* đang gõ thì các chấm phải rõ */

/* Ô nhập kiểu mã PIN: mỗi ký tự một chấm, gõ tới đâu mọc tới đó.
   transform-origin giữa: lúc phải co lại cho vừa ổ khoá thì co đều về hai bên. */
.pins {
  display: flex; align-items: center; justify-content: center; gap: 3px; min-height: 40px;
  transform-origin: 50% 50%; transition: transform .12s ease;
}
/* Chưa gõ gì: một cái khoá đang treo trên cửa. Nó nói ra ngay cái hang này ĐANG
   KHOÁ, và gõ chìa vào thì khoá biến thành cà rốt — mở dần từng nấc. */
/* Củ cà rốt giữa cánh cửa, thế chỗ cái ổ khoá 🔒 của bản cũ (21/08/2026). Ổ khoá là thứ
   duy nhất trên màn khoá còn nói "muốn vào phải biết một bí mật" — mà cửa hang giờ không
   hỏi ai câu nào, nên để nó lại là nói dối ngay từ màn hình đầu tiên. */
.cua-mo { font-size: 21px; line-height: 1; }
/* Gõ tới đâu rơi cà rốt tới đó. Cỡ 15px + khe 3px: gõ hết 14 ký tự (PIN_MAX)
   vẫn vừa bề ngang điện thoại nhỏ, không tràn ra khỏi ổ khoá. */
.pin {
  font-size: 15px; line-height: 1;
  animation: pinpop .16s ease-out;
}
.pin::before { content: '🥕'; }
@keyframes pinpop { from { transform: scale(.2); opacity: 0; } }
/* Gõ một phím thì chỉ NHÁY, không phóng to. Hồi ổ khoá còn là một dấu chấm trơ,
   phóng 1.35 lần mỗi lần gõ nhìn vui; giờ nó là tấm khoá có viền có nền, phóng cả
   tấm là màn hình giật lên giật xuống theo từng ký tự. */
.lock.tap .mark { opacity: 1; transition-duration: .08s; }
/* Đang soát chìa: mờ tỏ nhè nhẹ. Trước đây dùng @breathe — cái đó phóng to 1,5 lần,
   giờ thành tấm khoá phình ra co vào giữa màn hình. */
.lock.busy .mark { animation: plate-wait .9s ease-in-out infinite; }
@keyframes plate-wait { 0%, 100% { opacity: .5; } 50% { opacity: .95; } }
.lock.bad .mark { color: #ff9b91; opacity: 1; animation: shake .4s; }
/* Mở được cửa: cả màn khoá tan dần để lộ hang bên dưới. Trước đây phóng dấu chấm to
   26 lần — thành một cục tròn bự chảng nuốt cả màn hình, nhìn thô. */
.lock.ok { opacity: 0; pointer-events: none; transition: opacity .34s ease; }
.lock.ok .mark { color: #ffd98a; opacity: .85; transition-duration: .34s; }
@keyframes breathe { 0%,100% { transform: scale(1); } 50% { transform: scale(1.5); } }

/* ---------- cuộc đua Rùa & Thỏ ở màn khoá ----------
   Hai lớp lồng nhau vì một phần tử chỉ chạy được MỘT transform:
   lớp ngoài (.critter) lo đi ngang, lớp trong (span) lo nhảy / lắc mình. */
/* Cả cảnh nằm ngay dưới ổ khoá, trong dòng chảy của cột — không ghim đáy nữa.
   Bàn phím điện thoại bật lên thì cả cụm tự dồn lên theo, không đứa nào bị che.
   margin-top 8px: rặng cây đứng SÁT ĐÍT HANG, để cái hang có đất mà đứng lên. Trước
   đây chỗ này là 22px, cộng thêm cả một hàng 42+14px của nút → ở trên nữa, thành ra
   giữa hang và mặt đất hở một mảng trời 78px — nhìn ra ngay cái hang đang bay lơ lửng
   (chủ nhà chốt 17/08/2026). Nút → nay cưỡi lên mép ấy, không chiếm hàng nào nữa. */
.scene { position: relative; width: 100%; margin-top: 8px; pointer-events: none; }


.race {
  position: relative; width: 100%; height: 66px;
  overflow: hidden; pointer-events: none;      /* chạm vào con vật vẫn tính là chạm màn khoá */
}
/* KHO BÁU chất dưới gầm bàn tiệc, sát nền hang — trong hang phải có của thì mới ra
   cái hang giàu. Cỡ nhỏ và so le: xếp đều tăm tắp cùng cỡ là ra cái thước kẻ chứ
   không ra đống của. Nằm DƯỚI mặt bàn (z-index 0) nên không tranh chỗ với mâm cỗ. */
.feast-scene .hoard {
  display: flex; position: absolute; left: 0; right: 0; bottom: 0; z-index: 0;
  height: 16px; align-items: flex-end; justify-content: space-around;
  padding: 0 6px 2px; pointer-events: none;
}
.feast-scene .hoard span { font-size: 11px; line-height: 1; opacity: .72; }
.feast-scene .hoard span:nth-child(2n) { font-size: 9px;  opacity: .55; }
.feast-scene .hoard span:nth-child(3n) { font-size: 13px; opacity: .8; }
.feast-scene .hoard span:nth-child(5n) { font-size: 10px; opacity: .62; }
/* Đường hầm nằm TRONG đất nên phải nổi lên trên mảng đất (.lock .ground::after vẽ
   sau, không cho z-index thì nó phủ mất hai con vật). Lòng hầm sáng hơn đất xung
   quanh một chút — chỗ đã được đào rỗng. */
.lock .race {
  z-index: 1;
  background: linear-gradient(rgba(255, 236, 200, .16), rgba(255, 236, 200, .05));
  box-shadow: inset 0 6px 12px rgba(0, 0, 0, .28);
}
/* Vạch đứt giữa hầm: trên nền đất sẫm thì vạch đen tàng hình, phải đổi sang vạch sáng. */
.lock .road-line {
  background: repeating-linear-gradient(to right,
    rgba(255, 240, 214, .3) 0 15px, transparent 15px 30px);
}
/* Cái hang ở cuối hầm: ụ đất sáng màu giữa lòng đất thì lạc quẻ, cho nó sẫm lại
   thành một khoang khoét sâu vào vách. */
.lock .burrow::before { background: #6a4c2c; }

/* Vạch đứt giữa đường, chạy ngược lại hướng hai con vật → thấy như đang tiến tới. */
.road-line {
  position: absolute; left: 0; right: 0; bottom: 7px; height: 2px;
  background: repeating-linear-gradient(to right,
    rgba(0, 0, 0, .17) 0 15px, transparent 15px 30px);
  animation: road-scroll 1.5s linear infinite;
}
@keyframes road-scroll { to { background-position-x: -30px; } }

/* Cái hang ở cuối đường: một ụ đất với cái miệng hang tối om.
   Nhỏ và nhích lên cao cho ra vẻ ở XA, và z-index thấp nhất để Rùa với Thỏ luôn
   chạy ĐẰNG TRƯỚC nó — trước đây hang đè lên, tới đích là hai con biến mất. */
.burrow {
  position: absolute; right: 14px; bottom: 11px;
  width: 46px; height: 26px; z-index: 0; opacity: .9;
}
.burrow::before {                       /* ụ đất */
  content: ''; position: absolute; inset: 0;
  background: #cfc4ae; border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}
.burrow-mouth {                         /* miệng hang */
  position: absolute; left: 50%; bottom: 0; width: 22px; height: 16px;
  transform: translateX(-50%);
  background: #3a332a; border-radius: 50% 50% 0 0 / 100% 100% 0 0;
}

/* Vạt cỏ: ba lớp lá nghiêng khác chiều, bước lặp lệch nhau (9/13/17) để mắt không
   bắt được nhịp — hai lớp bước đều nhau thì nhìn ra ngay là hoa văn lặp. */
.ground {
  position: relative;
  height: 14px; border-top: 1px solid rgba(0, 0, 0, .07);
  background:
    repeating-linear-gradient(76deg,  #9cc286 0 1.5px, transparent 1.5px 9px),
    repeating-linear-gradient(104deg, #8bb474 0 1.5px, transparent 1.5px 13px),
    repeating-linear-gradient(88deg,  #adcf99 0 1.5px, transparent 1.5px 17px);
}
/* Đất kéo xuống hết màn hình: không có nó thì vạt cỏ lửng lơ giữa trang trắng,
   nhìn như cái gạch ngang chứ không ra mặt đất.
   CHỈ ở cửa hang — .lock có overflow:hidden nên cắt gọn phần thừa. Trong hang thì
   không ai cắt, để nguyên là nó trào xuống phủ kín cả ô soạn tin. */
/* LÒNG ĐẤT, trải xuống hết màn hình. Vạt cỏ ở trên là mặt đất, nên tất cả phía
   dưới phải là đất thịt: sẫm và ẩm ngay dưới lớp cỏ, ấm dần rồi lại sẫm khi xuống
   sâu, lấm tấm sỏi. Trước đây chỗ này là một mảng xanh nhạt — hoá ra Rùa với Thỏ
   đào hầm giữa một bãi cỏ lửng lơ.
   CHỈ ở cửa hang — .lock có overflow:hidden nên cắt gọn phần thừa. Trong hang thì
   không ai cắt, để nguyên là nó trào xuống phủ kín cả ô soạn tin. */
/* TẦNG ĐẤT MẶT: một dải đất pha đá màu cam đất, đánh dấu đây là lớp trên cùng —
   trên nó là trời, dưới nó là các tầng đất và đường hầm. Dùng chung cho cửa hang
   và trò đua trong hang. */
.lock .ground {
  height: 20px; border-top: 1px solid rgba(120, 70, 25, .35);
  background:
    radial-gradient(2px 1.5px at 14% 62%, rgba(255, 248, 232, .4) 0 100%, transparent 100%),
    radial-gradient(3px 2px   at 39% 34%, rgba(92, 48, 12, .32)   0 100%, transparent 100%),
    radial-gradient(2px 2px   at 61% 70%, rgba(255, 248, 232, .32) 0 100%, transparent 100%),
    radial-gradient(3px 2px   at 87% 42%, rgba(92, 48, 12, .28)   0 100%, transparent 100%),
    linear-gradient(#e0a45c 0, #d08f47 55%, #b9762f 100%);
}
.lock .ground::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 100vh;
  background:
    /* sỏi và mùn lấm tấm — đất phẳng lì một màu nhìn ra ngay là mảng tô */
    radial-gradient(3px 2px at 11% 34px,  rgba(255, 244, 222, .22) 0 100%, transparent 100%),
    radial-gradient(2px 2px at 67% 62px,  rgba(255, 244, 222, .18) 0 100%, transparent 100%),
    radial-gradient(4px 3px at 86% 128px, rgba(0, 0, 0, .16) 0 100%, transparent 100%),
    radial-gradient(3px 2px at 29% 172px, rgba(0, 0, 0, .15) 0 100%, transparent 100%),
    radial-gradient(2px 2px at 48% 232px, rgba(255, 244, 222, .14) 0 100%, transparent 100%),
    radial-gradient(5px 3px at 74% 288px, rgba(0, 0, 0, .13) 0 100%, transparent 100%),
    /* CÁC TẦNG ĐẤT, như nhát cắt bổ dọc: mùn đen sát chân cỏ → đất thịt → tầng sét
       đỏ → tầng cát sáng → đá mẹ sẫm ở dưới cùng. Mỗi tầng chuyển sang tầng sau
       trong 6px cho mềm; chuyển gắt thì ra mấy sọc kẻ, chuyển dài thì lại nhoè
       thành một mảng nâu không còn tầng nào. */
    linear-gradient(
      #4b3319 0, #4b3319 14px,
      #6d4d2a 20px, #6d4d2a 96px,
      #8a6238 102px, #8a6238 210px,
      #9c7a4e 216px, #9c7a4e 330px,
      #7d5c36 336px, #7d5c36 470px,
      #5a4126 476px, #5a4126 100%);
}
.critter {
  position: absolute; bottom: 2px; left: -76px; z-index: 2;   /* luôn chạy TRƯỚC cái hang */
  display: block; line-height: 1; will-change: transform;
}
.critter > span { display: block; line-height: 1; }

/* Bụi bốc sau đuôi Rùa mỗi lần bị thúc — nó đang RẤT cố, dù vẫn chậm.
   Nằm bên trái vì Rùa quay mặt sang phải; scaleX(-1) cho luồng bụi thổi ngược ra sau. */
.dust {
  position: absolute; right: 100%; bottom: 3px; margin-right: -8px;
  font-size: 15px; line-height: 1; opacity: 0; pointer-events: none;
  transform: scaleX(-1);
}
.critter.puff .dust { animation: puff .5s ease-out; }
/* Ở cửa hang không ai thúc được Rùa (cảnh đó chỉ để ngắm), nên luồng gió cứ phả
   đều đều — nó vẫn đang gắng hết sức, chậm mà đều.
   margin-right dương = lùi hẳn RA SAU đuôi Rùa. Bản trong hang để -8px cho luồng
   gió trùm lên mai (lúc bị thúc thì phải thấy nó bật ra từ dưới thân), nhưng ở đây
   gió phả đều nên dính sát quá trông như con Rùa đang ngồi lên chùm khói. */
.lock .dust {
  margin-right: 5px; bottom: 8px;
  /* KHÔNG dùng emoji 💨 ở đây: cụm mây tròn nhỏ nằm ngay sau đuôi Rùa nhìn ra thứ
     khác mất. Vẽ hai vạch gió bằng CSS — vạch ngang sau lưng là quy ước "đang lao
     đi" ai cũng đọc được, mà lại không gợi ra cái gì. font-size: 0 để nuốt luôn
     ký tự emoji nằm trong HTML (bản trong hang vẫn dùng nó lúc bị thúc). */
  font-size: 0; width: 20px; height: 11px; color: rgba(255, 240, 214, .6);
  background:
    linear-gradient(currentColor, currentColor) no-repeat 0   2px / 20px 1.6px,
    linear-gradient(currentColor, currentColor) no-repeat 5px 7px / 13px 1.6px;
  animation: puff 1.9s ease-out infinite;
}
@keyframes puff {
  0%   { opacity: 0;  transform: scaleX(-1) translateX(0) scale(.45); }
  25%  { opacity: .95; }
  100% { opacity: 0;  transform: scaleX(-1) translateX(-16px) scale(1.2); }
}
@media (prefers-reduced-motion: reduce) {
  .critter.puff .dust, .lock .dust { animation: none; }
}

/* Rùa: bò một mạch, chậm rì, thỉnh thoảng nghiêng mình cho đỡ mỏi. */
.turtle { font-size: 38px; opacity: 1; animation: crawl 38s linear infinite; animation-delay: -9s; }
.turtle > span { transform: scaleX(-1); animation: waddle 2.6s ease-in-out infinite; }

/* Thỏ: nhảy tưng tưng, phóng vèo được nửa đường thì ngủ quên (nhảy tại chỗ), rồi lại phóng. */
.rabbit { font-size: 32px; opacity: .95; animation: dash 13s ease-in-out infinite; }
.rabbit > span { display: block; transform: scaleX(-1); animation: hop .62s ease-in-out infinite; }

@keyframes crawl { to { transform: translateX(calc(min(100vw, 768px) + 76px)); } }
/* Hai con emoji vốn quay mặt sang trái — lật lại cho đúng hướng đang chạy. */
@keyframes waddle {
  0%, 100% { transform: scaleX(-1) rotate(-5deg); }
  50%      { transform: scaleX(-1) rotate(5deg); }
}
@keyframes dash {
  0%   { transform: translateX(0); }
  30%  { transform: translateX(min(58vw, 594px)); }   /* phóng lên dẫn đầu */
  62%  { transform: translateX(min(58vw, 594px)); }   /* ngủ quên giữa đường */
  100% { transform: translateX(calc(min(100vw, 768px) + 76px)); }
}
@keyframes hop {
  0%, 100% { transform: scaleX(-1) translateY(0); }
  45%      { transform: scaleX(-1) translateY(-17px); }
}

/* Gõ sai thì hai đứa giật mình. */
.lock.bad .critter > span { animation-duration: .22s; }
/* Rời mắt khỏi trang thì hai đứa nghỉ. Chạy hoài thì máy phải vẽ lại liên tục,
   ngồi lâu ở màn khoá là quạt kêu. */
:root.away .critter, :root.away .critter > span,
:root.away .road-line, :root.away .lock .mark, :root.away .lock-logo {
  animation-play-state: paused;
}
/* Ai không thích chuyển động thì cho cả cảnh đứng yên. */
@media (prefers-reduced-motion: reduce) {
  .critter, .critter > span, .road-line, .keyhole { animation: none; }
  .turtle { left: 22vw; } .rabbit { left: 62vw; }
}
@keyframes shake {
  0%,100% { transform: translateX(0); }
  20% { transform: translateX(-7px); } 40% { transform: translateX(7px); }
  60% { transform: translateX(-4px); } 80% { transform: translateX(4px); }
}
/* Ô nhập thật: vô hình, nhưng PHỦ ĐÚNG lên cửa hang (nó nằm trong #keyhole).
   Chạm vào hang = chạm thẳng vào ô nhập, nên bàn phím do chính trình duyệt bật lên,
   không cần JS gọi focus() hộ. Trước đây nó là một chấm 1px giấu giữa màn hình, cú
   chạm rơi xuống ụ đất chứ không rơi vào ô nhập, nên phải nhờ JS focus() hộ — cả một
   lối vòng, mà lối vòng nào cũng có chỗ hụt chân trên iPhone.
   (Đây là bản dựng cho CHẮC. Con lỗi "bàn phím bật rồi tắt" người dùng báo 17/08/2026
   hoá ra nằm chỗ khác hẳn — xem wipeThenReload trong app.js.)
   Vẫn phải là một ô nhập THẬT nhìn thấy được (rộng bằng cả cửa hang, chỉ trong suốt):
   giấu bằng opacity: 0 hay display: none là Safari xếp vào loại không gõ được.
   user-select: text: cả màn khoá đang user-select: none (xem .lock) cho khỏi bôi đen
   cảnh vật; WebKit đời nay vẫn cho gõ, nhưng đời cũ thì không — một dòng, mua đứt.
   font-size 16px: dưới mốc đó iPhone phóng to trang mỗi lần chạm vào (xem killZoom). */
/* TÊN PHẢI RIÊNG. Trước đây lớp này tên là .ghost, trùng đúng chữ với .btn.ghost
   (nút "Đóng" trong mọi hộp thoại) — nút Đóng ăn nguyên bộ position:absolute của
   ô nhập tàng hình: nó văng ra giữa màn hình, rộng bằng cả bề ngang trình duyệt,
   chữ "Đóng" chạy ra ngoài mép, và cái thân vô hình của nó nằm đè lên nút ngay
   giữa hộp — bấm "Câu thần chú" thì hộp thoại đóng cái rụp. */
.ghost-input {
  position: absolute; inset: 0; z-index: 2;
  padding: 0; margin: 0; border: 0; outline: 0; background: transparent;
  color: transparent; caret-color: transparent; font-size: 16px;
  cursor: pointer; -webkit-appearance: none; appearance: none;
  user-select: text; -webkit-user-select: text;
}

/* ---------- nút & hộp thoại ---------- */
/* min-height 44px: dưới mốc đó thì ngón tay trên điện thoại bấm trượt sang nút bên
   cạnh — cả hộp ▤ toàn nút rộng bằng nhau xếp chồng, trượt một nhịp là bấm nhầm. */
.btn {
  padding: 10px 14px; min-height: 44px; border-radius: var(--radius);
  border: 1px solid var(--line);
  background: var(--panel-2); color: var(--text); font: calc(12px * var(--fs)) var(--mono); cursor: pointer;
}
.btn:hover { border-color: var(--accent); color: var(--accent); }
.btn.primary { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.btn.danger { background: transparent; color: var(--danger); border-color: var(--danger); }
/* Nặng hơn nút thường, nhẹ hơn "Quét dọn": mất dữ liệu nhưng còn giữ chìa. */
.btn.danger-soft { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 55%, transparent); }
.btn.ghost { background: transparent; border-color: transparent; color: var(--dim); }
/* Khoảng cách 10px thay vì 7: mấy nút trong hộp ▤ nằm sát nhau quá thì bấm "Câu
   thần chú" hay trúng "Đào hang mới" ngay dưới nó — hai việc lệch nhau một trời. */
.btn.wide { width: 100%; margin-top: 10px; }
.btn-icon {
  width: 28px; height: 28px; flex: none; border: 0; border-radius: var(--radius); cursor: pointer;
  background: transparent; color: var(--dimmer); display: grid; place-items: center;
}
.btn-icon:hover { color: var(--accent); background: var(--panel-2); }
.btn-icon.danger:hover { color: var(--danger); }

.modal-bg {
  position: fixed; inset: 0; background: rgba(0,0,0,.72);
  display: grid; place-items: center; padding: 20px; z-index: 50;
}
.modal {
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 22px; width: min(460px, 100%); max-height: 88dvh; overflow-y: auto;
}
/* NÚT × GÓC PHẢI TRÊN. `position: sticky` chứ không phải `absolute`, và đó là cả điểm
   của nó: chính CÁI HỘP là thứ cuộn (`overflow-y: auto` ở trên), nên nút absolute sẽ
   trôi mất theo nội dung — mà hộp Thỏ cao 1363px, tức là đúng cái hộp cần nút này nhất
   lại là cái hộp mất nó sớm nhất. Sticky thì nó dính ở mép trên suốt lúc cuộn.
   `float: right` để nó KHÔNG chiếm một dòng riêng đẩy cái tựa xuống — tựa và nút nằm
   cùng một hàng, y như mọi hộp thoại khác trên đời.
   Có NỀN riêng vì chữ cuộn qua phía sau nó. */
.modal-x {
  position: sticky; top: -8px; z-index: 5; float: right;
  width: 34px; height: 34px; margin: -8px -8px 0 10px;
  border: 0; border-radius: 50%; cursor: pointer;
  background: var(--panel-2); color: var(--dim);
  font: 300 calc(21px * var(--fs))/1 var(--mono);
  display: grid; place-items: center;
  transition: color .15s, background .15s;
}
.modal-x:hover, .modal-x:focus-visible { color: var(--text); background: var(--line); outline: none; }
.modal h3 { margin: 0 0 10px; font: 600 calc(12px * var(--fs)) var(--mono); letter-spacing: .14em; text-transform: uppercase; color: var(--accent); }
.muted { color: var(--dim); font-size: calc(12px * var(--fs)); margin: 0 0 14px; line-height: 1.7; }
/* Lời mời ly nước đậu nành có hai đoạn, nên phải giữ chỗ xuống dòng trong TEXT. */
#lyNuocNoi { white-space: pre-line; }
.muted.small { font-size: calc(10.5px * var(--fs)); margin-bottom: 10px; }

/* Nhãn ô chìa Gemini: chữ nhỏ đứng trên ô, kèm dấu ✓ khi đã có chìa. */
.tho-chia-nhan {
  display: block; margin: 12px 0 5px;
  font: 600 calc(11px * var(--fs)) var(--mono);
  letter-spacing: .06em; color: var(--dim);
}
/* Ô vuông QR: nền TRẮNG bất kể nền màu nào đang bật — máy quét mã cần tương phản
   thật, mã đen trên nền đất là quét trượt. */
.ly-qr { display: grid; place-items: center; margin: 0 0 14px; }
.ly-qr canvas, .ly-qr img {
  display: block; max-width: 100%; height: auto;
  padding: 10px; border-radius: var(--radius); background: #fff;
}
.copybox { display: flex; gap: 7px; margin-bottom: 9px; }

/* ---------- bảng chọn tiếng Thỏ nói ----------
   Cùng một luật với khay ☰: MỖI DÒNG CÓ CHỮ, và dòng đang dùng thì nói ra là nó
   đang dùng (dấu ✓ + nền sáng) chứ không bắt người ta đoán bằng màu. */
/* Mười lăm thứ tiếng xếp MỘT cột là hộp cài đặt dài gần 600px, phải cuộn mới thấy hết
   — mà bảng này dựng ra để nhìn một phát thấy tiếng mình đang học nằm đâu. Nên cho nó
   tự gãy hai cột khi lòng hộp đủ rộng, còn máy hẹp thì rơi về một cột như cũ.
   VẪN LÀ DÒNG (cờ · tên · ✓), không phải ô vuông: lưới ô vuông là của bảng chọn CẢNH
   ngay bên dưới, hai thứ bày giống nhau thì mắt lướt qua tưởng một bảng rồi bấm nhầm
   (chủ nhà chốt 21/08/2026). */
.tieng-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(148px, 1fr));
  gap: 2px; margin-bottom: 10px;
}
.tieng-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; text-align: left;
  background: none; border: 0; border-radius: var(--radius); cursor: pointer;
  color: var(--text); font: inherit;
}
.tieng-row:hover, .tieng-row:focus-visible { background: var(--panel-2); outline: none; }
.tieng-row.on { background: var(--panel-2); }
.tieng-co { flex: none; font-size: calc(17px * var(--fs)); line-height: 1; }
.tieng-ten { flex: 1; font: 600 calc(12.5px * var(--fs)) var(--mono); }
/* Dòng chọn CẢNH cần hai tầng chữ: tên cảnh, và ngay dưới là AI ĐÓNG VAI GÌ. Không nói
   rõ vai thì người ta gõ dăm câu mới đoán ra mình đang là ai, mà lúc đó đã lệch mất rồi.
   Cùng luật với khay ☰: mỗi dòng có chữ, dòng phụ nói cho biết chuyện gì đang xảy ra. */
.tieng-hai { flex: 1; display: flex; flex-direction: column; gap: 1px; text-align: left; }
.tieng-hai .tieng-ten { flex: none; }
.tieng-ai { font-size: calc(10.5px * var(--fs)); line-height: 1.4; color: var(--dim); }
.tieng-row.on .tieng-ai { color: var(--dim); }
.tieng-tick { flex: none; color: var(--accent); font-size: calc(13px * var(--fs)); }

/* ---------- CHỦ ĐỀ: lưới ô vuông ----------
   Cố ý KHÔNG giống bảng chọn tiếng ngay trên nó (chủ nhà chốt 21/08/2026). Hai thứ
   chọn nằm sát nhau trong cùng một hộp mà bày cùng một kiểu thì mắt lướt qua tưởng
   một bảng dài rồi bấm nhầm — mà chọn nhầm cảnh là cả buổi tám lệch vai.
   `auto-fill` + `minmax(92px…)`: đo thật trên máy ảo 390px — lòng hộp chỉ còn 304px,
   để `104px` là lưới rơi về **2 cột**, ô phình lên 149px và cả bảng cao 614px, tức là
   phải cuộn mới xem hết 8 cảnh. Mà lưới dựng lên chính là để nhìn MỘT PHÁT thấy hết,
   phải cuộn thì nó lại thành cái danh sách, chỉ khác là danh sách béo hơn. Hạ xuống
   92px thì điện thoại ra 3 cột (ô 97px, vẫn thừa chỗ cho ngón tay) và cả bảng còn
   303px; hộp 460px trên máy tính ra 4 cột, 8 cảnh gọn trong 2 hàng.
   `aspect-ratio: 1` mới đúng chữ "ô vuông" — bỏ nó đi là mấy ô cao thấp so le theo
   tên dài ngắn, nhìn như bảng chữ chứ không ra khối. */
.canh-luoi {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
  gap: 6px; margin-bottom: 8px;
}
.canh-o {
  position: relative; aspect-ratio: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 7px;
  padding: 8px 6px; text-align: center;
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--text); font: inherit; cursor: pointer;
  transition: border-color .15s, background .15s;
}
.canh-o:hover, .canh-o:focus-visible { border-color: var(--accent); background: var(--panel-2); outline: none; }
/* Ô đang chọn: viền sáng + nền sáng + dấu ✓ ở góc. Ba dấu hiệu chứ không chỉ màu —
   cùng luật với khay ☰, đừng bắt ai đoán bằng mỗi màu sắc. */
.canh-o.on { border-color: var(--accent); background: var(--panel-2); }
.canh-bieu { font-size: calc(21px * var(--fs)); line-height: 1; }
.canh-ten {
  font: 600 calc(10.5px * var(--fs))/1.35 var(--mono);
  overflow-wrap: anywhere;
}
.canh-o.on .canh-ten { color: var(--accent); }
/* Dấu ✓ nằm ở GÓC ô: ô vuông thì không có "cuối dòng" nào để đặt nó như bên bảng tiếng. */
.canh-tick {
  position: absolute; top: 5px; right: 7px;
  color: var(--accent); font-size: calc(11px * var(--fs)); line-height: 1;
}
/* KHAY VIÊN cho chủ đề phụ (mẩu chuyện trong một cảnh). Khác khay viên ở màn chào
   đúng một chỗ: dồn về mép TRÁI chứ không canh giữa — trong hộp cài đặt mọi thứ đều
   bắt đầu từ mép trái, canh giữa một mình thì nó lạc lõng. Viên thì dùng chung
   `.tho-canh-chip`, không đẻ thêm một bộ màu thứ hai. */
.man-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }

/* Dòng nói AI ĐÓNG VAI GÌ của cảnh ĐANG CHỌN. Không nhét nổi vào ô vuông (bóp còn 8px
   là không ai đọc), mà bỏ hẳn thì người ta gõ dăm câu mới đoán ra mình đang là ai. */
.canh-vai {
  margin: 0 0 10px; padding-left: 2px;
  color: var(--dim); font-size: calc(10.5px * var(--fs)); line-height: 1.5;
}
.copybox input {
  flex: 1; min-width: 0; padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--bg); color: var(--text); font: calc(12px * var(--fs)) var(--mono); outline: none;
}
.copybox input:focus { border-color: var(--accent); }
/* Một dòng có ô tick trong hộp thoại (hiện chỉ có "bỏ luôn câu thần chú cũ"). Cả
   dòng là <label> nên chạm vào chữ cũng tick được — ô tick trần bé xíu, ngón tay
   trên điện thoại chạm mười lần trượt ba. */
.check-row {
  display: flex; align-items: center; gap: 9px; cursor: pointer;
  margin: 2px 0 12px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: var(--radius);
  color: var(--dim); font-size: calc(12px * var(--fs)); line-height: 1.5;
}
.check-row input { width: 16px; height: 16px; flex: none; accent-color: var(--accent); }
/* Mã QR của vé. Khung LUÔN trắng kể cả khi app đang ở giao diện tối: máy quét
   cần đúng độ tương phản đen-trên-trắng, đảo màu là nhiều máy chịu thua. */
.qr-box {
  position: relative; display: flex; justify-content: center;
  padding: 10px; margin-bottom: 9px;
  background: #fff; border-radius: var(--radius);
  min-height: 132px;                     /* chỗ cho mã trước khi vẽ xong, khỏi giật bố cục */
}
/* Tấm che "úp sấp" (.qr-cover) đã xoá 21/08/2026 cùng nút 🎫 "chạm để giở vé": mã QR giờ
   nằm trong một ô gấp <details>, nên chính cú chạm mở ô ấy LÀ cú chạm chủ động. Giữ cả hai
   là bắt người ta chạm hai lần cho cùng một việc.
   PHẢI xoá luôn hai dòng gác nó — `.qr-box:not(.shown) canvas { visibility: hidden }` —
   không thì canvas vẽ xong vẫn tàng hình vĩnh viễn, vì không còn ai gắn class `.shown` nữa.
   (Đúng con lỗi chủ nhà báo: "mã QR lối vào hang k hiển thị".) */
.row-2 { display: flex; gap: 7px; }
.row-2 .btn { flex: 1; }
.qr-box canvas {
  display: block; max-width: 100%; height: auto;
  image-rendering: pixelated;      /* co lại vẫn phải sắc cạnh, mờ là quét hụt */
}
.qr-note { margin: 0 0 9px; text-align: center; color: var(--dim); font-size: calc(11.5px * var(--fs)); }

.or { display: flex; align-items: center; gap: 10px; margin: 15px 0 11px; color: var(--dimmer); font-size: calc(10.5px * var(--fs)); }
.or::before, .or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.err { color: var(--danger); font-size: calc(12px * var(--fs)); margin: 4px 0 0; }
.facts { margin: 0 0 8px; padding-left: 16px; color: var(--dim); font-size: calc(12px * var(--fs)); line-height: 1.75; }
.facts li { margin-bottom: 9px; }
.facts b { color: var(--text); font-weight: 600; }
.stat-row { display: flex; gap: 8px; margin-bottom: 13px; }
.stat { flex: 1; background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius); padding: 9px; text-align: center; }
.stat b { display: block; font-size: calc(17px * var(--fs)); color: var(--accent); }
.stat span { font-size: calc(10px * var(--fs)); color: var(--dim); }

#diagOut {
  background: var(--bg); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px; font: calc(11px * var(--fs))/1.6 var(--mono); color: var(--text);
  white-space: pre-wrap; word-break: break-all; max-height: 46vh; overflow: auto; margin: 0 0 10px;
}

/* Thông báo trượt: neo GÓC TRÊN BÊN PHẢI. Trước đây nó nằm giữa đáy màn hình —
   đúng chỗ ô gõ tin nhắn, nên mỗi lần báo là che mất chữ đang gõ (trên điện thoại
   thì che cả nút gửi). Trên cùng bên phải thì không đè lên thứ gì người ta đang dùng. */
.toast {
  position: fixed; z-index: 100;
  top: calc(14px + env(safe-area-inset-top));
  right: calc(14px + env(safe-area-inset-right));
  background: var(--panel-2); color: var(--text); border: 1px solid var(--line);
  padding: 9px 15px; border-radius: var(--radius); font-size: calc(12px * var(--fs));
  max-width: min(360px, calc(100vw - 28px)); text-align: left; line-height: 1.6;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .18);   /* nổi lên trên nội dung phía sau */
  animation: toast-in .18s ease-out;
}
@keyframes toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
/* Ai bật "giảm chuyển động" thì hiện thẳng, đừng trượt. */
@media (prefers-reduced-motion: reduce) { .toast { animation: none; } }

/* ---------- điện thoại ---------- */
/* iOS Safari tự phóng to khi focus ô nhập < 16px — lối zoom cuối cùng, và là lối
   duy nhất không chặn được bằng sự kiện (xem killZoom trong app.js). Ép 16px trên
   thiết bị cảm ứng là hết đường. */
@media (pointer: coarse) {
  /* Thêm ô nhập mới thì PHẢI thêm vào đây, không thì đúng ô đó làm trang nhảy zoom. */
  #lockPass, .composer textarea, .copybox input, .name-input,
  .ticket-panel input { font-size: 16px; }
}

/* MỘT CỘT: điện thoại VÀ máy tính bảng. Mốc đẩy từ 720 lên 1024 (17/08/2026) — tablet
   trước đây rơi vào bố cục hai cột của máy tính, thành ra không có cái màn danh sách
   với cửa sổ trổ ra trời, băng chữ hướng dẫn và cảnh đua; chủ nhà muốn tablet thấy
   đúng những thứ điện thoại thấy. Đổi mốc ở đây thì đổi luôn narrowScreen trong
   app.js — hai chỗ phải cùng một con số, lệch là trò đua ngồi nhầm màn. */
@media (max-width: 1024px) {
  .app { grid-template-columns: 1fr; }
  .sidebar { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; padding: 12px; gap: 10px; }
  /* Hàng đầu màn danh sách:  (?) tên mình ·········· (H) Trò chuyện  +
     Tên mình giãn ra chiếm hết khoảng giữa, đẩy cửa vào phòng chat và nút + về sát
     mép phải. Ba thứ bên phải đi liền một cụm nên đọc ra ngay là một việc. */
  /* HAI CÁI VỎ TAN RA (display: contents) — .side-head và .me-text thôi làm hộp,
     mấy đứa bên trong chúng xếp thẳng hàng với cửa sổ và cái khay. Phải thế mới
     ĐỔI CHỖ được hai thứ này (chủ nhà chốt 20/08/2026):
         ☰ lên góc trái trên cùng   ·   tên hang xuống dưới cửa sổ
     Hai thứ đó nằm ở hai tầng lồng nhau khác nhau, mà `order` thì chỉ xếp được
     anh em ruột — gỡ vỏ ra thì cả đám mới thành anh em xếp chung một mạch.
     Trên MÁY TÍNH hai cái vỏ đó vẫn là hộp thật (tấm tên hiện khi rê chuột dựa
     hẳn vào .me-text): đừng bao giờ đem display: contents ra ngoài khối @media này.
     Thứ tự chạy một mạch 1→11, năm đứa 7·8·9·10·11 nằm ở cuối khối: cửa sổ 7, bàn tiệc
     8, tên mình 9, tên hang 10, băng chữ 11. Chèn thêm gì thì đánh số lại cho liền.
     BÀN TIỆC LEO LÊN NGAY DƯỚI CỬA SỔ (chủ nhà chốt 21/08/2026: *"nên mang tên hang và
     băng chữ xuống dưới khung của bữa tiệc, để bữa tiệc trông gần với cửa hang hơn"*):
     cửa sổ trổ ra trời và bàn tiệc trong hang là MỘT cảnh, chen hai dòng chữ vào giữa
     là cắt nó làm đôi. Chữ nghĩa xuống dưới, chỗ nào cũng đọc được. */
  .side-head, .me-text { display: contents; }
  /* Lối tắt là của CỘT DỌC. Ở đây thanh bên đã nằm ngang và chật cứng (☰ · tên ·
     hai cửa vào phòng), nhét thêm sáu nút vào là vỡ hàng — mấy việc ấy vẫn còn
     nguyên trong khay ☰, nơi mỗi dòng có chữ và có cả dòng trạng thái. */
  .side-quick { display: none; }
  /* CỠ CHỮ VÀ CỠ NÚT CỦA HÀNG ĐẦU — chủ nhà chốt 21/08/2026: *"thanh header trên
     mobile phải to lên, chữ và icon đều nhau. hiện tại nó nhỏ quá"*.
     Cột dọc trên máy tính thì mấy thứ này chỉ là mấy cái chấm nhỏ cạnh nhau, bé là
     đúng; còn ở đây chúng là TOÀN BỘ hàng điều hướng của cả app trên một cái màn hình
     cầm tay, mà lại đang mang cỡ của mấy cái chấm ấy. Nên hàng này khai lại cỡ riêng:
     Cỡ gốc: ba cái tròn/vuông đều 40px, hai mẩu chữ đều 15px. Hai lần chỉnh sau đó
     (cùng ngày) đã đổi luật ấy có chủ ý — xem hai khối ngay dưới: vòng cửa vào phòng nhỏ
     lại còn 36px, và vòng avatar của mình bỏ hẳn. */
  /* `margin-right: auto` GIỜ NẰM Ở .side-foot (cái bọc của nút ☰). Nó vốn ở avatar của
     mình — avatar bỏ rồi mà không dời cái này đi thì cụm (hang)(🐇) Trò chuyện tụt về
     giữa màn, không còn bám mép phải.
     ĐẶT Ở BỌC, KHÔNG ĐẶT Ở NÚT: hàng đầu này là flex, mà .side-head/.me-text mới được
     `display: contents` để con chúng nổi lên thành flex item — .side-foot thì KHÔNG, nên
     chính nó mới là flex item, còn .menu-btn chỉ là con nằm bên trong. Đặt margin lên
     .menu-btn thì không ai nghe cả (đo thật 21/08/2026). */
  .side-foot { margin-right: auto; }
  /* Tên MÌNH giữ nguyên chỗ cũ cạnh avatar, và giãn ra chiếm hết khoảng giữa để
     đẩy cụm (H) Trò chuyện + về sát mép phải — đúng việc .me-text làm trước đây. */
  /* TÊN MÌNH XUỐNG DƯỚI, đi cùng cụm chữ với tên hang (order 9·10·11).
     Đo thật ở 390px sau khi hàng đầu to lên: sáu món cố định đã ăn 383px, ô tên chỉ
     còn **6px** — tức là mất hẳn. Ép mọi thứ nhỏ lại thì lại đúng cái chủ nhà vừa bảo
     là nhỏ quá. Nên ô tên rời hàng đầu: hàng đầu thuần ĐIỀU HƯỚNG (☰ · mình · cửa vào
     phòng · +), còn tên mình là chữ, xuống nằm với chữ.
     `margin-right: auto` ở avatar thay đúng việc mà ô tên vừa làm — hút hết khoảng
     giữa để đẩy cụm (H) Trò chuyện + về sát mép phải. */
  #myName {
    order: 9; width: 100%; flex: none; text-align: center;
    font: 600 calc(14px * var(--fs)) var(--mono);
  }
  /* Chữ "Trò chuyện" đứng TRƯỚC hai cái vòng (chủ nhà chốt 21/08/2026: *"chữ trò chuyện
     nên nằm bên trái của H và AI"*) — nó là cái NHÃN của cụm, mà nhãn thì đọc trước rồi
     mới tới thứ nó gọi tên. Đứng sau thì mắt gặp hai cái vòng không tên trước, đoán xong
     mới đọc được chữ giải thích. Đổi bằng `order`, không đụng vào HTML. */
  .side-list { order: 5; flex-direction: row; flex-wrap: nowrap; width: auto; padding: 0; gap: 8px; }
  /* CỬA VÀO PHÒNG CHAT — hai cái vòng (hang) và (🐇).
     ĐẢO lại luật cũ ở đây, cố ý: trước ghi "to bằng avatar của mình, không nhỏ hơn" (hồi
     đó chúng đang bị làm mờ nên trông chìm). Nay chủ nhà chốt ngược (21/08/2026: *"cho
     vòng tròn thỏ ai và hang nhỏ lại 1 chút so với avatar rùa cute"*).
     Cái vòng đem ra so ấy sau đó bỏ luôn — nhưng 36px thì GIỮ: hàng đầu còn ☰ vuông và
     hai vòng tròn, để cả ba cùng 40px thì thành một dãy ô đều tăm tắp, đọc ra là một
     thanh công cụ chứ không phải hai cánh cửa.

     Vòng nhỏ lại 36px nhưng CÁI NÚT vẫn 40px: bóp ô bấm theo là mất tầm ngón tay, mà đây
     đang là thứ đáng bấm nhất màn hình. Nhỏ đi phần NHÌN, giữ nguyên phần CHẠM. */
  .side-list .peer { width: 40px; height: 40px; display: grid; place-items: center; }
  .side-list .avatar.sm { width: 36px; height: 36px; font-size: calc(17px * var(--fs)); }
  /* Chữ "Trò chuyện" đi liền cái vòng, cả cụm là MỘT cái nút đọc bằng mắt. (Dấu + đã bỏ.)
     Để `--dim`/`--dimmer` như cột dọc thì cụm ấy chìm xuống dưới cả chữ trang trí
     trong hang — mà nó là lối đi chính (chủ nhà chốt 21/08/2026: "cần có màu sáng sủa
     hơn, dấu cộng cũng vậy, chữ trò chuyện cũng vậy"). */
  /* Là <button> nên phải gỡ hết đồ trình duyệt tự sơn, để nó vẫn đọc ra là một cái NHÃN
     chứ không thành cái nút chữ nhật thứ ba trên hàng. */
  .side-tag {
    order: 4; display: block; font-size: calc(15px * var(--fs)); color: var(--text);
    white-space: nowrap;
    -webkit-appearance: none; appearance: none;
    background: none; border: 0; padding: 0; font-family: inherit; cursor: pointer;
  }
  /* THỎ AI RA SÁT MÉP PHẢI (chủ nhà chốt 21/08/2026: *"tôi muốn chat AI icon nằm ngay bên
     phải để dễ dàng bấm tới"*). `margin-left: auto` hút hết khoảng trống còn lại trong
     hàng, đẩy nó ra tận mép — vừa dễ với ngón cái, vừa tách khỏi cụm "Trò chuyện (H)" nên
     mắt đọc ra hai đích khác nhau: bên trái là hang, ngoài cùng bên phải là Thỏ.
     Chỗ đứng CỐ ĐỊNH, không đổi theo việc hôm nay nói với ai nhiều hơn — ngón tay nhớ
     đường thì đường phải đứng yên (xem thứ tự cố định trong renderPeers). */
  .side-list { flex: 1; justify-content: flex-start; }
  .side-list .peer.tho { margin-left: auto; }
  /* #btnInvite KHÔNG còn nằm trên hàng đầu nữa — nó đã dời vào cụm tên hang trong bàn
     tiệc (xem .hang-tag / .hang-add). Luật cũ ở đây khai 40px và chọn bằng #id, nên nếu
     để lại thì nó THẮNG cỡ 26px của chỗ mới và dấu + phình to đè lên mâm cỗ. */
  /* ☰ và + là HAI ĐẦU của cùng một hàng: cùng cỡ khung, cùng cỡ hình, cùng độ đậm nét,
     cùng màu. Rời nhau một thông số thôi là mắt bắt được ngay. */
  #btnMenu { width: 40px; height: 40px; color: var(--text); }
  /* Chỉ còn ☰: dấu + đã rời hàng đầu, và svg của nó khai riêng ở .hang-add. */
  #btnMenu svg { width: 24px; height: 24px; stroke-width: 2; }
  /* TÊN HANG + chấm đèn nối: xuống DƯỚI BÀN TIỆC. Nó là thứ nói "mình đang ngồi trong
     hang nào" — một dòng chữ, không phải một mảng cảnh; đứng chen giữa cửa sổ và bàn
     tiệc là cắt đôi cái cảnh, mà đứng dưới cùng thì vẫn liếc một cái là thấy. */
  /* #myIdChip KHÔNG còn nằm ở hàng chữ dưới cảnh nữa — nó đã dời vào trong bàn tiệc,
     treo trên bếp lửa (xem .id-chip.in-tiec). Luật cũ ở đây (`width: 100%`, order 10)
     nếu để lại thì THẮNG luật mới vì nó chọn bằng #id, và viên chữ bị kéo dài gần hết
     bề ngang cảnh — đúng chỗ chủ nhà bảo "thu nền hang bưởi trong lại nha". */
  .peer-body { display: none; }
  /* Nhiều hang thì cụm bên phải hết chỗ — cho nó rớt xuống hàng dưới, đừng ép
     tên mình co lại tới mức chỉ còn một chữ. */
  .side-list:has(.peer + .peer) { flex-wrap: wrap; }
  .side-foot { flex-direction: row; }
  /* Khay trổ xuống từ mép trái, y như cột dọc — giờ ☰ đã về góc trái nên hai bố
     cục dùng chung một luật, không còn cái nào canh giữa nữa. */
  .menu-tray { min-width: min(300px, calc(100vw - 28px)); }
  /* Băng chữ trong hang. Cùng một luật với băng ngoài cửa (.lock-say): chữ trần không
     hộp, chiều cao ghim ở 2 dòng để câu dài câu ngắn thay nhau mà hàng nút bên trên
     và đống của cải bên dưới đứng im.
     order: 9 — BẮT BUỘC, dù trong HTML nó đã đứng sau .side-foot rồi: cả thanh bên
     giờ xếp bằng `order` (xem bảng số ở đầu khối), mà đứa không có số thì coi như 0
     và nhảy phóc lên đầu màn. Bỏ dòng này đi là băng chữ leo lên trên cả cửa sổ. */
  .hang-say {
    display: grid; place-items: center; order: 11;
    width: 100%; margin: 0; padding: 0 12px;
    height: calc(12.5px * var(--fs) * 1.7 * 2);
    /* CHẶN CỨNG: câu nào lỡ dài quá hai dòng thì bị cắt, KHÔNG được lòi ra ngoài ô.
       Đã dính: câu NPH tràn xuống dòng thứ ba trên máy 320px, thò ra khỏi khung và
       đè lên chữ bên dưới, nhìn như hai câu chồng lên nhau (17/08/2026). Cắt cụt thì
       còn thấy ngay mà sửa; đè chữ thì tưởng lỗi hiệu ứng, mò mãi không ra. */
    overflow: hidden;
    color: var(--dim); font: calc(12.5px * var(--fs))/1.7 var(--mono);
    letter-spacing: .02em; text-align: center; white-space: pre-line;
    transition: opacity .26s ease;
    user-select: none; -webkit-user-select: none;
  }
  .hang-say.mo { opacity: 0; }
  .hang-say .nhan { color: var(--accent); font-weight: 600; }
  /* Trò đua chuyển sang màn danh sách (màn có nút + và ◐) — xem placePlay() trong
     app.js. Ở đây thanh bên xếp ngang nên phải tự chiếm trọn một hàng, không thì
     nó chen vào giữa mấy cái nút. */
  /* Cửa sổ trổ ra trời — xem ghi chú ở index.html. Nó ăn hết khoảng trống giữa
     hàng đầu và cảnh đua, đúng cái mảng nâu trống trước đây.
     Miệng cửa sổ bo vòm ở trên, vuông ở dưới: một cái lỗ đào trong vách đất, không
     phải một khung ảnh. Bóng đổ VÀO TRONG dọc mép cho ra bề dày của vách. */
  .cave-view {
    display: block; order: 7; position: relative; overflow: hidden;
    width: 100%; flex: 1 1 auto; min-height: 132px; max-height: 300px;
    border-radius: 46% 46% 14px 14px / 36% 36% 14px 14px;
    background: linear-gradient(#8fc9ee 0%, #b6dcf3 30%, #d9ecf7 56%, #eef3f2 76%, #f8f1de 100%);
    box-shadow: inset 0 0 0 5px var(--panel), inset 0 6px 26px 8px rgba(20, 10, 2, .5),
                0 0 0 1px var(--line), 0 6px 18px rgba(0, 0, 0, .25);
  }
  /* Mây trôi trong khung cửa sổ. .cloud vốn dịch ngang theo bề rộng cả trang, ở
     đây khung hẹp hơn nên nó chỉ ghé qua rồi đi — đúng kiểu ngồi trong nhìn ra. */
  .cave-view .sky { position: absolute; inset: 0; z-index: 0; }
  .cave-view .cloud { opacity: .78; }
  .cave-view .cloud:nth-child(1) { top: 14%; font-size: 26px; }
  .cave-view .cloud:nth-child(2) { top: 30%; font-size: 18px; }
  .cave-view .cloud:nth-child(3) { top: 6%;  font-size: 21px; }
  /* Rặng cây đứng trên mặt đất, ngay mép dưới khung — nhìn từ dưới hang lên thì
     chỉ thấy được chừng đó. */
  .cave-view .treeline {
    position: absolute; left: 0; right: 0; bottom: 11px;
    margin: 0; padding: 0 10px; z-index: 1;
  }
  /* Sói đi tới đi lui ngoài cửa, quay đầu mỗi lần tới mép. Chậm (19 giây một vòng)
     và hơi tối màu: nó là cái bóng rình ở rìa cảnh, không phải nhân vật chính.
     Đi trên mặt đất nên bottom trùng mép trên vạt đất. */
  .cave-view .wolf {
    position: absolute; left: 14px; bottom: 9px; z-index: 1;
    font-size: 31px; line-height: 1; transform-origin: 50% 100%;
    --far: calc(min(100vw, 768px) - 112px);   /* mép phải của cửa sổ, chừa chỗ cho thân sói */
    /* Dìm gần thành cái BÓNG: để nguyên màu thì đó là con chó nhà, dìm xuống mới
       ra con sói xám đi ngoài rặng cây. KHÔNG quầng đỏ — bóng đen tự nó đã đủ, thêm
       màu đỏ vào là ra phim kinh dị chứ không còn là cảnh trong truyện ngụ ngôn. */
    filter: brightness(.42) contrast(1.45) saturate(.35)
            drop-shadow(0 2px 3px rgba(0, 0, 0, .45));
    animation: prowl 19s ease-in-out infinite;
  }
  /* 🐕 nguyên con vẽ sẵn quay mặt sang PHẢI, y như 🐢 🐇 ở cảnh đua — nên đi sang
     phải thì để nguyên, đi sang trái mới lật. (Cái đầu 🐺 thì ngược lại, quay sẵn
     sang trái: đặt nhầm chiều là con sói đi giật lùi.)
     Tới mỗi đầu thì KHỰNG LẠI, chồm lên một cái rồi mới quay đầu — cái chồm đó mới
     ra vẻ hung dữ; đi tới đi lui đều đều thì chỉ như con chó nhà đi dạo. */
  @keyframes prowl {
    0%,  2% { transform: translateX(0)          scale(1, 1); }
    40%     { transform: translateX(var(--far)) scale(1, 1); }
    44%     { transform: translateX(var(--far)) scale(1.32, 1.32) rotate(-7deg); }
    47%     { transform: translateX(var(--far)) scale(1, 1); }
    50%     { transform: translateX(var(--far)) scale(-1, 1); }
    88%     { transform: translateX(0)          scale(-1, 1); }
    92%     { transform: translateX(0)          scale(-1.32, 1.32) rotate(7deg); }
    95%     { transform: translateX(0)          scale(-1, 1); }
    100%    { transform: translateX(0)          scale(1, 1); }
  }
  /* Tắt hiệu ứng thì sói đứng yên một chỗ, vẫn còn đó. */
  @media (prefers-reduced-motion: reduce) { .cave-view .wolf { animation: none; } }
  /* Mặt đất: cùng màu cam đất với vạt đất ở màn khoá, để hai màn ăn vào nhau. */
  .cave-view .cave-ground {
    position: absolute; left: 0; right: 0; bottom: 0; height: 11px; z-index: 2;
    background: linear-gradient(#d99a52, #b9793b);
  }
  /* Kho báu nằm dưới gầm bàn tiệc (xem .feast-scene .hoard) — chỗ này bỏ trống. */
  /* align-content: flex-start để mấy hàng dồn hết lên trên thay vì bị giãn đều ra
     khắp chiều cao — có dồn lên thì đáy hang mới trống ra cho đống của. */
  .sidebar { position: relative; align-content: flex-start; }
  /* ☰ đứng đầu hàng trên cùng, sát mép trái: width: auto để nó chỉ chiếm đúng bề
     ngang cái nút, không thì nó ôm cả hàng và đẩy avatar xuống dòng dưới. */
  /* `margin-right: auto` phải nằm ở ĐÂY, không phải ở khối trên: dòng này chọn bằng
     `.sidebar .side-foot` (đặc hiệu hơn) và trước đây khai `margin: 0`, quét sạch luôn
     cái margin vừa đặt. Việc của nó: hút hết khoảng trống ở giữa để cụm (hang)(🐇)
     Trò chuyện bám mép phải — việc mà avatar của mình làm trước khi bị bỏ. */
  .sidebar .side-foot { order: 1; width: auto; margin: 0 auto 0 0; }
  .sidebar .play-box { width: 100%; padding: 4px 0 10px; order: 8; }
  .chat { display: none; }
  .app.viewing .sidebar { display: none; }
  .app.viewing .chat { display: flex; }
  .back { display: grid; }
}


/* ── CỬA HANG là cái NÚT ─────────────────────────────────────────────────────────────
   Trước đây .keyhole là <label> ôm ô mật khẩu; giờ là <button type=submit>. Trình duyệt
   tự sơn nền/viền/canh chữ cho button, phải gỡ hết để nó về đúng cái cửa đang vẽ sẵn. */
.lock .keyhole {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
/* Chạm vào là vào — cho ngón tay thấy nó bấm được. */
.lock .keyhole:active .mark { transform: scale(.96); }

/* ── MÃ QR GẤP LẠI ───────────────────────────────────────────────────────────────────
   Cái lối mới là đầu ra chính của hộp vé; mã QR lùi xuống sau một dòng chạm mới mở
   (chủ nhà chốt 21/08/2026: "mã QR thì ít nổi bật hơn"). Nên nó phải trông như một dòng
   chữ phụ, không phải một cái nút to tranh chỗ với hai nút thật ở trên. */
.qr-fold { margin-top: 12px; }
.qr-fold > summary {
  cursor: pointer;
  font-size: 13px;
  opacity: .72;
  padding: 6px 2px;
  list-style: none;
}
.qr-fold > summary::-webkit-details-marker { display: none; }
.qr-fold > summary::before { content: '› '; }
.qr-fold[open] > summary::before { content: '⌄ '; }
.qr-fold > summary:hover { opacity: 1; }

/* Ô CHỮ chứa cái lối. Trước đây #shareLink luôn hidden (vé chỉ đi qua nút Chép/Gửi);
   giờ nó là thứ đáng nhìn nhất hộp, nên phải đọc được và chọn được — nhưng vẫn một dòng,
   không xuống hàng thành một khối chữ dài choán hết hộp. */
#shareLink {
  width: 100%;
  margin: 10px 0 8px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--line, rgba(255, 255, 255, .18));
  background: rgba(0, 0, 0, .22);
  color: inherit;
  font: 12px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  text-overflow: ellipsis;
}

/* ── HAI CỬA Ở MÀN HANG TRỐNG ────────────────────────────────────────────────────────
   Hang chưa có ai thì chỗ này là màn hình quan trọng nhất app: người mới vừa vào, chưa có
   bạn, và cái duy nhất dùng được ngay là con Thỏ. Nút Thỏ đứng trước và sáng màu; nút lấy
   lối đứng sau, viền mỏng — không phải vì nó kém quan trọng, mà vì nó cần một người thứ
   hai mới có tác dụng, còn Thỏ thì trả lời ngay. */
.empty-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
  margin-top: 18px;
}
.empty-cta .btn { min-width: 168px; }
.empty-note-small {
  margin-top: 10px;
  font-size: 12px;
  opacity: .6;
  text-align: center;
}

/* ---------- HAI LỐI RA MẠNG XÃ HỘI ----------
   Chỉ là thẻ <a> trần, cả ở khay ☰ lẫn ở chân màn khoá: KHÔNG nạp SDK của Facebook hay
   TikTok, không cookie của họ, không phải khai thêm tên miền nào vào CSP. Nhúng "nút
   Like" hay khung video của hai hãng đó là mời thêm hai người lạ vào đứng trong sân —
   mà cả cái app này dựng lên để không ai đứng đó cả. Đổi ý thì chỉ cần sửa href. */
.tray-row.social { text-decoration: none; }
.tray-row.social .tray-ico { display: grid; place-items: center; }
.tray-row.social svg { width: calc(15px * var(--fs)); height: calc(15px * var(--fs)); fill: currentColor; }

/* Ở CHÂN MÀN KHOÁ. Cố ý nhỏ và nhạt — cửa hang cần yên tĩnh, đây là lối phụ chứ không
   phải lời mời. Màu chốt cứng chứ không lấy theo theme: màn khoá vốn không theo theme
   của app (xem ghi chú ở .lock). */
.lock-social {
  /* flex-basis 100%: .lock-extras là một hàng flex có wrap, không có dòng này thì hai
     cái nút chen vào NGANG HÀNG với "tôi có vé 🎫", đẩy nó lệch hẳn sang trái. */
  flex-basis: 100%;
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 14px;
}
.lock-social a {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 13px; border-radius: 999px;
  background: rgba(255, 255, 255, .42); border: 1px solid rgba(18, 63, 58, .11);
  color: #2c5c56; text-decoration: none;
  font: 600 calc(9.5px * var(--fs)) var(--mono); letter-spacing: .02em;
  transition: background .14s ease, color .14s ease;
}
.lock-social a:hover { background: #fff; color: #123f3a; }
.lock-social svg { width: 14px; height: 14px; fill: currentColor; }
