Spaces:
Sleeping
Sleeping
Download app/static/style.css from masterdanh/poolcoach-analyzer: direct link, hf CLI and curl.
- Browser
- Download file 19.4 kB
-
https://huggingface.co/spaces/masterdanh/poolcoach-analyzer/resolve/main/app/static/style.css
- Command line
-
hf download hf://spaces/masterdanh/poolcoach-analyzer/app/static/style.css
-
curl -L -o style.css https://huggingface.co/spaces/masterdanh/poolcoach-analyzer/resolve/main/app/static/style.css
19.4 kB
| /* PoolCoach Analyzer front-end styles, mobile first. | |
| No framework, no build step. | |
| Some rules below still style parts of the earlier app (the suggestion | |
| tab, the drill tab, the image-scan overlay) that this repository does not | |
| ship. They are kept for now because several Analyzer elements inherit | |
| from the same base rules; removing them is a separate cleanup that needs | |
| its own visual check. */ | |
| :root { | |
| --bg: #14181d; | |
| --card: #1e242b; | |
| --card-b: #2d3640; | |
| --text: #e9edf1; | |
| --muted: #93a0ac; | |
| --green: #0da357; | |
| --green-d: #0b8a4b; | |
| --gold: #ffd700; | |
| --zone: #38c8f5; /* the drill target zone -- deliberately NOT the gold | |
| of a target pocket */ | |
| } | |
| * { 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; | |
| } | |
| /* ----------------------------------------------------------- the table */ | |
| #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; /* dragging must not scroll the page on mobile */ | |
| user-select: none; | |
| } | |
| /* ----------------------------------------------------------- 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; | |
| } | |
| /* .an-cta is a call-to-action button that LOOKS like the old .btn-go but | |
| is its own class. In the earlier app, app.js bound an action to every | |
| .btn-go and forced their label and disabled state, so any button | |
| borrowing that class inherited both -- a real bug caught in a smoke test. | |
| The rule stands as a reminder: never borrow a class that carries an event | |
| binding. */ | |
| .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; | |
| } | |
| /* ---------------------------------------- secondary buttons */ | |
| .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; } | |
| /* the cue-ball and power-bar widget of the earlier app */ | |
| .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; } | |
| .viz-cap { color: var(--muted); font-size: 11.5px; margin-top: 5px; } | |
| /* The width cap belongs to the caption UNDER THE BALL, which is tied to | |
| that column's width -- it used to sit on .viz-cap itself, so the caption | |
| in the wide right-hand column was squeezed to 120px too and broke one | |
| sentence over five cramped lines. */ | |
| .viz-spin .viz-cap { | |
| max-width: 140px; | |
| text-wrap: balance; /* two tidy halves instead of a stray last word */ | |
| } | |
| .viz-power .viz-cap { text-wrap: pretty; } | |
| .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; } | |
| .viz-scale { | |
| display: flex; | |
| justify-content: space-between; | |
| color: var(--muted); | |
| font-size: 10.5px; | |
| margin-top: 5px; | |
| } | |
| /* The replay widget on the detail page: it looks EXACTLY like the older | |
| #shotviz but carries its OWN interactive classes, because .power-bar and | |
| .spin-dot were swept globally by the suggestion tab's initialisers. */ | |
| #and-spin-ball { width: 104px; height: 104px; 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; } | |
| /* The hero at the top of the detail page: the spin label is the headline of | |
| the whole page, so it reads at heading size with the plain sentence and | |
| the power line stepping down from it. */ | |
| .and-hero-head { | |
| display: flex; | |
| align-items: center; | |
| gap: 9px; | |
| flex-wrap: wrap; | |
| } | |
| .and-hero-spin { | |
| font-size: 19px; | |
| font-weight: 700; | |
| letter-spacing: .2px; | |
| } | |
| .and-hero-sub { | |
| color: var(--muted); | |
| font-size: 12.5px; | |
| margin: 3px 0 10px; | |
| } | |
| .and-toggle { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| margin: 10px 0 2px; | |
| font-size: 13px; | |
| cursor: pointer; | |
| } | |
| /* Progressive disclosure: the technical numbers are one click away, not | |
| gone. It reads as a quiet drawer rather than a section heading, so the | |
| hero above it stays the loudest thing on the page. */ | |
| .and-tech { | |
| margin-top: 12px; | |
| border: 1px solid var(--card-b); | |
| border-radius: 10px; | |
| padding: 8px 11px; | |
| } | |
| .and-tech > summary { | |
| cursor: pointer; | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--muted); | |
| list-style: none; | |
| } | |
| .and-tech > summary::-webkit-details-marker { display: none; } | |
| /* The caret is DRAWN out of borders, not typed: a decorative glyph in | |
| `content` was mangled into a control byte on the way to disk once | |
| already (a unicode escape for the triangle reached disk as 0x15 | |
| followed by "B8") and showed on screen as a broken box. No glyph, | |
| no font dependency, no escape left to get wrong -- just a triangle | |
| that turns. Keep it so: no decorative unicode in CSS `content`. */ | |
| .and-tech > summary::before { | |
| content: ""; | |
| display: inline-block; | |
| width: 0; | |
| height: 0; | |
| margin-right: 9px; | |
| vertical-align: middle; | |
| border-left: 6px solid currentColor; | |
| border-top: 5px solid transparent; | |
| border-bottom: 5px solid transparent; | |
| transition: transform .18s ease; | |
| } | |
| .and-tech[open] > summary::before { transform: rotate(90deg); } | |
| .and-tech[open] > summary { margin-bottom: 4px; } | |
| .and-tech-links { margin-top: 10px; } | |
| /* ------------------------------------------- the full-screen overlay */ | |
| /* Every .scan-* class was introduced fresh, borrowing nothing that carries | |
| an event binding. The overlay floats above everything (including the | |
| mobile sheet at z-index 20 and the backdrop at 15), because placing | |
| corners is a full-screen job. The Analyzer's corner overlay reuses these | |
| STYLE-ONLY classes. */ | |
| .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; /* two lines, so a longer hint does not shift the | |
| image */ | |
| } | |
| .scan-actions { margin-top: 12px; margin-bottom: 0; } | |
| .scan-go { flex: 2; width: auto; padding: 11px; } | |
| /* ------------------------------------------------- the Analyzer view */ | |
| /* Every .an-* class is new; the pocket overlay borrows the STYLE-ONLY | |
| .scan-overlay/.scan-box classes, none of which carry an event binding. */ | |
| .an-intro { | |
| font-size: 13px; | |
| color: var(--muted, #9fb0c0); | |
| line-height: 1.5; | |
| margin-bottom: 10px; | |
| } | |
| /* One declaration, not two. There used to be a second `.an-note` block | |
| further down that overrode only the type, so `display: flex` from up here | |
| leaked into EVERY note on the page and laid its prose out as flex items. | |
| The flex layout belongs to the illustrated note alone, which now says so | |
| with a class of its own. */ | |
| .an-note { | |
| margin: 8px 0 10px; | |
| font-size: 12px; | |
| color: var(--muted, #9fb0c0); | |
| line-height: 1.5; | |
| } | |
| /* the static note stating the cloth-nose click standard, above the corner | |
| canvas: a cross-section drawing beside its sentence */ | |
| .an-note-illus { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-size: 12.5px; | |
| line-height: 1.45; | |
| } | |
| .an-note-illus 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; | |
| } | |
| /* The two-column model-versus-analytic table: the header and the middle | |
| value cell (the model column) use the same numeric style as the last | |
| column. The model caveat and the disagreement lines are INFORMATION, so | |
| they read in a different tone from the .an-warn warnings. */ | |
| .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; | |
| } | |
| /* ------------------------------------------------- the shot list table. | |
| Every .anv-* class is new, borrowing nothing that carries an event | |
| binding. An .anv-err row is a shot that could not be analysed: greyed, | |
| with its reason. */ | |
| #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; | |
| } | |
| /* The spin cell: a picture of WHERE the cue hit, the class in words, and | |
| how much to trust it. The dot carries .anv-spin-dot and NOT | |
| .and-spin-dot -- fillAndShotviz sweeps every .and-spin-dot on the page | |
| from the shot currently open, which would rewrite every row at once. */ | |
| .anv-spin-cell { display: flex; align-items: center; gap: 9px; } | |
| .anv-spin-ball { | |
| width: 34px; | |
| height: 34px; | |
| flex: 0 0 auto; | |
| display: block; | |
| } | |
| .anv-spin-lbl { font-size: 12.5px; margin-bottom: 3px; } | |
| .anv-badge-sm { padding: 0 7px; font-size: 10.5px; } | |
| /* The power cell: the same 0.5-8.0 m/s scale as the detail widget, at row | |
| size, with its own class for the same reason as the spin dot. */ | |
| .anv-power-cell { min-width: 104px; } | |
| .anv-power-bar { display: flex; gap: 2px; } | |
| .anv-power-bar .seg { | |
| flex: 1; | |
| height: 9px; | |
| border-radius: 2px; | |
| background: #2a323b; | |
| } | |
| .anv-power-lbl { font-size: 12px; margin-top: 5px; } | |
| .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); } | |
| /* The valve: a shot suspected of not being a shot. Greyed like .anv-err | |
| but its OWN class, because the row is still done and still clickable | |
| whereas .anv-err means the shot could not be analysed at all. Never | |
| borrow a class whose meaning differs. */ | |
| .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); } | |
| /* the per-shot overlay: play and download links inside the status cell */ | |
| .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; } | |
| /* ----------------------------- the shot detail page (.and-* classes) */ | |
| #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 ; } | |
| /* the quickbar and the bottom sheet: mobile only */ | |
| #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; } /* tighter -- the UI explains itself */ | |
| main { | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 12px; | |
| padding: 8px 12px calc(150px + env(safe-area-inset-bottom)); /* room for the quickbar */ | |
| } | |
| /* the panel is HIDDEN by default and opens as a bottom sheet from the | |
| "Details" button */ | |
| #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; | |
| } | |
| /* the quickbar pinned to the bottom of the screen */ | |
| #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; } | |
| /* the engine pills no longer fitted one row at 375px, so they wrap | |
| instead of overflowing. Settings row only; the main row stays on one | |
| line. */ | |
| .qb-row.qb-settings { flex-wrap: wrap; } | |
| #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; | |
| } | |
| /* push the buttons to the right, with the target badge on the left */ | |
| .qb-mid { flex: 1; min-width: 0; } | |
| .qb-power { gap: 3px; } | |
| .qb-power .seg { height: 12px; border-radius: 3px; } | |
| /* the hero gives width back to the words: at 375px the ball column was | |
| taking 140px of 347 to fit one caption line, which cost the sentence | |
| beside it two extra lines. The caption wraps to two instead. */ | |
| #and-spin-ball { width: 84px; height: 84px; } | |
| .viz-spin .viz-cap { max-width: 112px; } | |
| .viz { gap: 13px; } | |
| /* the Analyzer row keeps the same height as the row above it, so the | |
| bottom of the screen does not jump (main already reserves 150px for the | |
| 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)); } | |
| /* inside the sheet: larger tap targets */ | |
| /* chips: nine buttons wrapping onto two rows in the sheet, with bigger | |
| tap targets */ | |
| } | |
| @keyframes slideup { | |
| from { transform: translateY(28%); opacity: .4; } | |
| to { transform: none; opacity: 1; } | |
| } | |