Spaces:
Sleeping
Sleeping
| /* 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 ; | |
| color: #6d7883 ; | |
| 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 ; } | |
| /* 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; } | |
| } | |