/* PoolCoach FE — 23/07/2026, polish mobile-first 23/07 tối. Không framework, không build step. */ :root { --bg: #14181d; --card: #1e242b; --card-b: #2d3640; --text: #e9edf1; --muted: #93a0ac; --green: #0da357; --green-d: #0b8a4b; --gold: #ffd700; --zone: #38c8f5; /* vùng đích cue_zone của drill (F3) — KHÁC gold lỗ đích */ } * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; font-family: system-ui, "Segoe UI", Roboto, sans-serif; background: radial-gradient(1200px 600px at 20% -10%, #1d242c 0%, var(--bg) 55%); color: var(--text); -webkit-tap-highlight-color: transparent; } header { padding: 16px 18px 6px; } header h1 { margin: 0; font-size: 22px; letter-spacing: .3px; } header p { margin: 4px 0 0; color: var(--muted); font-size: 13px; } main { display: flex; gap: 20px; padding: 12px 18px 26px; align-items: flex-start; flex-wrap: wrap; justify-content: center; } /* ------------------------------------------------------------- bàn */ #table-wrap { position: relative; flex: 0 0 auto; } #table { display: block; border-radius: 16px; box-shadow: 0 10px 34px rgba(0, 0, 0, .5); cursor: grab; touch-action: none; /* kéo bi không cuộn trang trên mobile */ user-select: none; } #table.dragging { cursor: grabbing; } #tooltip { position: absolute; pointer-events: none; background: rgba(0, 0, 0, .85); color: #fff; font: 12px/1.4 ui-monospace, Consolas, monospace; padding: 4px 8px; border-radius: 6px; white-space: nowrap; z-index: 5; } /* ----------------------------------------------------------- panel */ #panel { flex: 1 1 320px; min-width: 300px; max-width: 440px; } .banner { background: #453d10; border: 1px solid #8a7418; color: #ffe58f; padding: 10px 13px; border-radius: 10px; font-size: 13px; margin-bottom: 12px; } .banner.error { background: #4a1d1d; border-color: #a33; color: #ffb3b3; } .banner.warn { background: #4a3a10; border-color: #b8901f; color: #ffdf9e; } .banner.win { background: #12321f; border-color: var(--green); color: #9df2c0; font-size: 15px; font-weight: 650; text-align: center; } .controls, #describe, .alt { background: var(--card); border: 1px solid var(--card-b); border-radius: 14px; } .controls { padding: 14px 15px; } .controls .row { margin-bottom: 12px; display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } .controls .lbl { color: var(--muted); font-size: 12px; min-width: 48px; } /* engine: 2 pill bấm được */ .radio { font-size: 14px; cursor: pointer; } .radio input { accent-color: var(--green); } .radio small { color: var(--muted); } .pill { position: relative; padding: 8px 13px; border: 1px solid var(--card-b); border-radius: 999px; background: #171c22; transition: border-color .15s, background .15s; } .pill input { position: absolute; opacity: 0; pointer-events: none; } .pill:has(input:checked) { border-color: var(--green); background: #12281c; } .pill:has(input:disabled) { opacity: .45; cursor: not-allowed; } /* .an-cta = nút CTA không-recommend: TRÔNG như .btn-go nhưng là class riêng — app.js bind recommend cho mọi .btn-go và syncButtons ép label/ disabled của chúng; nút nào mượn class đó là dính cả hai (bug Danh bắt được ở smoke BG25 — đúng bài học .btn-play 04/08). Dùng cho 2 nút Analyzer (BG25) + #scan-go của overlay Quét thế bi (BG26). */ .btn-go, .an-cta { width: 100%; padding: 13px; font-size: 15px; font-weight: 650; color: #fff; background: linear-gradient(180deg, var(--green), var(--green-d)); border: 0; border-radius: 12px; cursor: pointer; box-shadow: 0 4px 14px rgba(13, 163, 87, .25); transition: filter .15s, transform .05s; } .btn-go:hover:not(:disabled), .an-cta:hover:not(:disabled) { filter: brightness(1.08); } .btn-go:active:not(:disabled), .an-cta:active:not(:disabled) { transform: translateY(1px); } .btn-go:disabled, .an-cta:disabled { background: #333c45; color: #8a949d; cursor: not-allowed; box-shadow: none; } /* ------------------------------------------- chip bi 1-9 + badge mục tiêu */ #ball-chips { gap: 6px; } .chip { width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, .55); background: #2a323b; color: var(--muted); font-size: 14px; font-weight: 700; cursor: pointer; padding: 0; transition: transform .08s, opacity .15s; } .chip:hover:not(:disabled) { transform: translateY(-1px); } .chip.off { background: #2a323b !important; color: #6d7883 !important; border-color: #3a434d; text-decoration: line-through; } .chip:disabled { opacity: .5; cursor: not-allowed; } /* badge phiên bàn (QR — bàn giao 12). Class riêng, không mượn class có event binding toàn cục (bug .btn-play 04/08). Nằm trong header h1 nên cần font-size/vertical-align riêng để không phóng theo cỡ chữ tiêu đề. */ .session-badge { display: inline-block; background: #12281c; border: 1px solid var(--green); color: #9df2c0; border-radius: 999px; padding: 3px 10px; font-size: 12.5px; font-weight: 650; vertical-align: 3px; white-space: nowrap; } .target-badge { background: #3a3410; border: 1px solid var(--gold); color: var(--gold); border-radius: 999px; padding: 4px 11px; font-size: 12.5px; font-weight: 650; white-space: nowrap; } /* ---------------------------------------- nút phụ: Bàn mẫu / Undo / Auto */ .btn-row { display: flex; gap: 8px; margin-bottom: 12px; } .btn-mini { flex: 1; padding: 9px 12px; font-size: 13px; color: var(--text); background: #232a32; border: 1px solid var(--card-b); border-radius: 10px; cursor: pointer; transition: border-color .15s, background .15s; } .btn-mini:hover:not(:disabled) { border-color: #55636f; background: #283039; } .btn-mini:disabled { opacity: .45; cursor: not-allowed; } /* form Ghép TV (bàn giao 15) — class riêng, không mượn class có binding */ .tv-pair { display: flex; gap: 8px; margin-bottom: 10px; } .tv-pair input { flex: 2; min-width: 0; padding: 9px 12px; font-size: 15px; letter-spacing: 3px; color: var(--text); background: #171c22; border: 1px solid var(--card-b); border-radius: 10px; } .tv-pair button { flex: 1; padding: 9px 12px; font-size: 13px; font-weight: 650; color: #fff; background: linear-gradient(180deg, var(--green), var(--green-d)); border: 0; border-radius: 10px; cursor: pointer; } /* .btn-pass = nút "Đạt" của drill: TRÔNG như .btn-play nhưng là class riêng — app.js bind playShot cho mọi .btn-play, drill không được dính binding đó. */ .btn-play, .btn-pass { flex: 2; padding: 11px 14px; font-size: 14px; font-weight: 650; color: #fff; background: linear-gradient(180deg, #1cb35f, #14904c); border: 0; border-radius: 11px; cursor: pointer; box-shadow: 0 4px 14px rgba(28, 179, 95, .22); transition: filter .15s, transform .05s; } .btn-play:hover:not(:disabled), .btn-pass:hover:not(:disabled) { filter: brightness(1.08); } .btn-play:active:not(:disabled), .btn-pass:active:not(:disabled) { transform: translateY(1px); } .btn-play:disabled, .btn-pass:disabled { background: #333c45; color: #8a949d; cursor: not-allowed; box-shadow: none; } .btn-pass { flex: 1; } .play-row { margin-bottom: 10px; } .btn-auto { width: 100%; margin-top: 8px; } /* ---------------------------------------------------------- kết quả */ #result { margin-top: 14px; } /* viz: bi cái + thanh lực */ .viz { display: flex; gap: 18px; align-items: center; background: var(--card); border: 1px solid var(--card-b); border-radius: 14px; padding: 13px 15px; margin-bottom: 10px; } .viz-spin { flex: 0 0 auto; text-align: center; } #spin-ball { width: 88px; height: 88px; display: block; margin: 0 auto; } .spin-dot { transition: cx .25s ease, cy .25s ease; } .viz-cap { color: var(--muted); font-size: 11.5px; margin-top: 5px; max-width: 120px; } .viz-power { flex: 1; min-width: 0; } .viz-lbl { font-size: 13px; color: var(--muted); margin-bottom: 8px; } .viz-lbl b { color: var(--text); font-size: 14px; margin-left: 4px; } .power-bar { display: flex; gap: 4px; } .power-bar .seg { flex: 1; height: 17px; border-radius: 4px; background: #2a323b; transition: background .2s; } .viz-scale { display: flex; justify-content: space-between; color: var(--muted); font-size: 10.5px; margin-top: 5px; } /* Widget "Đánh lại cú" trang chi tiết (A2b phần 4): nhìn Y HỆT #shotviz nhưng class tương tác RIÊNG — .power-bar/.spin-dot bị initPowerBar + updateShotViz của tab gợi ý quét toàn cục (bài học .btn-play). */ #and-spin-ball { width: 88px; height: 88px; display: block; margin: 0 auto; } .and-spin-dot { transition: cx .25s ease, cy .25s ease; } .and-power-bar { display: flex; gap: 4px; } .and-power-bar .seg { flex: 1; height: 17px; border-radius: 4px; background: #2a323b; transition: background .2s; } #and-shotviz { margin-top: 10px; } .and-toggle { display: flex; align-items: center; gap: 8px; margin: 10px 0 2px; font-size: 13px; cursor: pointer; } #describe { background: #12241b; border-color: #1f4d34; padding: 13px 15px; font: 13px/1.6 ui-monospace, Consolas, monospace; white-space: pre-wrap; margin: 0 0 10px; } .alt { padding: 11px 13px; font-size: 13px; margin-bottom: 7px; cursor: pointer; transition: border-color .15s, background .15s; } .alt:hover { border-color: #55636f; } .alt.active { border-color: var(--gold); background: #2a2a20; } /* render lười: cú thay thế đang chờ /api/trajectory. Mờ nhẹ thôi — nút vẫn bấm được, chờ ở đây KHÔNG chặn UI (xem ensureTrajectories trong app.js). */ .alt.pending { opacity: .62; } #searchinfo { color: var(--muted); font-size: 12px; margin-top: 7px; } /* ----------------------------------------------------------- legend */ .legend { margin-top: 14px; color: var(--muted); font-size: 12px; } .legend span { margin-right: 13px; white-space: nowrap; line-height: 2; } .dot { display: inline-block; width: 11px; height: 11px; border-radius: 50%; border: 1px solid #000; vertical-align: -1px; } .ring { display: inline-block; width: 10px; height: 10px; border-radius: 50%; border: 2px solid var(--gold); vertical-align: -1px; } .ring.target { box-shadow: 0 0 5px var(--gold); } /* viền "bi scan chưa chắc" (F2, bàn giao 18) — đứt + cam để KHÔNG lẫn với nhẫn vàng liền của bi mục tiêu/lỗ đích */ .ring.lowconf { border-style: dashed; border-color: #ffb020; } /* ----------------------------------------- tab Bài tập (F3, 04/08/2026) */ .tabs { display: flex; gap: 8px; margin-bottom: 12px; } .tab { flex: 1; padding: 10px; font-size: 14px; font-weight: 650; color: var(--muted); background: var(--card); border: 1px solid var(--card-b); border-radius: 12px; cursor: pointer; transition: border-color .15s, color .15s, background .15s; } .tab:hover { border-color: #55636f; } .tab.active { color: var(--text); border-color: var(--green); background: #12281c; } /* khu "Tiến bộ" phiên bàn (bàn giao 12) — class riêng, không mượn class có event binding toàn cục (bug .btn-play 04/08). */ .drill-progress { background: var(--card); border: 1px solid var(--card-b); border-radius: 14px; padding: 11px 14px; font-size: 13px; margin-bottom: 10px; } .drill-progress > b { font-size: 13.5px; } .drill-progress .progress-row { color: var(--muted); margin-top: 6px; line-height: 1.45; } .drill-item b { font-size: 13.5px; } .drill-item .tag { display: inline-block; margin-left: 6px; padding: 2px 8px; border-radius: 999px; background: #16303a; border: 1px solid #29556a; color: var(--zone); font-size: 11px; } .drill-item .drill-reps { float: right; color: var(--muted); font-size: 12px; } #drill-title { margin: 12px 0 8px; font-size: 16px; } #drill-goal { background: #10242c; border: 1px solid #1f4250; border-radius: 14px; padding: 12px 14px; font-size: 13px; line-height: 1.55; margin-bottom: 10px; } .drill-round { margin: 4px 0 10px; display: flex; align-items: center; gap: 8px; } .drill-round b { font-size: 14px; } .btn-fail { flex: 1; padding: 11px 14px; font-size: 14px; font-weight: 650; color: #fff; background: linear-gradient(180deg, #c04545, #a03434); border: 0; border-radius: 11px; cursor: pointer; box-shadow: 0 4px 14px rgba(192, 69, 69, .22); transition: filter .15s, transform .05s; } .btn-fail:hover:not(:disabled) { filter: brightness(1.08); } .btn-fail:active:not(:disabled) { transform: translateY(1px); } .btn-fail:disabled { background: #333c45; color: #8a949d; cursor: not-allowed; box-shadow: none; } #drill-summary { background: #12321f; border: 1px solid var(--green); color: #9df2c0; border-radius: 10px; padding: 10px 13px; font-size: 14px; font-weight: 650; text-align: center; margin-bottom: 8px; } #drill-retry { width: 100%; margin-bottom: 8px; } .drill-note { color: #ffdf9e; font-size: 12px; margin: 2px 0 10px; } #drill-suggest { margin-top: 4px; } /* ------------------------------------ Scan ảnh (F2 MVP, bàn giao 18) */ /* Mọi class .scan-* là class MỚI — không mượn class có event binding (bài học .btn-play 04/08). Overlay nổi trên tất cả (kể cả sheet mobile z-index 20 và backdrop 15) — chấm góc là việc toàn màn hình. */ .scan-overlay { position: fixed; inset: 0; z-index: 30; background: rgba(0, 0, 0, .72); display: flex; align-items: center; justify-content: center; padding: 12px; } .scan-box { background: var(--card); border: 1px solid var(--card-b); border-radius: 16px; padding: 13px 15px; max-width: min(94vw, 760px); max-height: 94vh; overflow-y: auto; box-shadow: 0 12px 44px rgba(0, 0, 0, .6); } .scan-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; font-size: 15px; } .scan-x { width: 32px; height: 32px; border-radius: 50%; background: #232a32; border: 1px solid var(--card-b); color: var(--text); font-size: 14px; cursor: pointer; } .scan-hint { color: #ffdf9e; font-size: 13px; line-height: 1.5; margin-bottom: 10px; min-height: 2.9em; /* 2 dòng — hint đổi độ dài không làm ảnh nhảy */ } #scan-canvas { display: block; max-width: 100%; max-height: 62vh; /* ảnh dọc mobile không đẩy nút ra khỏi màn hình */ margin: 0 auto; border-radius: 10px; border: 1px solid var(--card-b); cursor: crosshair; touch-action: none; /* chấm góc không cuộn trang trên mobile */ } .scan-actions { margin-top: 12px; margin-bottom: 0; } .scan-go { flex: 2; width: auto; padding: 11px; } /* ---------------------------------- tab Analyzer (bàn giao 24) */ /* Mọi class .an-* là class MỚI (bài học .btn-play); overlay chấm pocket mượn các class STYLE-ONLY .scan-overlay/.scan-box — không class nào trong đó có event binding. */ .an-intro { font-size: 13px; color: var(--muted, #9fb0c0); line-height: 1.5; margin-bottom: 10px; } /* BG31: note chuẩn click mép nose vải (tĩnh, trên canvas chấm góc) */ .an-note { display: flex; align-items: center; gap: 10px; font-size: 12.5px; color: var(--muted, #9fb0c0); line-height: 1.45; margin-bottom: 10px; } .an-note svg { width: 168px; flex-shrink: 0; } #an-canvas { display: block; max-width: 100%; max-height: 62vh; margin: 0 auto; border-radius: 10px; border: 1px solid var(--card-b); cursor: crosshair; touch-action: none; } .an-metrics { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 8px; } .an-metrics td { padding: 5px 6px; border-bottom: 1px solid var(--card-b); } .an-metrics td:last-child { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; } .an-warn { background: #4a3a10; border: 1px solid #b8901f; color: #ffdf9e; border-radius: 8px; padding: 7px 10px; font-size: 12.5px; line-height: 1.45; margin-bottom: 6px; } .an-note { margin-top: 8px; font-size: 12px; color: var(--muted, #9fb0c0); line-height: 1.5; } /* BG28 — bảng 2 cột model↔analytic: header + ô giá trị giữa (cột model) cùng kiểu số với cột cuối; caveat model + dòng bất đồng (thông tin, giọng khác .an-warn cảnh báo) */ .an-metrics th { padding: 5px 6px; border-bottom: 1px solid var(--card-b); text-align: left; font-size: 12px; color: var(--muted, #9fb0c0); } .an-metrics th:not(:first-child) { text-align: right; } .an-metrics td:not(:first-child) { text-align: right; font-variant-numeric: tabular-nums; font-weight: 600; } .an-caveat { margin-top: 8px; background: #16324a; border: 1px solid #2f6da8; color: #bcdcf5; border-radius: 8px; padding: 7px 10px; font-size: 12.5px; line-height: 1.45; } .an-diff { background: #14323e; border: 1px solid #2e7f9e; color: #a9e0f2; border-radius: 8px; padding: 7px 10px; font-size: 12.5px; line-height: 1.45; margin-bottom: 6px; } /* --------------------- Analyzer đa cú (lát A1) — bảng danh sách cú §3.1. Mọi class .anv-* là class MỚI (bài học .btn-play — không mượn class có event binding). Hàng .anv-err = cú không phân tích được: xám + lý do. */ #anv-result { margin-top: 12px; } .anv-progressline { font-size: 13px; font-weight: 600; margin-bottom: 8px; } .anv-tablewrap { overflow-x: auto; } .anv-table { width: 100%; border-collapse: collapse; font-size: 12.5px; white-space: nowrap; } .anv-table th { padding: 5px 6px; border-bottom: 1px solid var(--card-b); text-align: left; font-size: 12px; color: var(--muted, #9fb0c0); } .anv-table td { padding: 5px 6px; border-bottom: 1px solid var(--card-b); vertical-align: middle; font-variant-numeric: tabular-nums; } .anv-thumb { display: block; width: 72px; border-radius: 4px; border: 1px solid var(--card-b); } .anv-ts { font-size: 11.5px; color: var(--muted, #9fb0c0); text-align: center; } .anv-badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 600; } .anv-badge-high { background: #143f28; border: 1px solid #2e9e56; color: #8fe0ae; } .anv-badge-medium { background: #4a3a10; border: 1px solid #b8901f; color: #ffdf9e; } .anv-badge-low { background: #4a1d15; border: 1px solid #b8502f; color: #ffb59e; } .anv-err td { color: var(--muted, #9fb0c0); background: rgba(255, 255, 255, .025); } /* van A2 phần 1: cú "nghi không phải cú đánh" — xám như .anv-err nhưng là class RIÊNG (hàng vẫn done + click được; .anv-err là cú KHÔNG phân tích được). Không mượn class có ngữ nghĩa khác — bài học .btn-play. */ .anv-suspect td { color: var(--muted, #9fb0c0); background: rgba(255, 255, 255, .025); } .anv-reason { font-size: 11.5px; color: var(--muted, #9fb0c0); white-space: normal; max-width: 260px; } .anv-clickable { cursor: pointer; } .anv-clickable:hover td { background: rgba(255, 255, 255, .05); } /* overlay per cú (A2 phần 3): link phát/tải trong ô trạng thái */ .anv-overlay-links a { display: inline-block; margin: 2px 10px 0 0; font-size: 11.5px; color: #7fc4ff; text-decoration: none; } .anv-overlay-links a:hover { text-decoration: underline; } /* -------------------- trang chi tiết cú (lát A2 phần 4, class .and-*) */ #and-detail h3 { margin: 8px 0 4px; } .and-video { width: 100%; max-height: 320px; border-radius: 8px; background: #000; margin: 6px 0; } .and-link { text-decoration: none; display: inline-flex; align-items: center; } #and-replay { margin: 6px 0; } /* ------------------------------------------------------------ toast */ #toast { position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%); background: #4a1d1d; border: 1px solid #a33; color: #ffd6d6; padding: 11px 16px; border-radius: 10px; font-size: 14px; max-width: 92vw; z-index: 10; box-shadow: 0 6px 22px rgba(0, 0, 0, .45); } .hidden { display: none !important; } /* quickbar + bottom sheet: chỉ dùng trên mobile */ #quickbar, #backdrop, .sheet-close { display: none; } /* ----------------------------------------------------------- mobile */ @media (max-width: 760px) { header { padding: 10px 14px 0; text-align: center; } header h1 { font-size: 18px; } header p { display: none; } /* gọn — hướng dẫn đã tự hiểu qua UI */ main { flex-direction: column; align-items: center; gap: 12px; padding: 8px 12px calc(150px + env(safe-area-inset-bottom)); /* chừa quickbar */ } /* panel mặc định ẨN — mở bằng nút "Chi tiết" (bottom sheet) */ #panel { display: none; } body.sheet-open #backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, .55); z-index: 15; } body.sheet-open #panel { display: block; position: fixed; left: 0; right: 0; bottom: 0; z-index: 20; max-width: none; max-height: 78vh; overflow-y: auto; background: var(--bg); border-radius: 18px 18px 0 0; padding: 12px 14px calc(18px + env(safe-area-inset-bottom)); box-shadow: 0 -12px 44px rgba(0, 0, 0, .6); animation: slideup .22s ease; } body.sheet-open .sheet-close { display: flex; align-items: center; justify-content: center; position: sticky; top: 0; margin-left: auto; width: 36px; height: 36px; background: var(--card); border: 1px solid var(--card-b); border-radius: 50%; color: var(--text); font-size: 15px; cursor: pointer; z-index: 2; } /* quick bar cố định đáy màn hình */ #quickbar { display: flex; flex-direction: column; gap: 7px; position: fixed; left: 0; right: 0; bottom: 0; z-index: 9; background: var(--card); border-top: 1px solid var(--card-b); padding: 9px 12px calc(9px + env(safe-area-inset-bottom)); box-shadow: 0 -6px 24px rgba(0, 0, 0, .4); } .qb-row { display: flex; align-items: center; gap: 9px; } /* 3 engine (thêm zone 30/07) không còn vừa một hàng ở 375px — xuống dòng thay vì tràn ngang. Chỉ hàng settings, hàng chính vẫn một dòng. */ .qb-row.qb-settings { flex-wrap: wrap; } .pill.mini { padding: 6px 12px; font-size: 12.5px; } .radio.mini { font-size: 13px; } #qb-detail, #qb-drills { background: none; border: 1px solid var(--card-b); color: var(--text); border-radius: 999px; padding: 6px 13px; font-size: 12.5px; cursor: pointer; } /* đẩy cặp nút (Bài tập + Chi tiết) sang phải, badge mục tiêu ở trái */ #qb-drills { margin-left: auto; } #qb-spin { width: 50px; height: 50px; flex: 0 0 auto; } .qb-mid { flex: 1; min-width: 0; } .qb-power { gap: 3px; } .qb-power .seg { height: 12px; border-radius: 3px; } #qb-info { color: var(--muted); font-size: 11.5px; margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #qb-go { width: auto; flex: 0 0 auto; padding: 12px 17px; font-size: 14px; border-radius: 11px; } #qb-play { flex: 0 0 auto; padding: 12px 15px; font-size: 14px; border-radius: 11px; } /* hàng Analyzer: cùng kích thước với hàng gợi ý ở trên để đáy màn hình không nhảy khi đổi tab (main đã chừa sẵn 150px cho quickbar) */ #qban-spin { width: 50px; height: 50px; flex: 0 0 auto; } .qban-bar .seg { height: 12px; border-radius: 3px; } #qban-info { color: var(--muted); font-size: 11.5px; margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .qban-replay { flex: 0 0 auto; width: auto; padding: 12px 15px; font-size: 14px; border-radius: 11px; } #toast { bottom: calc(150px + env(safe-area-inset-bottom)); } /* trong sheet: tap target lớn */ .alt { padding: 13px 14px; font-size: 14px; } .pill { padding: 10px 15px; } /* chips: 9 nút wrap 2 hàng trong sheet, tap target to hơn */ #ball-chips { gap: 8px; } .chip { width: 40px; height: 40px; font-size: 15px; } } @keyframes slideup { from { transform: translateY(28%); opacity: .4; } to { transform: none; opacity: 1; } }