/* ============================================================================
   PROJECT X design language v2 — "the scoreboard"  (foundation: tokens + chrome)
   ----------------------------------------------------------------------------
   PRIMARY BUTTON — standardized treatment (use this one, everywhere):
     The single primary CTA per view = `.btn` (gold, var(--gold)) WITH `.px-display`
     added:  <button class="btn px-display">Unlock</button>
     That renders the gold button in the athletic display stack — italic, uppercase,
     ~15px, faux-condensed — for the Reps "scoreboard" feel. Secondary actions stay
     `.btn-outline`. A plain `.btn` (no .px-display) is still valid for non-hero gold
     actions and keeps its original 13px/800 uppercase look, so nothing breaks.
     Rule of thumb for the page agents: ONE `.btn.px-display` gold primary per view;
     demote every other action to `.btn-outline`.
   TOKENS: --radius=10px (controls) · --radius-card=14px (cards) · warmer canvas #FAF9F6.
   Additive only — every existing class keeps working; dark overrides re-verified.
   ============================================================================ */
* { margin: 0; padding: 0; box-sizing: border-box; }
/* kill iOS double-tap zoom app-wide (taps stay instant; scroll + pinch on
   canvas/media unaffected). Rim-drag handles keep their own touch-action:none. */
html, body { touch-action: manipulation; }
a, button, input, select, textarea, label, [role="button"] { touch-action: manipulation; }
/* ---------- iOS edge-swipe "goes back" guard ----------
   In the Capacitor WKWebView every section switch is a real location.href page
   load, so the webview keeps a back/forward history stack. A horizontal pan that
   over-scrolls a strip (the .tabs row, a .px-shelf, a segmented -seg control) or
   the page body then chains outward and can trip iOS's edge-swipe navigation,
   yanking the athlete back a page mid-scroll. overscroll-behavior stops the
   chaining WITHOUT disabling any scrolling: vertical scroll and the horizontal
   tab/shelf strips still scroll normally, they just no longer bubble the pan.
   `none` on the root also kills pull-to-refresh / rubber-band at the viewport. */
html, body { overscroll-behavior: none; }
/* Any horizontal scroller (the shared .tabs-inner, plus the page-scoped shelves
   and segmented strips that set overflow-x inline) contains its own overscroll so
   a swipe that hits the end doesn't chain out to a back gesture. */
.tabs-inner, .px-shelf-scroll, [style*="overflow-x"] { overscroll-behavior-x: contain; }
:root {
  --canvas: #FAF9F6;
  --card: #FFFFFF;
  --border: #E4E4E4;
  --text: #202124;
  --muted: #808080;
  --gold: #F8D24B;
  --gold-dark: #E5BE33;
  --gold-ink: #A8842A; /* readable gold for TEXT/icons on light backgrounds (brand antique gold) */
  --star: #B8860B; /* rating stars — darker amber clears contrast on white */
  --blue: #4B7BEC;
  --green: #259A46;
  --radius: 10px;
  --radius-card: 14px;
  --shadow: 0 1px 2px rgba(0,0,0,.07);
  --hover: #F4F2EC;
  --gold-tint: #FDF6DF;
  --track: #EFEDE6;
  --body-text: #444444;
  --px-display-font: 'Arial Narrow', Impact, 'Helvetica Neue', sans-serif;

  /* ---------------------------------------------------------------------
     PX MOTION TOKENS (motion bible §1) — ONE easing/duration vocabulary so
     every animation in the app reads like one hand made it. Additive: these
     are new custom properties, nothing existing is redefined.

     CURVES
       --px-ease-out-expo   entrances. Covers most of the distance in the
                            first third, then settles — the "arrives" feel.
       --px-ease-out-quint  the house curve v1 already used everywhere; kept
                            as a token so v1 and v2 rules can't drift.
       --px-ease-in-out     moves (something travelling A→B on screen).
       --px-ease-in         exits. Accelerates away.
       --px-spring          "physical" overshoot. Approximates a spring of
                            stiffness 170 / damping 22 (ζ≈0.84) for a single
                            transition; anything that needs a REAL spring
                            (multi-property, retargetable) uses
                            PXMotion.springKeyframes() in shell.js, which
                            integrates that same ODE and samples it.
     DURATIONS (bible: UI 140-260ms, celebratory 400-800ms, ambient 2-4s)
       tap 140 · ui 200 · ui-slow 260 · page 220 · sheet 320
       celebrate 460 · celebrate-long 800 · ambient 3s
     --------------------------------------------------------------------- */
  --px-ease-out-expo:  cubic-bezier(.16, 1, .3, 1);
  --px-ease-out-quint: cubic-bezier(.22, 1, .36, 1);
  --px-ease-in-out:    cubic-bezier(.65, 0, .35, 1);
  --px-ease-in:        cubic-bezier(.4, 0, 1, 1);
  --px-spring:         cubic-bezier(.34, 1.56, .64, 1);
  --px-spring-soft:    cubic-bezier(.3, 1.3, .5, 1);
  --px-t-tap: 140ms;
  --px-t-ui: 200ms;
  --px-t-ui-slow: 260ms;
  --px-t-page: 220ms;
  --px-t-sheet: 320ms;
  --px-t-celebrate: 460ms;
  --px-t-celebrate-long: 800ms;
  --px-t-ambient: 3s;
}
[data-theme="dark"] {
  --canvas: #141119;  /* slight purple cast (was #121212) */
  --card: #1E1A24;    /* slight purple cast (was #1C1C1C) */
  --border: #322D3B;
  --text: #EDEDED;
  --muted: #9A9A9A;
  --shadow: 0 1px 2px rgba(0,0,0,.4);
  --hover: #242424;
  --gold-tint: #2A2412;
  --track: #2E2E2E;
  --body-text: #C9C9C9;
  --gold-ink: #F8D24B; /* on dark backgrounds full-brightness gold reads fine */
  --star: #E5A400; /* on dark, vibrant amber stars read fine */
}
[data-theme="dark"] .badge-green { background: #173420; color: #7ED9A0; }
[data-theme="dark"] .search input { background: #242424; color: var(--text); }
html { background: var(--canvas); }
body { transition: background .2s ease, color .2s ease; }
body {
  background: var(--canvas);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.5;
}
a { color: inherit; text-decoration: none; }

/* ============================================================================
   PRE-PAINT CHROME — the fix for the "empty room" frame between sections.
   ----------------------------------------------------------------------------
   Every section is a real document load, and the topbar + bottom nav are MARKUP
   THAT shell.js INJECTS. styles.css is render-blocking, so the browser paints the
   new document the moment the CSS lands — but shell.js (164KB, plus the other
   blocking scripts behind it) hasn't run yet, so that first paint is a flat sheet
   of --canvas with NO topbar and NO tab bar. Measured on an iPhone-sized viewport
   with 4x CPU throttle: 277ms of that on index.html with local assets, and
   1.2-3.6s on Fast 3G. That gap is what reads as "weird glitches" when you jump
   from Train back to Community — the tab bar visibly disappears and pops back.

   Fix: paint the chrome's two bands from CSS, in the SAME first frame as the
   background, so the frame is never empty. They are pure decoration —
   position:fixed, pointer-events:none, no layout box — so they can never cause a
   shift, and the real chrome lands on top of pixels that already match. The
   moment shell.js runs it stamps html.px-shell (synchronously, before any further
   paint), which switches these off.

   OPT-IN via html.px-prepaint so the pages that deliberately ship without the
   shell (onboard, tracker, landing, legal, drill-runner...) are untouched.
   Geometry mirrors shell.js exactly: topbar 54px (mobile) / 60px (>=641px) plus
   the top inset; bottom nav 62px plus the bottom inset, and it only exists at
   <=760px where .bottomnav is display:flex. --px-sat/--px-sab floors match the
   native rules in shell.js so the Capacitor webview lines up too.
   ============================================================================ */
html.px-prepaint:not(.px-shell) body::before,
html.px-prepaint:not(.px-shell) body::after {
  content: "";
  position: fixed;
  left: 0;
  right: 0;
  z-index: 90;
  background: var(--card);
  pointer-events: none;
}
html.px-prepaint:not(.px-shell) body::before {
  top: 0;
  height: calc(60px + env(safe-area-inset-top));
  border-bottom: 1px solid var(--border);
}
html.px-prepaint.px-native:not(.px-shell) body::before {
  height: calc(54px + max(env(safe-area-inset-top), var(--px-sat, 50px)));
}
html.px-prepaint:not(.px-shell) body::after { display: none; }
@media (max-width: 760px) {
  html.px-prepaint:not(.px-shell) body::after {
    display: block;
    bottom: 0;
    height: calc(62px + env(safe-area-inset-bottom));
    border-top: 1px solid var(--border);
  }
  html.px-prepaint.px-native:not(.px-shell) body::after {
    height: calc(62px + max(env(safe-area-inset-bottom), 16px));
  }
}
@media (max-width: 640px) {
  html.px-prepaint:not(.px-shell) body::before { height: calc(54px + env(safe-area-inset-top)); }
}

/* ---------- Topbar ---------- */
.topbar { background: var(--card); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 50; }
.topbar-inner { max-width: 1085px; margin: 0 auto; display: flex; align-items: center; gap: 16px; height: 60px; padding: 0 16px; }
.logo { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 17px; white-space: nowrap; }
.logo-mark { background: var(--text); color: var(--gold); font-size: 13px; font-weight: 800; width: 32px; height: 32px; border-radius: 8px; display: flex; align-items: center; justify-content: center; }
.search { flex: 1; }
.search input { width: 100%; max-width: 420px; height: 38px; border: 1px solid var(--border); border-radius: 20px; padding: 0 16px; font-size: 14px; background: var(--canvas); outline: none; }
.search input:focus { border-color: #c9c9c9; }
.topbar-icons { display: flex; align-items: center; gap: 12px; }
.icon-btn { background: none; border: none; font-size: 18px; cursor: pointer; opacity: .75; }
.icon-btn:hover { opacity: 1; }

/* ---------- Tabs ---------- */
.tabs { background: var(--card); border-bottom: 1px solid var(--border); }
.tabs-inner { max-width: 1085px; margin: 0 auto; display: flex; gap: 4px; padding: 0 16px; overflow-x: auto; }
.tab { padding: 12px 14px; font-size: 14px; font-weight: 500; color: var(--muted); border-bottom: 2px solid transparent; white-space: nowrap; position: relative; }
.tab:hover { color: var(--text); }
.tab.active { color: var(--text); font-weight: 700; border-bottom-color: var(--text); }
.tab-train::after { content: ""; width: 6px; height: 6px; background: var(--gold-dark); border-radius: 50%; position: absolute; top: 10px; right: 4px; }

/* ---------- Layout ---------- */
.container { max-width: 1085px; margin: 0 auto; padding: 20px 16px 60px; }
.layout { display: flex; gap: 20px; align-items: flex-start; }
.main-col { flex: 1; min-width: 0; }
.sidebar { width: 300px; flex-shrink: 0; display: flex; flex-direction: column; gap: 16px; }
@media (max-width: 860px) { .layout { flex-direction: column; } .sidebar { width: 100%; } }

/* ---------- Cards ---------- */
.card { background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-card); box-shadow: var(--shadow); }
.card-pad { padding: 16px; }

/* ---------- Buttons ---------- */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; background: var(--gold); color: var(--text); border: none; border-radius: var(--radius); height: 40px; padding: 0 18px; font-size: 13px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; cursor: pointer; }
.btn:hover { background: var(--gold-dark); }
.btn:disabled { opacity: .5; cursor: default; }
.btn-block { width: 100%; }
.btn-outline { background: transparent; border: 1px solid var(--border); color: var(--text); }
.btn-outline:hover { background: var(--canvas); }
/* Standardized primary CTA: gold .btn + athletic display type (see header comment) */
.btn.px-display { font-size: 15px; letter-spacing: .02em; text-transform: uppercase; }

/* ---------- Avatars + level badge ---------- */
.avatar { width: 40px; height: 40px; border-radius: 50%; color: #fff; font-weight: 700; font-size: 14px; display: flex; align-items: center; justify-content: center; position: relative; flex-shrink: 0; }
.avatar-sm { width: 32px; height: 32px; font-size: 12px; }
.avatar-lg { width: 64px; height: 64px; font-size: 22px; }
.lvl { position: absolute; bottom: -3px; right: -3px; background: var(--blue); color: #fff; font-size: 10px; font-weight: 700; width: 17px; height: 17px; border-radius: 50%; border: 2px solid var(--card); display: flex; align-items: center; justify-content: center; }
.avatar-lg .lvl { width: 22px; height: 22px; font-size: 12px; }

/* ---------- Feed ---------- */
.composer { display: flex; align-items: center; gap: 12px; padding: 14px 16px; margin-bottom: 16px; cursor: text; }
.composer .prompt { color: var(--muted); font-size: 15px; }
.pills { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.pill { background: var(--card); border: 1px solid var(--border); border-radius: 20px; padding: 7px 14px; font-size: 13px; font-weight: 500; color: var(--muted); cursor: pointer; }
.pill.active { background: var(--text); color: #fff; border-color: var(--text); }
.post { margin-bottom: 16px; padding: 16px; }
/* ---- feed windowing (build 182) ------------------------------------------
   The community feed carries 400+ post cards, and the browser was styling,
   laying out and painting every one of them on every frame — measured on a
   390x844 / 4x-CPU profile as ~5.0s of pure rendering work across a 5-second
   scroll, with ZERO javascript running. That is the "glitchy scrolling" the
   founder keeps reporting.

   `content-visibility: auto` tells the engine to skip the guts of any card
   that isn't near the viewport, so the cost tracks what's on screen instead of
   how many posts the community has ever written. `contain-intrinsic-size: auto
   <fallback>` keeps the scrollbar and the scroll position honest: `auto`
   replays each card's last-rendered height once it's been seen, and the
   fallback is a realistic first guess for one that hasn't. 190px is not a
   guess: it is the MEASURED mean height of the 412 real cards in this
   community at 390px wide (median 173, p75 203, p90 266). Getting that number
   wrong is what makes a windowed feed feel rubbery, because the page length
   shifts under the thumb as unseen cards resolve to their true size.

   Scoped to cards inside the feed on purpose — a post opened in the sheet is
   always on screen and must never be skipped. Unsupported engines (iOS Safari
   before 18) simply ignore both properties and behave exactly as they do now. */
#feed > .post {
  content-visibility: auto;
  contain-intrinsic-size: auto 190px;
}
/* A card whose thread is open grows far past the estimate and is being read —
   take it out of the windowing entirely so nothing can be skipped mid-scroll. */
#feed > .post.thread-open { content-visibility: visible; }
.post-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.post-meta { font-size: 12px; color: var(--muted); }
.post-author { font-weight: 700; font-size: 14px; }
.post-title { font-size: 16px; font-weight: 700; margin-bottom: 4px; }
.post-body { color: var(--body-text); font-size: 14px; margin-bottom: 12px; }
.post-flex { display: flex; gap: 16px; }
.post-flex > div:first-child { flex: 1; min-width: 0; }
.thumb { width: 120px; height: 80px; border-radius: 8px; flex-shrink: 0; }
.post-actions { display: flex; gap: 18px; color: var(--muted); font-size: 13px; font-weight: 500; }
.post-actions button { background: none; border: none; color: var(--muted); font-size: 13px; font-weight: 500; cursor: pointer; display: flex; align-items: center; gap: 6px; }
.post-actions button:hover { color: var(--text); }
.pin { color: var(--muted); font-size: 12px; font-weight: 600; margin-bottom: 8px; display: flex; align-items: center; gap: 6px; }
.auto-post { border-left: 4px solid var(--gold); }
.micro-label { font-size: 10px; font-weight: 800; letter-spacing: .8px; color: #a8842a; margin-bottom: 6px; }
.workout-summary { background: var(--gold-tint); border-radius: 8px; padding: 12px; font-size: 14px; margin-bottom: 10px; }

/* ---------- Sidebar community card ---------- */
.cover { height: 140px; border-radius: 8px 8px 0 0; background: linear-gradient(135deg, #202124 0%, #3d3d3d 60%, #a8842a 100%); display: flex; align-items: center; justify-content: center; color: var(--gold); font-weight: 800; font-size: 26px; letter-spacing: 3px; }
.stats-row { display: flex; text-align: center; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); margin: 12px 0; }
.stats-row > div { flex: 1; padding: 10px 0; }
.stats-row > div + div { border-left: 1px solid var(--border); }
.stat-num { font-family: var(--px-display-font); font-style: italic; font-weight: 800; text-transform: uppercase; letter-spacing: -.01em; font-size: 21px; line-height: 1; }
.stat-label { font-size: 12px; color: var(--muted); }

/* ---------- Leaderboard rows ---------- */
.lb-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; }
.lb-rank { width: 22px; color: var(--muted); font-weight: 700; font-size: 13px; text-align: center; flex-shrink: 0; }
.lb-name { flex: 1; font-weight: 500; font-size: 14px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lb-val { font-weight: 700; font-size: 14px; }
.lb-me { border: 2px solid var(--gold); border-radius: 8px; padding: 8px 10px; background: var(--gold-tint); }

/* ---------- Progress ---------- */
.progress { height: 8px; background: var(--track); border-radius: 4px; overflow: hidden; }
.progress > div { height: 100%; background: var(--gold-dark); border-radius: 4px; }

/* ---------- Misc ---------- */
.badge { display: inline-block; font-size: 10px; font-weight: 800; letter-spacing: .6px; padding: 3px 8px; border-radius: 4px; background: var(--text); color: var(--gold); }
.badge-green { background: #E3F5E9; color: #1a7f3c; }
.online-dot { width: 8px; height: 8px; background: var(--green); border-radius: 50%; display: inline-block; }
.muted { color: var(--muted); }
.duotone-1 { background: linear-gradient(135deg, #33322E, #191919); }
.duotone-2 { background: linear-gradient(135deg, #8A6A1F, #1B1B1B); }
.duotone-3 { background: linear-gradient(135deg, #4A3D18, #141414); }
.duotone-4 { background: linear-gradient(135deg, #55524A, #1B1B1B); }
h1 { font-size: 22px; margin-bottom: 16px; }
h2 { font-size: 17px; margin-bottom: 12px; }
input[type="number"], input[type="text"] { border: 1px solid var(--border); border-radius: var(--radius); height: 38px; padding: 0 12px; font-size: 14px; outline: none; width: 100%; background: var(--card); color: var(--text); }
input:focus { border-color: #c9c9c9; }

/* ---------- Athletic display language (Reps DNA) ---------- */
/* .px-display — the display stack. On page h1s, big stat numbers, primary CTAs only.
   Never on body copy, post titles, or member names. */
.px-display, .page-head h1 {
  font-family: var(--px-display-font);
  font-style: italic;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.02em;
}
/* .px-eyebrow — micro-label above every page h1 (e.g. THE FEED). Optional gold dot. */
.px-eyebrow {
  display: block;
  font-size: 10px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .35em;
  color: var(--muted);
}
.px-eyebrow.dot::before {
  content: "";
  display: inline-block;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--gold);
  margin-right: 8px;
  vertical-align: 1px;
}
/* .page-head — eyebrow + display h1 + muted sub, consistent rhythm. */
.page-head { margin-bottom: 24px; }
.page-head .px-eyebrow { margin-bottom: 8px; }
.page-head h1 { font-size: 30px; line-height: 1.04; margin-bottom: 6px; }
.page-head .page-sub, .page-head .sub { font-size: 14px; color: var(--muted); margin-bottom: 0; }
/* .px-score — signature dark scoreboard tile. Dark in BOTH themes (this is what
   separates us from Skool). Gold display number + spaced gray label. */
.px-score {
  background: #101010;
  border: 1px solid #262626;
  border-radius: var(--radius-card);
  padding: 14px 12px;
  text-align: center;
}
.px-score .n {
  display: block;
  font-family: var(--px-display-font);
  font-style: italic;
  font-weight: 800;
  letter-spacing: -.01em;
  font-size: 34px;
  line-height: 1;
  color: var(--gold);
}
.px-score .l {
  display: block;
  margin-top: 7px;
  font-size: 9px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .18em;
  color: #8C8C8C;
}

/* ---------- Mobile polish ---------- */
@media (max-width: 640px) {
  .container { padding: 14px 12px 84px; }
  h1 { font-size: 19px; }
  h2 { font-size: 16px; }
  .page-head { margin-bottom: 18px; }
  .page-head h1 { font-size: 25px; }
  .post, .card-pad { padding: 14px; }
  .thumb { width: 92px; height: 64px; }
  .topbar-inner { height: 54px; gap: 10px; }
  .logo { font-size: 15px; }
  .logo-mark { width: 28px; height: 28px; font-size: 12px; }
  .pills { gap: 6px; }
  .pill { padding: 6px 12px; font-size: 12px; }
  .post-title { font-size: 15px; }
}

/* ---------- Dark-mode fixes for shared components (light mode unchanged) ----------
   These invert cleanly in dark: components that use a light var(--text) as a *background*
   with white/gold text, or gold buttons whose var(--text) label goes light in dark. */
[data-theme="dark"] .btn { color: #1a1a1a; }              /* gold btn: dark label stays readable */
[data-theme="dark"] .btn-outline { color: var(--text); }  /* outline btn keeps light label */
[data-theme="dark"] .pill.active { color: #1a1a1a; }      /* active pill bg is light var(--text) */
[data-theme="dark"] .badge:not(.badge-green) { background: var(--gold); color: #1a1a1a; }
[data-theme="dark"] .logo-mark { background: var(--gold); color: #1a1a1a; }

/* ---------- iOS zoom guard (BUG2) ----------
   iOS Safari/WKWebView auto-zooms when a focused text field has font-size < 16px and
   never zooms back out. Several fields ship at 14px (input[type=text|number], .search
   input, etc.). Force >=16px for text-entry controls on mobile ONLY so tapping them no
   longer zooms. Desktop sizing above is untouched. The type-selectors below match the
   (0,1,1) specificity of the offending `input[type="text"]` rule and come last, so they
   win; the bare input/textarea/select line covers everything else. */
@media (max-width: 760px) {
  input, textarea, select { font-size: 16px; }
  input[type="text"], input[type="number"], input[type="email"], input[type="tel"],
  input[type="url"], input[type="password"], input[type="search"], input[type="date"] { font-size: 16px; }
  .search input { font-size: 16px; }

  /* Bottom cushion: the fixed bottom nav is 62px + the phone's home-indicator
     safe-area, so every page's content must reserve at least that PLUS breathing
     room, or the last card/section hides under the nav and you can't scroll to it.
     Overrides the tighter 84px above so content always clears the bar with slack. */
  .container { padding-bottom: calc(62px + env(safe-area-inset-bottom) + 40px) !important; }
  html.px-native .container { padding-bottom: calc(62px + var(--px-sab) + 40px) !important; }
}

/* ---------- Premium section transitions (feel like an app, not a website) ----------
   shell.js stamps .px-enter on the page's OWN content (never the topbar / bottom nav /
   siderail — those stay persistent), so switching sections reads as one continuous
   move instead of a hard cut. Since each section is a real page load (location.href),
   the incoming document's own content RISES 12px + fades while the chrome is already
   painted (px-prepaint) in the very first frame. Pure CSS animation ⇒ content is
   clickable the whole time (never gates interactivity).

   MOTION BIBLE §1 (v2): retuned from the old 400ms / 38px horizontal slide to a
   220ms / 12px rise on --px-ease-out-expo. Same content, same layout — it just
   arrives faster and from below, which (a) matches the outgoing scale-down below so
   the two halves of a route change read as ONE gesture, (b) removes the transient
   horizontal overhang the 38px slide used to create, and (c) gets the athlete to an
   interactive screen ~180ms sooner. */
@keyframes pxContentIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
.px-enter,
html.px-shell .container { animation: pxContentIn var(--px-t-page) var(--px-ease-out-expo) both; }

/* ---- OUTGOING half of the route change ----
   PXMotion (shell.js) stamps html.px-leaving on a tap that is definitely going to
   navigate this document away. The old screen fades and scales down 2% so it reads
   as receding BEHIND the incoming one. Navigation is never delayed or preventDefault-ed
   to make room for this — the browser keeps presenting the old document until the new
   one commits, so the 150ms exit simply plays inside latency that already existed.
   Chrome is excluded (it is never .px-enter and never a .container), so the topbar and
   bottom nav hold perfectly still through the whole transition. */
@keyframes pxContentOut {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: scale(.98); }
}
html.px-leaving .px-enter,
html.px-leaving.px-shell .container { animation: pxContentOut 150ms var(--px-ease-in) both; }
/* html.px-shell .container covers the common case: the page's main content wrapper
   (built by shell.js-following scripts on JS-heavy pages like the feed) slides in the
   moment it's inserted. .px-enter is stamped by shell.js on EVERY top-level body child
   (see shell.js), so full-screen app shells (.screen / .day-screen / .lab-screen etc.)
   that aren't wrapped in a .container get the same slide. The chrome is never a
   .container and is force-disabled below, so it stays put. */
/* Overflow guard (kept from v1): the entrance is vertical now and the exit only scales
   DOWN, so neither can overhang — but pages carry their own horizontal shelves and
   strips, and this clip has been shipping since the nav pass. Leaving it in place.
   overflow-x:clip (NOT hidden) does not create a scroll container, so it leaves the
   sticky topbar / fixed overlays and vertical scrolling completely intact. Supported in
   the iOS Capacitor webview (Safari 16+); older engines simply ignore it and fall back
   to a harmless one-frame scrollbar. */
html.px-shell, html.px-shell body { overflow-x: clip; }
/* Chrome is persistent — belt-and-suspenders so it can never inherit the entrance. */
.topbar, .siderail, .bottomnav, .sheet, .drawer, #px-offline-banner { animation: none; }

/* ============================================================================
   PREMIUM MOTION LAYER — kills the "pop, pop, pop".
   Every overlay/sheet/modal in the app opens by toggling display:none → flex on
   a backdrop element (.x.open / .x.show). display can't be transitioned, so we
   attach a keyframe *animation* to the open state instead — it fires the instant
   the element is shown. Two coordinated moves so it reads as one premium gesture:
     1. the backdrop (dim + blur) FADES in                → pxScrimIn (opacity only, zero layout risk)
     2. the content panel ZOOMS/RISES into place          → pxSurfaceIn (scale .965→1 + 10px rise + fade)
   Loaded on every page via styles.css, so it lands app-wide without touching the
   40+ per-page overlay definitions. Where a page already animates its own content
   the higher-specificity ">*" rule below wins and unifies it to one feel.
   Timing (~.30s decelerating ease-out) is intentionally a hair slower than snappy
   so it feels expensive, per request. ============================================================================ */
@keyframes pxScrimIn { from { opacity: 0; } to { opacity: 1; } }
/* v2: the surface now SPRINGS open instead of easing. The overshoot is written as an
   explicit middle keyframe rather than a back-ease so it is BOUNDED — a back-ease on
   `scale` would overshoot ~9% and a full-bleed sheet would briefly bleed off-screen.
   -2px / +0.4% is the whole overshoot: enough to feel physical, too small to overhang.
   Closing stays a plain ease (pxSurfaceOut, §6) — things should leave calmly. */
@keyframes pxSurfaceIn {
  0%   { opacity: 0; transform: translateY(12px) scale(.965); }
  62%  { opacity: 1; transform: translateY(-2px) scale(1.004); }
  100% { opacity: 1; transform: translateY(0)    scale(1);     }
}
/* --- 1. backdrop fade — broad list, opacity-only so it can never shift layout --- */
.modal-backdrop.open, .sheet-bg.open, .sheet.open, .pxs-overlay.open, .pxlink-overlay.open,
.rc-modal.open, .vmodal-bg.open, .wiz-bg.open, .wr-detail.open, .fr-scrim.open, .lab-sheet.open,
.spotsheet.open, .search-pop.open, .admin-menu.open, .chat-panel.open, .room.open, .shell-pop.open,
.vlb.open, .ca-editor.open, .sheet-bg.open, .manual-rim-link.show, .dm-upsell.show, .dc-complete.show,
.detwarn.show, .lab-countdown.show, .startveil.show, .req-sent.show, .promo-msg.show, .unlock-note.show,
.pl-saved.show, .rp-ci-sent.on, .drillpanel.on, .drillcheck.on {
  animation: pxScrimIn .24s ease both;
}
/* --- 2. content zoom/rise — only the clear centered/sheet modals (single content child).
       ">*" beats any inline content animation so the whole app shares one entrance. --- */
.modal-backdrop.open > *, .sheet-bg.open > *, .pxs-overlay.open > *, .rc-modal.open > *,
.vmodal-bg.open > *, .wiz-bg.open > *, .spotsheet.open > *, .lab-sheet.open > * {
  animation: pxSurfaceIn var(--px-t-sheet) var(--px-ease-out-quint) both;
}
/* Dropdowns (admin menu, notifications, search) are the positioned element itself, not a
   backdrop+child — so they only get the fade above, plus a small drop-from-top so they feel
   like they hinge open rather than blink in. transform-origin keeps them pinned to their anchor. */
.admin-menu.open, .shell-pop.open, .search-pop.open {
  animation: pxDropIn .2s cubic-bezier(.22,1,.36,1) both; transform-origin: top center;
}
@keyframes pxDropIn { from { opacity: 0; transform: translateY(-7px) scale(.98); } to { opacity: 1; transform: none; } }

/* Respect the OS reduced-motion setting: no entrance, no theme cross-fade, no overlay motion. */
@media (prefers-reduced-motion: reduce) {
  .px-enter,
  html.px-shell .container,
  [class*="open"], [class*="show"], [class*="open"] > *, [class*="show"] > * { animation: none !important; }
  body { transition: none; }
}

/* ============================================================================
   PX MOTION KIT v1 — the "feels like a real app" layer.
   ----------------------------------------------------------------------------
   Ground rules that every rule below obeys, no exceptions:
     · transform + opacity ONLY. Never width/height/top/left/margin/padding, so
       nothing here can ever produce a layout shift (CLS stays where the nav
       pass left it: ~0).
     · UI feedback 120-300ms. Celebratory moments up to 600ms. Nothing longer.
     · every single animation is switched off under prefers-reduced-motion by
       the one blanket block at the bottom of this section.
     · :active (not :hover) drives tap feedback — phones don't hover, and a
       sticky :hover state on touch is exactly the "not clean" feel he called out.
   ========================================================================== */

/* ---- 1. TAP FEEDBACK ------------------------------------------------------
   Every tappable primitive squishes on press and springs back. The transition
   lives on the base rule so the RELEASE is animated too (a transition only on
   :active snaps back instantly and reads cheap). */
.btn, .btn-outline, .pill, .px-tap, .lb-row.px-tap, .tab, .post-actions button {
  transition: transform 140ms cubic-bezier(.2,.8,.3,1), background-color 140ms ease, opacity 140ms ease;
  -webkit-tap-highlight-color: transparent;
}
.btn:active, .btn-outline:active, .px-tap:active { transform: scale(.955); }
.pill:active, .tab:active, .post-actions button:active { transform: scale(.93); }
.btn:disabled:active { transform: none; }
/* Cards are big surfaces — a full .955 squish on a 350px card reads goofy.
   Press them a hair, like pressing a physical key. */
.card.px-tap:active, .px-card-tap:active { transform: scale(.985); }
/* Chunky-cartoon 3D lip (his paywall taste): the offset shadow collapses as the
   button travels down, so it reads as a real key being pushed in. Opt-in via
   .px-lip so it never rewrites a button that was already art-directed. */
/* The lip colour and travel are lifted verbatim from the paywall CTA he signed
   off on (onboard.html #btn-trial: `0 6px 0 #8a6d1a`, pressing to `0 2px 0`).
   A neutral black lip is invisible on this dark ground — the deep antique gold
   is what actually reads as a pressed toy key. Scaled down from 6px to 4px
   because app CTAs are 40-48px tall, not a full-bleed pinned button. */
.px-lip { box-shadow: 0 4px 0 #8a6d1a; transition: transform 120ms cubic-bezier(.2,.8,.3,1), box-shadow 120ms cubic-bezier(.2,.8,.3,1); }
.px-lip:active { transform: translateY(3px); box-shadow: 0 1px 0 #8a6d1a; }
/* .btn.px-display is, by the contract at the top of this file, the ONE hero CTA
   per view — so that is exactly the button that should carry the lip, and the
   hero action on every screen now matches the paywall's quality bar. The .955
   squish is suppressed here because the lip already supplies the travel; doing
   both at once reads mushy. */
.btn.px-display { box-shadow: 0 4px 0 #8a6d1a; transition: transform 120ms cubic-bezier(.2,.8,.3,1), box-shadow 120ms cubic-bezier(.2,.8,.3,1), background-color 140ms ease; }
.btn.px-display:active { transform: translateY(3px) scale(1); box-shadow: 0 1px 0 #8a6d1a; }
.btn.px-display:disabled { box-shadow: none; }

/* ---- 2. FIRST-PAINT STAGGER ----------------------------------------------
   Lists come in as a wave instead of a wall. Applied to the CONTAINER; the
   children carry the delay. Capped at 10 so a 200-row leaderboard doesn't take
   6 seconds to finish arriving — everything past #10 lands with the 10th. */
/* v2: 8px (was 10px) per bible §1 — at 34ms apart the shorter travel keeps the wave
   reading as one surface arriving rather than ten separate cards flying up. */
@keyframes pxRiseIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.px-stagger > * { animation: pxRiseIn 280ms var(--px-ease-out-expo) both; }
.px-stagger > *:nth-child(1) { animation-delay: 0ms; }
.px-stagger > *:nth-child(2) { animation-delay: 34ms; }
.px-stagger > *:nth-child(3) { animation-delay: 68ms; }
.px-stagger > *:nth-child(4) { animation-delay: 102ms; }
.px-stagger > *:nth-child(5) { animation-delay: 136ms; }
.px-stagger > *:nth-child(6) { animation-delay: 170ms; }
.px-stagger > *:nth-child(7) { animation-delay: 204ms; }
.px-stagger > *:nth-child(8) { animation-delay: 238ms; }
.px-stagger > *:nth-child(9) { animation-delay: 272ms; }
.px-stagger > *:nth-child(n+10) { animation-delay: 306ms; }

/* ---- 3. VALUE CHANGES -----------------------------------------------------
   .px-bump is stamped by PXMotion (shell.js) whenever a counter it watches
   lands on a new value — XP, streak, makes, level. Scale-only so a number that
   grows from 9 to 10 can't nudge its neighbours. */
@keyframes pxBump { 0% { transform: scale(1); } 38% { transform: scale(1.22); } 100% { transform: scale(1); } }
.px-bump { animation: pxBump 340ms cubic-bezier(.34,1.56,.64,1); }
/* Counting digits must not reflow while they tick — tabular figures lock the
   glyph advance so 1→8 is the same width. */
.stat-num, .px-score .n, [data-px-count] { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* ---- 4. CELEBRATORY MOMENTS (session complete / streak / unlock) ----------
   Up to 600ms — these are allowed to feel expensive, they happen rarely.
   PXGame.celebrateLevelUp already owns the full-screen level-up confetti; these
   are the smaller, in-place hits that everything else can reach for. */
@keyframes pxPunch {
  0%   { opacity: 0; transform: scale(.6) rotate(-4deg); }
  55%  { opacity: 1; transform: scale(1.08) rotate(1.5deg); }
  100% { opacity: 1; transform: scale(1) rotate(0); }
}
.px-punch { animation: pxPunch 460ms cubic-bezier(.22,1,.36,1) both; }
/* Gold ring that expands out and fades — a "that counted" pulse behind a
   streak flame / completed session tile. Needs a positioned parent. */
@keyframes pxRing { from { opacity: .55; transform: scale(.7); } to { opacity: 0; transform: scale(1.9); } }
.px-ring::after {
  content: ""; position: absolute; inset: -2px; border-radius: inherit;
  border: 2px solid var(--gold); pointer-events: none;
  animation: pxRing 600ms cubic-bezier(.22,1,.36,1) both;
}
/* Streak flame heartbeat — fires once when the streak ticks up. */
@keyframes pxFlame { 0%,100% { transform: scale(1); } 30% { transform: scale(1.35) rotate(-6deg); } 60% { transform: scale(1.12) rotate(4deg); } }
.px-flame { display: inline-block; animation: pxFlame 520ms cubic-bezier(.34,1.56,.64,1); }
/* Progress / XP bar fill. Transform-only on purpose: the app's bars are driven
   by inline `width: N%`, and transitioning width is a layout property — banned
   here. Pages that want an animated fill set width:100% and drive scaleX, which
   this transition then smooths for free at zero layout cost. */
.progress > div { transform-origin: left center; transition: transform 420ms cubic-bezier(.22,1,.36,1); }

/* ---- 5. XP TOAST ---------------------------------------------------------
   Small gold chip that rises out of the bottom nav when XP lands. Built by
   PXMotion; fixed-position + pointer-events:none so it is invisible to layout. */
@keyframes pxToastIn  { from { opacity: 0; transform: translate(-50%, 14px) scale(.9); } to { opacity: 1; transform: translate(-50%, 0) scale(1); } }
@keyframes pxToastOut { from { opacity: 1; transform: translate(-50%, 0) scale(1); }    to { opacity: 0; transform: translate(-50%, -12px) scale(.96); } }
.px-xp-toast {
  position: fixed; left: 50%; z-index: 3000; pointer-events: none;
  bottom: calc(72px + env(safe-area-inset-bottom));
  background: var(--gold); color: #191919;
  border: 2px solid #191919; border-radius: 999px;
  box-shadow: 0 4px 0 rgba(0,0,0,.45);
  padding: 8px 16px; font-size: 13px; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; white-space: nowrap;
  animation: pxToastIn 260ms cubic-bezier(.34,1.56,.64,1) both;
}
.px-xp-toast.px-out { animation: pxToastOut 220ms ease both; }

/* ---- 6. SHEET / MODAL CLOSE ----------------------------------------------
   The open side was already handled by the PREMIUM MOTION LAYER above; the
   close side used to just yank display back to none. .px-closing (stamped by
   PXMotion.closeSheet) plays the entrance in reverse so both ends match. */
@keyframes pxScrimOut { from { opacity: 1; } to { opacity: 0; } }
@keyframes pxSurfaceOut { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(8px) scale(.975); } }
.px-closing { animation: pxScrimOut .18s ease both !important; }
.px-closing > * { animation: pxSurfaceOut .18s ease both !important; }

/* ---- 7. BOTTOM NAV ACTIVE TAB --------------------------------------------
   The tab you just landed on gives one small confirming pop. Scoped to the icon
   glyph so the label's text box never moves. */
@keyframes pxNavPop { 0% { transform: scale(1); } 40% { transform: scale(1.18); } 100% { transform: scale(1); } }
.bottomnav a.active svg, .bottomnav a.active .ico, .bottomnav .active svg { animation: pxNavPop 300ms cubic-bezier(.34,1.56,.64,1); }

/* ---- 8. SKELETON SHIMMER --------------------------------------------------
   For the "still loading" boxes so they read as deliberate instead of broken.
   background-position is compositor-cheap and touches no layout. */
@keyframes pxShimmer { from { background-position: -180px 0; } to { background-position: 320px 0; } }
.px-skel {
  background-image: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.055) 45%, transparent 90%);
  background-size: 320px 100%; background-repeat: no-repeat;
  animation: pxShimmer 1.25s linear infinite;
}

/* ---- 9. THE BLANKET REDUCED-MOTION GUARD ---------------------------------
   Everything in the motion kit, off in one place. Kept last so it wins. */
@media (prefers-reduced-motion: reduce) {
  .px-stagger > *, .px-bump, .px-punch, .px-ring::after, .px-flame,
  .px-xp-toast, .px-xp-toast.px-out, .px-skel, .px-closing, .px-closing > *,
  .bottomnav a.active svg, .bottomnav a.active .ico, .bottomnav .active svg { animation: none !important; }
  .btn, .btn-outline, .pill, .px-tap, .tab, .post-actions button, .px-lip,
  .btn.px-display, .progress > div { transition: none !important; }
  .btn:active, .btn-outline:active, .px-tap:active, .pill:active, .tab:active,
  .post-actions button:active, .card.px-tap:active, .px-card-tap:active,
  .px-lip:active, .btn.px-display:active { transform: none !important; }
  .px-ring::after { display: none; }
}

/* ============================================================================
   PX MOTION KIT v2 — THE FOUNDATION  (motion bible §1, wave 1)
   ----------------------------------------------------------------------------
   v1 (above) covered the primitives. v2 is the PERVASIVE layer: the things that
   have to be true on every screen for the app to feel expensive — a tap that is
   always acknowledged, a route change with two matching halves, bars that fill,
   loads that shimmer the real layout instead of going blank.

   Same non-negotiables as v1, re-stated because they are what keeps his phone
   cool: transform/opacity/scale ONLY (all compositor properties — no width,
   height, top, left, box-shadow or filter, and nothing on an infinite loop that
   outlives its load state). Everything here is switched off in one block at the
   bottom of this section under prefers-reduced-motion.
   ========================================================================== */

/* ---- v2.1 UNIVERSAL TAP FEEDBACK -----------------------------------------
   v1 gave press feedback to a fixed list of primitives (.btn, .pill, .tab...).
   Everything else in the app — page-local buttons, icon buttons, chips, rows,
   <summary>, [role=button] — got nothing, and a control that does not answer a
   touch is the single loudest "cheap app" tell.

   This closes that gap with ZERO scanning: PXMotion delegates one pointerdown
   listener on the document and stamps .px-tapfx + .px-pressed on the nearest
   interactive ancestor. That means dynamically rendered controls (every list row
   the app builds at runtime) are covered automatically, and there is no
   querySelectorAll sweep or MutationObserver cost on any page.

   WHY `scale:` AND NOT `transform: scale()` — this is load-bearing.
   `scale` is an INDEPENDENT transform property: it composes with whatever
   `transform` the element already has instead of replacing it. Half the controls
   in this app carry their own transform from a page-local rule or an inline
   style, and a blanket `transform: scale(.96)` would silently wipe those. With
   `scale` a rotated badge stays rotated and just squishes too. On an engine
   without support the declaration is dropped and the element simply doesn't
   squish — no breakage, and PXMotion feature-detects it before stamping. */
.px-tapfx { -webkit-tap-highlight-color: transparent; }
/* RELEASE is the spring: the transition lives on the base class, so letting go
   springs back through a hair of overshoot (from .96 the peak is ~1.004). */
.px-tapfx { transition: scale var(--px-t-ui-slow) var(--px-spring); }
/* PRESS is fast and flat — an overshoot on the way DOWN would read as a wobble. */
.px-tapfx.px-pressed { transition: scale 90ms var(--px-ease-out-quint); scale: .96; }
/* Big surfaces get a lighter squish: .96 on a 350px card reads goofy, the same
   way v1 already handles .card.px-tap. */
.px-tapfx.px-press-lg.px-pressed { scale: .985; }
/* Small dense controls (chips, icon buttons) can take a deeper press. */
.px-tapfx.px-press-sm.px-pressed { scale: .93; }
/* A disabled control must never pretend it did something. */
.px-tapfx:disabled.px-pressed, .px-tapfx[aria-disabled="true"].px-pressed { scale: 1; }

/* ---- v2.2 PROGRESS BARS THAT FILL ----------------------------------------
   Bible §1: "never static — animate to target on view, fill uses scaleX, with a
   moving gold shimmer sweep."

   The app's bars are all driven by an inline `width: N%`, and width is a layout
   property — banned. So the width STAYS the truth (zero layout change, zero CLS
   risk, pages keep setting it exactly as they do today) and the entrance is
   purely a transform on top: scaleX runs from --px-bar-from up to 1, where 1 is
   whatever width the page already set. Origin is the left edge so it grows out
   of the track's start.

   PXMotion strips .px-bar-go on animationend, which also removes the ::after —
   so the shimmer is not a permanent compositor layer sitting on every bar. */
.px-bar-fill { transform-origin: left center; position: relative; }
@keyframes pxBarFill { from { transform: scaleX(var(--px-bar-from, 0)); } to { transform: scaleX(1); } }
.px-bar-fill.px-bar-go { animation: pxBarFill var(--px-bar-dur, 620ms) var(--px-ease-out-expo) both; }
/* The gold sweep. Two passes, then gone — an infinite shimmer on every bar on
   screen is exactly the kind of always-on GPU work that cooked his phone during
   the long Shot Lab session. translateX only; the parent .progress clips it. */
@keyframes pxBarShine { from { transform: translateX(-130%); } to { transform: translateX(360%); } }
.px-bar-fill.px-bar-go::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(100deg, transparent 22%, rgba(255,238,190,.62) 50%, transparent 78%);
  animation: pxBarShine 1050ms var(--px-ease-in-out) 2 both;
}
/* Near-full bars breathe — the near-win tension the bible asks for, at 3s so it
   sits in the "ambient" band and never competes with a real interaction.
   Opt-in (PXMotion sets it only at >=90%) so it is never more than one bar. */
@keyframes pxBarPulse { 0%, 100% { transform: scaleX(1); } 50% { transform: scaleX(1.012); } }
.px-bar-fill.px-bar-near { animation: pxBarPulse var(--px-t-ambient) var(--px-ease-in-out) infinite; }

/* ---- v2.3 SKELETONS -------------------------------------------------------
   Bible §1: "every load state is a shimmering skeleton of the REAL layout —
   never a spinner, never blank."

   v1's .px-skel animates background-position, which repaints the element every
   frame. These animate an overlay's transform instead, so the sweep runs on the
   compositor and the skeleton box itself is painted exactly once.

   Sized in em/% off the host so a skeleton row lands at roughly the height of the
   real row it stands in for — the point is to reserve the space the content is
   about to take, not to add a second layout shift on top of the first. */
.px-skel-wrap { display: block; }
.px-skel-row { display: flex; align-items: center; gap: 12px; padding: 12px 0; }
.px-skel-row + .px-skel-row { border-top: 1px solid var(--border); }
.px-skel-col { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.px-skel-line, .px-skel-dot {
  position: relative; overflow: hidden;
  background: var(--track);
}
.px-skel-line { height: 10px; border-radius: 5px; }
.px-skel-line.px-w70 { width: 70%; }
.px-skel-line.px-w45 { width: 45%; }
.px-skel-line.px-w30 { width: 30%; }
.px-skel-dot { width: 34px; height: 34px; border-radius: 50%; flex: 0 0 auto; }
@keyframes pxSkelSweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.px-skel-line::after, .px-skel-dot::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, rgba(255,255,255,.09), transparent);
  animation: pxSkelSweep 1.15s linear infinite;
}
/* Real content cross-fades in over where the skeleton was, so the swap is a
   dissolve rather than a blink. */
@keyframes pxContentFade { from { opacity: 0; } to { opacity: 1; } }
.px-skel-done { animation: pxContentFade var(--px-t-ui) var(--px-ease-out-quint) both; }

/* ---- v2.4 THE v2 REDUCED-MOTION GUARD ------------------------------------
   Every v2 animation and transition, off in one place, last in the file so it
   wins. The page-entrance/exit pair is already force-disabled by the
   prefers-reduced-motion block in the section-transitions area above (that rule
   is !important, so it beats html.px-leaving's higher specificity). */
@media (prefers-reduced-motion: reduce) {
  .px-bar-fill.px-bar-go, .px-bar-fill.px-bar-near,
  .px-bar-fill.px-bar-go::after,
  .px-skel-line::after, .px-skel-dot::after,
  .px-skel-done { animation: none !important; }
  .px-tapfx, .px-tapfx.px-pressed { transition: none !important; }
  .px-tapfx.px-pressed, .px-tapfx.px-press-lg.px-pressed,
  .px-tapfx.px-press-sm.px-pressed { scale: 1 !important; }
  .px-bar-fill.px-bar-go::after { display: none; }
}
