    *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

    :root {
      --bg: #0a0a0a;
      --surface: #141416;
      --surface-2: #1c1c1c;
      --surface-3: #2a2a2a;
      --accent: #E10600;
      --accent-glow: rgba(225, 6, 0, 0.2);
      --text: #f0f0f0;
      /* #a0a0a0 (issue #91): ~7.6:1 on --bg, ~6.5:1 on --surface-2 — both
         comfortably above WCAG AA (4.5:1) on the lighter --surface-2 tiles
         the Runden-/Zeiten-Listen actually sit on, not just on solid black.
         The previous #777 was ~4.4:1 on --bg (already borderline) and
         ~3.8:1 on --surface-2 (below AA). */
      --text-muted: #a0a0a0;
      /* Reserved for purely decorative elements only (issue #91) — dashed
         borders, the ›-chevron affordance, empty-value placeholder text
         ("--.---"), the version footer. Real content (dates, counts,
         button/link labels) uses --text-muted or --text instead; a few
         spots here used to fail that rule (.time-item-date, the rides=0
         empty state) and were fixed as part of this ticket. Deliberately
         NOT raised to an AA-passing value itself — anything that needs to
         actually be read shouldn't be using this token at all. */
      --text-dim: #444;
      --green: #00C853;
      --safe-top: env(safe-area-inset-top, 0px);
      --safe-bottom: env(safe-area-inset-bottom, 0px);
      /* Only ever consumed via --safe-side below (issue #92) — the app is
         portrait-only everywhere EXCEPT the fullscreen card view's
         force-landscape mode, which fakes landscape by CSS-rotating a
         portrait-sized box rather than actually changing the OS/viewport
         orientation. env(safe-area-inset-*) reflects the PHYSICAL device
         orientation, independent of that CSS rotation, so on a notched
         phone turned sideways the notch can land on either physical left or
         right depending on which way the user rotated it — not reliably
         knowable from CSS alone without a real device to verify the exact
         rotated geometry. */
      --safe-left: env(safe-area-inset-left, 0px);
      --safe-right: env(safe-area-inset-right, 0px);
      /* The larger of the two — used as extra clearance on every fullscreen
         close/overlay control regardless of which physical side the notch
         ends up on after rotation. Deliberately conservative (may add a
         little more padding than one specific rotation direction strictly
         needs) rather than guessing the exact rotated geometry wrong; on any
         non-notched device both insets are 0px, so this is a no-op there. */
      --safe-side: max(var(--safe-left), var(--safe-right));
      /* Mini type scale (issue #91) — replaces 30 scattered 0.6-0.75rem
         literals. --fs-xs is also the floor: nothing that renders real
         content goes below it. Not the full token system #93 covers (that
         also spans spacing/color) — just the font-size stops this ticket's
         own contrast/legibility fixes needed. Issue #205's own mechanical
         sweep also folded every 0.8rem literal (8x, equidistant between
         the two stops here) into --fs-xs rather than adding a third,
         near-indistinguishable tier between --fs-xs/--fs-sm. */
      --fs-xs: 0.75rem;
      --fs-sm: 0.85rem;
      --fs-base: 1rem;
      /* Spacing scale (issue #93) — matches the most common margin/padding/
         gap values already in use throughout this file (4/8/12/16/20/24px
         accounted for ~90% of all such declarations before this ticket).
         Applied to a representative set of foundational layout spots
         (header, overlay panels, dialog headers, the fullscreen safe-area
         offsets) to establish the scale, not swept across every declaration
         in the file — see CLAUDE.md for why a full mechanical migration was
         deliberately scoped out of this ticket. */
      --space-2xs: 2px;
      --space-xs: 4px;
      --space-sm: 8px;
      --space-md: 12px;
      --space-lg: 16px;
      --space-xl: 20px;
      --space-2xl: 24px;

      /* Redesign "Timing Tower" tokens (issue #130, epic #141) — additive
         only: existing tokens above (--surface-2/-3, the base --accent-glow)
         stay as-is so shipped, not-yet-redesigned screens are unaffected.
         Each screen's own redesign issue migrates its specific selectors
         from the old tokens to these as it's rebuilt, rather than a single
         blind rename here regressing every screen at once. */
      --raised: #1c1c1e;
      --border: #26262a;
      --border-strong: #3a3a3c;
      --accent-lt: #FF453A;
      /* Stronger than the base --accent-glow (0.2) — used by the redesign's
         primary-button/card glows. A new token rather than raising
         --accent-glow in place, for the same additive-only reason above. */
      --accent-glow-strong: rgba(225, 6, 0, 0.35);
      --radius-card: 16px;
      --radius-btn: 14px;
      --radius-dialog: 20px;
      --shadow-btn-primary: 0 8px 28px var(--accent-glow-strong);
      --shadow-dialog: 0 24px 60px rgba(0, 0, 0, 0.7);
      /* CSS consolidation (issue #205) */
      --surface-badge: #2c2c2e; /* the P-badge "dampened grey" fill, previously hardcoded 7x */
      --accent-tint: rgba(225, 6, 0, 0.07); /* the subtle best-lap row tint (Fahrt-Detail "Alle Runden") */
      --green-tint: rgba(0, 200, 83, 0.15); /* translucent-green PB/BEST badges */
    }

    /* Redesign "Timing Tower" fonts (issue #130, epic #141) — self-hosted
       WOFF2 (latin subset only; covers German incl. ÄÖÜäöüß, which all sit
       within U+0000-00FF), precached like any other shell asset for full
       offline support. This is a deliberate, documented exception to the
       "system fonts only" convention (see CLAUDE.md), same category as the
       vendored ECharts bundle — not a CDN dependency, no CSP change needed
       (default-src 'self' already covers font-src). Titillium Web has no
       true 900-italic face upstream; headline elements that need it
       (font-weight:900 + font-style:italic) get the browser's synthetic
       oblique applied to the real upright 900 face below, which is accurate
       enough for this typeface's fairly geometric letterforms — not worth a
       fake extra @font-face entry mapping a different real weight to it. */
    @font-face {
      font-family: 'Titillium Web';
      font-style: normal;
      font-weight: 400;
      font-display: swap;
      src: url('../fonts/titillium-web-400.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Titillium Web';
      font-style: normal;
      font-weight: 600;
      font-display: swap;
      src: url('../fonts/titillium-web-600.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Titillium Web';
      font-style: normal;
      font-weight: 700;
      font-display: swap;
      src: url('../fonts/titillium-web-700.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Titillium Web';
      font-style: normal;
      font-weight: 900;
      font-display: swap;
      src: url('../fonts/titillium-web-900.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Saira Condensed';
      font-style: normal;
      font-weight: 600;
      font-display: swap;
      src: url('../fonts/saira-condensed-600.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Saira Condensed';
      font-style: normal;
      font-weight: 700;
      font-display: swap;
      src: url('../fonts/saira-condensed-700.woff2') format('woff2');
    }
    @font-face {
      font-family: 'Saira Condensed';
      font-style: normal;
      font-weight: 900;
      font-display: swap;
      src: url('../fonts/saira-condensed-900.woff2') format('woff2');
    }

    /* Convention (issue #130): every NUMBER display (times, ride counts,
       P-badges, card numbers) uses this class instead of repeating the
       font-family declaration — and the redesign's rule that a time's
       decimal point is always --accent-colored via a wrapping
       <span class="dot">. Individual screens adopt this as they're
       redesigned (see epic #141); not applied to any existing number
       display yet by this issue itself. */
    .font-numeric {
      font-family: 'Saira Condensed', 'Titillium Web', system-ui, sans-serif;
    }
    .font-numeric .dot {
      color: var(--accent);
    }

    /* CSS-only Zielflaggen-Chip (issue #130) — a checkerboard rendered as two
       conic quadrants per repeat cell, no image asset. Sized per usage site
       (e.g. 22px in the redesigned header, per epic #141's per-screen
       issues); this class only sets the pattern + shape, not a fixed size. */
    .flag-chip {
      border-radius: 4px;
      background:
        conic-gradient(#fff 90deg, transparent 90deg 180deg, #fff 180deg 270deg, transparent 270deg 360deg)
        0 0 / 50% 50%;
    }

    /* issue #104: the monochrome line-art icon set (see index.html's own
       sprite comment for source/license) — sized to sit inline with text at
       the current font-size (1em), colored via currentColor so it always
       matches whatever text color surrounds it (including the a11y contrast
       fixes from #91) with zero extra CSS per usage site. vertical-align
       nudges it to align with adjacent text baselines instead of sitting
       high (the SVG's own default inline baseline). */
    .icon {
      width: 1em;
      height: 1em;
      vertical-align: -0.125em;
      flex-shrink: 0;
    }

    /* The <symbol> definitions themselves are never rendered directly — only
       via <use> at each usage site — so this whole element stays out of
       layout/paint (a CSS class, not an inline style=, per this project's
       CSP/no-inline-styles convention). */
    .icon-sprite {
      display: none;
    }

    html, body {
      height: 100%;
      overflow: hidden;
      /* Without this, Chrome/Android's own pull-to-refresh would reload the
         page on the exact same downward drag js/pull-refresh.js (#42)
         listens for, firing before our gesture's preventDefault() ever gets
         a chance. Scroll containers inside overlays (e.g. .fullscreen-track
         above) keep their own overscroll-behavior and are unaffected. */
      overscroll-behavior-y: none;
      background: var(--bg);
      color: var(--text);
      font-family: 'Titillium Web', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
      -webkit-tap-highlight-color: transparent;
      /* Kept as the app-wide default (issue #92) rather than flipped to
         "interactive elements only" — the carousel/fullscreen swipe and
         pull-to-refresh drag gestures both pan over real text (profile
         names, card numbers), and a mid-swipe text-selection drag instead of
         a scroll would be a real regression. Real value displays a user
         would actually want to copy (card number, best times) get an
         explicit user-select: text override below instead — see "SELECTABLE
         VALUE DISPLAYS". */
      user-select: none;
      -webkit-user-select: none;
    }

    body {
      display: flex;
      flex-direction: column;
      padding-top: var(--safe-top);
      padding-bottom: var(--safe-bottom);
      background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.02) 1px, transparent 0);
      background-size: 4px 4px;
    }

    /* Covers the gap before firstRender()/router.init() run (store.init()'s
       IndexedDB open), during which the browser would otherwise already
       paint the bare card carousel underneath — a different mechanism than
       the (now-removed, issue #194) overlay fade-transition suppression
       this rule used to also carry: this class no longer needs to touch
       transitions at all, since routed overlays have none anymore. app.js
       removes this class only after router.init() has applied the restored
       route (or immediately on the boot-error path). */
    body.booting .main-content { display: none; }

    /* ─── HEADER ─── */
    header {
      flex-shrink: 0;
      padding: 10px 20px 0;
      display: flex;
      align-items: center;
      justify-content: space-between;
    }

    /* Redesign "Timing Tower" header brand (issue #131, epic #141): the
       kart-icon badge + wordmark, grouped so the home button on the other
       side of the header's own space-between layout is unaffected. Design
       review fix: the style guide (5g) is explicit that the kart icon is
       the app's brand mark wherever a header/logo appears, and the
       checkered flag is reserved for race actions only (the LET'S RACE
       button) — this slot originally (mis-specified in issue #131's own
       text) used a flag-chip instead. */
    .header-brand {
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      min-width: 0;
    }

    .header-brand-icon {
      width: 26px;
      height: 26px;
      border-radius: 7px;
      border: 1px solid var(--border-strong);
      flex-shrink: 0;
    }

    .header-title {
      font-weight: 900;
      font-style: italic;
      font-size: 1.125rem;
      letter-spacing: 0.5px;
      text-transform: uppercase;
      line-height: 1;
      color: var(--text);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .header-btn {
      width: 36px;
      height: 36px;
      border-radius: 50%;
      /* issue #205: last redesign straggler among the round buttons — every
         sibling (.pitstop-close etc.) already uses --border-strong/--raised. */
      border: 1px solid var(--border-strong);
      background: var(--raised);
      color: var(--text);
      font-size: 1rem;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s, background 0.15s;
    }
    .header-btn:active {
      transform: scale(0.92);
      background: var(--surface-2);
    }

    /* Redesign "Timing Tower" kerb-stripe (issue #130) — a diagonal racing
       kerb instead of the previous checkerboard; kept as the same class name
       and slot (under every screen/overlay header) so no call site changes,
       only the pattern. */
    .checkered-stripe {
      flex-shrink: 0;
      height: 5px;
      margin-top: 8px;
      background: repeating-linear-gradient(-45deg, var(--accent) 0 10px, var(--bg) 10px 20px);
    }

    /* ─── PULL-TO-REFRESH (#42) ─── */
    .pull-refresh-indicator {
      position: fixed;
      top: calc(var(--safe-top) + 6px);
      left: 50%;
      width: 32px;
      height: 32px;
      display: flex;
      align-items: center;
      justify-content: center;
      transform: translate(-50%, 0);
      opacity: 0;
      pointer-events: none;
      z-index: 50;
    }
    .pull-refresh-indicator.visible { opacity: 1; }
    /* issue #156: only applied for the release/spring-back moment (added and
       removed in js/pull-refresh.js) — never during an active drag, where
       every touchmove already assigns a fresh transform value faster than
       any transition could keep up with; the global @media
       (prefers-reduced-motion) block elsewhere in this file already
       disables this transition too. */
    .pull-refresh-indicator.pull-refresh-releasing,
    .main-content.pull-refresh-releasing {
      transition: transform 0.25s cubic-bezier(0.2, 0.9, 0.3, 1);
    }

    .pull-refresh-spinner {
      width: 22px;
      height: 22px;
      border-radius: 50%;
      border: 2px solid var(--surface-3);
      border-top-color: var(--accent);
    }
    .pull-refresh-indicator.spinning .pull-refresh-spinner {
      animation: pullRefreshSpin 0.7s linear infinite;
    }
    @keyframes pullRefreshSpin {
      to { transform: rotate(360deg); }
    }

    /* ─── MAIN CONTENT ─── */
    .main-content {
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
      overflow: hidden;
    }

    .controls-section {
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
    }

    /* ─── CAROUSEL ─── */
    .carousel-section {
      flex-shrink: 0;
      padding: 16px 0 0;
    }

    .carousel {
      display: flex;
      overflow-x: auto;
      scroll-snap-type: x mandatory;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      padding: 0 4px;
      gap: 4px;
    }
    .carousel::-webkit-scrollbar { display: none; }

    .card-slide {
      flex: 0 0 calc(100% - 8px);
      scroll-snap-align: center;
      scroll-snap-stop: always;
      position: relative;
      border-radius: 12px;
      overflow: hidden;
      cursor: pointer;
      transition: transform 0.15s;
    }
    .card-slide:active { transform: scale(0.98); }

    .card-img {
      width: 100%;
      display: block;
      border-radius: 12px;
      box-shadow:
        0 8px 32px rgba(0,0,0,0.6),
        0 0 0 1px rgba(255,255,255,0.06),
        0 0 60px -10px var(--accent-glow);
    }

    /* A photo card with no blob: URL yet (image still loading, or missing) —
       a plain box in the same aspect ratio the real photo will fill in at. */
    .card-img-placeholder {
      background: var(--surface-2);
      aspect-ratio: 16/10;
    }

    /* issue #108: a profile whose cardType this app version doesn't
       recognize (e.g. one introduced by a newer version, synced/backed-up
       down here) — an explicit notice, never a blank box that could be
       mistaken for a photo card whose blob just hasn't loaded yet. */
    .card-unsupported {
      width: 100%;
      aspect-ratio: 16/10;
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      padding: var(--space-lg);
      border-radius: 12px;
      background: var(--surface-2);
      color: var(--text-muted);
      font-size: var(--fs-sm);
    }

    /* ─── CARD OVERLAY BADGES ─── */
    .card-badge-rides {
      position: absolute;
      top: 10px;
      left: 10px;
      background: rgba(0, 0, 0, 0.75);
      color: var(--text);
      font-weight: 900;
      font-size: 0.95rem;
      padding: 5px 14px;
      border-radius: 20px;
      backdrop-filter: blur(8px);
      -webkit-backdrop-filter: blur(8px);
      z-index: 2;
      pointer-events: none;
      min-width: 40px;
      text-align: center;
      border: 1px solid rgba(255,255,255,0.1);
    }

    .card-badge-rides-zero {
      opacity: 0.4;
    }

    .carousel-dots {
      display: flex;
      justify-content: center;
      gap: 8px;
      padding: 12px 0 0;
    }

    /* Scoped to .carousel-dots (issue #137) — a bare ".dot" would otherwise
       collide with the redesign's OWN ".dot" convention (issue #130's "wrap
       a time's decimal point in <span class='dot'>", first actually used by
       this ticket's time-input dialog): the carousel pagination pip's
       width/height/border-radius/background would leak onto a numeric
       decimal point (same class name, and higher specificity than the
       color-only ".font-numeric .dot" rule, so it wouldn't just lose to it),
       turning the "." into a small circle. Also fixes the same latent
       collision in issue #131's own resting-pill rule (merged bare ".dot").
       */
    .carousel-dots .dot {
      width: 8px;
      height: 4px;
      border-radius: 2px;
      background: var(--border-strong);
      transition: all 0.3s;
    }
    .carousel-dots .dot.active {
      background: var(--accent);
      box-shadow: 0 0 8px var(--accent-glow);
      width: 22px;
      border-radius: 2px;
    }

    /* ─── TELEMETRY CARD (issue #131, redesign "Timing Tower", epic #141) ───
       Replaces the old plain profile-info block: a bordered surface card
       grouping the profile identity, the Fahrten/Bestzeit stat pair, and the
       Analytics teaser — this is also the #103 "Analytics reachable with one
       tap from the card view" entry point (the teaser button below). IDs on
       #profileName/#ridesCount/#profileBesttime/#profileBesttimeValue are
       unchanged from the pre-redesign markup so ui.js's render()/animateCount()
       need no changes beyond adding the new teaser/P-badge content. */
    .telemetry-card {
      flex-shrink: 0;
      margin: 12px 20px 0;
      padding: var(--space-lg);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-card);
    }

    .telemetry-profile-row {
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      min-width: 0;
    }

    /* Shared "P-badge" component (issue #205): P1 in solid --accent + white,
       every other position in a dampened grey fill (--surface-badge) — the
       same front-row convention used across 6 places (main card view,
       Fahrten-Historie rows, Fahrt-Detail, Analytics kart ranking, Gesamt-
       Analytics Hausrangliste, Home list). Each site keeps its OWN class
       name (JS targets them individually, and existing tests assert
       against them) rather than a single renamed class everywhere — this
       comma selector is what makes them ONE actual component instead of 6
       independently-maintained copies of the same 7 declarations.
       .p-badge/.p-badge-lead are also included so either name is usable
       directly for a future site. Modifier class name is uniformly
       `<own-class>-lead` (issue #202's positionBadge() JS helper already
       produces exactly this for the 4 ui.js sites; the other two
       — analytics-charts.js's kart ranking and overall-analytics.js's
       Hausrangliste — used to chain a bare .fastest/.lead class instead and
       were renamed to match, issue #205).
       Review fix: 3 of the 6 (.telemetry-p-badge, .ride-detail-position-
       badge, .home-row-badge) were actually still at a literal 0.9375rem
       (15px), not var(--fs-sm) (13.6px) like the other 3 — this unifies all
       6 onto --fs-sm, a genuine ~10% size reduction on the main card view's
       own P-badge, Home's list rows, and Fahrt-Detail's position badge, not
       just a copy/paste of already-identical rules. .home-row-badge's own
       padding (2px 9px, the other 5 all 2px 8px) is unified the same way.
       Both changes deliberate, not accidental — visually verified via
       screenshots of the affected screens before merging. */
    .telemetry-p-badge,
    .history-tt-position,
    .ride-detail-position-badge,
    .analytics-kart-row-badge,
    .overall-standings-badge,
    .home-row-badge,
    .p-badge {
      flex-shrink: 0;
      min-width: 26px;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 2px 8px;
      border-radius: 5px;
      background: var(--surface-badge);
      color: var(--text);
      font-weight: 900;
      font-style: italic;
      font-size: var(--fs-sm);
    }
    .telemetry-p-badge-lead,
    .history-tt-position-lead,
    .ride-detail-position-badge-lead,
    .analytics-kart-row-badge-lead,
    .overall-standings-badge-lead,
    .home-row-badge-lead,
    .p-badge-lead {
      background: var(--accent);
      color: #fff;
    }

    .telemetry-name {
      flex: 1;
      min-width: 0;
      font-weight: 700;
      font-size: 1.1875rem; /* 19px */
      letter-spacing: 1px;
      text-transform: uppercase;
      color: var(--text);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .telemetry-stats-row {
      display: flex;
      align-items: stretch;
      margin-top: var(--space-lg);
      padding-top: var(--space-md);
      border-top: 1px solid var(--border);
    }

    .telemetry-stat {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 2px;
      background: none;
      border: none;
      font: inherit;
      color: inherit;
      cursor: default;
      -webkit-tap-highlight-color: transparent;
    }

    .telemetry-stat-divider {
      width: 1px;
      align-self: stretch;
      margin: 2px 0;
      background: var(--border);
    }

    .telemetry-stat-value {
      font-weight: 900;
      font-size: 2.625rem;
      line-height: 1;
      color: var(--text);
      font-variant-numeric: tabular-nums;
    }
    .telemetry-stat-value.pop-down {
      animation: popDown 0.35s ease-out;
    }
    .telemetry-stat-value.pop-up {
      animation: popUp 0.35s ease-out;
    }

    @keyframes popDown {
      0% { transform: translateY(0); opacity: 1; }
      40% { transform: translateY(-30px); opacity: 0; }
      41% { transform: translateY(30px); opacity: 0; }
      100% { transform: translateY(0); opacity: 1; }
    }
    @keyframes popUp {
      0% { transform: translateY(0); opacity: 1; }
      40% { transform: translateY(30px); opacity: 0; }
      41% { transform: translateY(-30px); opacity: 0; }
      100% { transform: translateY(0); opacity: 1; }
    }

    .telemetry-stat-label {
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 2px;
      color: var(--text-muted);
      text-transform: uppercase;
    }

    .rides-zero .telemetry-stat-rides .telemetry-stat-value,
    .rides-zero .telemetry-stat-rides .telemetry-stat-label {
      color: var(--text-muted);
    }

    .profile-besttime {
      display: none;
      cursor: pointer;
    }
    .profile-besttime.visible { display: flex; }
    .profile-besttime:active .telemetry-stat-value { opacity: 0.6; }

    /* No best time yet (issue #131) — collapse to a single "Fahrten" column
       rather than showing an empty divider/column next to nothing. */
    .telemetry-stats-row-single .telemetry-stat-divider,
    .telemetry-stats-row-single .profile-besttime {
      display: none;
    }

    /* Analytics-Teaser (issues #131/#103): tappable mini-sparkline + trend
       summary — the single-tap Analytics entry point from the card view the
       Pit Stop button (#pitstopStatsBtn) already offered too. A real
       <button>, min. 44px tall, sitting fully below the carousel/card tap
       area so it can't steal a carousel swipe or the pull-to-refresh
       gesture (js/pull-refresh.js only reacts to a genuinely vertical
       downward drag, so a plain tap here is unaffected either way). */
    .telemetry-teaser {
      width: 100%;
      min-height: 44px;
      margin-top: var(--space-md);
      padding: var(--space-sm) var(--space-md);
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      background: var(--raised); /* issue #205: was #17171a, a near-identical one-off 2 points from this token */
      border: 1px solid var(--border);
      border-radius: 10px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .telemetry-teaser:active { transform: scale(0.98); }

    .telemetry-teaser-sparkline {
      flex-shrink: 0;
      display: flex;
      align-items: flex-end;
      gap: 3px;
      height: 24px;
    }

    .telemetry-teaser-bar {
      width: 4px;
      border-radius: 2px;
      background: var(--border-strong);
    }
    .telemetry-teaser-bar-empty { height: 4px; background: var(--border); }
    .telemetry-teaser-bar-lvl1 { height: 6px; }
    .telemetry-teaser-bar-lvl2 { height: 11px; }
    .telemetry-teaser-bar-lvl3 { height: 16px; }
    .telemetry-teaser-bar-lvl4 { height: 20px; }
    .telemetry-teaser-bar-lvl5 { height: 24px; }
    /* The most recent Fahrt's own bar is always accent-colored (issue #131's
       "letzter --accent"), on top of whatever height level it also got. */
    .telemetry-teaser-bar-last { background: var(--accent); }

    .telemetry-teaser-trend {
      flex: 1;
      min-width: 0;
      font-size: var(--fs-sm);
      font-weight: 600;
      color: var(--text-muted);
      /* Design review fix: a real trend sentence (e.g. "0.830s langsamer
         über die letzten 5 Fahrten") doesn't fit on one line at this card's
         width the way the mockup's short example did — wrap to a second
         line instead of truncating it with an ellipsis; .telemetry-teaser's
         min-height (not a fixed height) already accommodates that. */
      text-align: left;
    }
    /* .analytics-trend-better/-worse/-flat (analytics.js's own Chart 1 trend
       text colors) are reused as-is here — each is already a standalone
       single-class selector (see "─── ANALYTICS ───" below), so applying one
       alongside .telemetry-teaser-trend just overrides its default muted
       color with the matching green/red/muted tone, no new color rule
       needed. */

    .telemetry-teaser-chevron {
      flex-shrink: 0;
      color: var(--accent);
      font-weight: 700;
    }

    /* ─── ACTION BUTTONS (issue #131) ─── */
    .action-section {
      flex-shrink: 0;
      padding: 16px 20px 0;
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    /* Split card-view layout for landscape orientation. Must come AFTER every
       base rule it overrides (issue #247) — @media doesn't raise specificity,
       so with equal specificity the LATER same-selector declaration in the
       cascade wins regardless of the media query; this block used to sit up
       near .main-content/.controls-section (the only two of its 7 overridden
       selectors whose own base rule happens to come before it), so 5 of its 7
       declarations (.carousel-section/.card-slide/.carousel-dots/
       .telemetry-card/.action-section, all redesigned after the block was
       originally written) were silently dead — the later base rule always
       won in real landscape rendering. */
    @media (orientation: landscape) {
      .main-content {
        flex-direction: row;
        align-items: stretch;
      }

      .carousel-section {
        width: 42%;
        flex-shrink: 0;
        display: flex;
        flex-direction: column;
        justify-content: center;
        padding: 8px 0 8px 16px;
      }

      .card-slide {
        flex: 0 0 100%;
      }

      .carousel-dots {
        padding-top: 6px;
      }

      .controls-section {
        justify-content: center;
        padding: 8px 16px;
      }

      .telemetry-card {
        margin: 0;
      }

      .action-section {
        padding: 10px 0 0;
      }
    }

    .race-btn {
      width: 100%;
      height: 54px;
      border: none;
      border-radius: var(--radius-btn);
      background: var(--accent);
      color: white;
      font-family: inherit;
      font-weight: 800;
      font-size: 1.15rem;
      letter-spacing: 5px;
      text-transform: uppercase;
      cursor: pointer;
      position: relative;
      overflow: hidden;
      transition: transform 0.1s, box-shadow 0.2s, opacity 0.3s;
      box-shadow: var(--shadow-btn-primary), inset 0 1px 0 rgba(255,255,255,0.15);
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
    }

    .race-btn::before {
      content: '';
      position: absolute;
      inset: 0;
      background: linear-gradient(135deg, rgba(255,255,255,0.12) 0%, transparent 50%);
      pointer-events: none;
    }

    .race-btn:active:not(:disabled) {
      transform: scale(0.97);
      box-shadow: 0 2px 10px var(--accent-glow-strong);
    }

    .race-btn:disabled {
      background: var(--surface-3);
      box-shadow: none;
      cursor: not-allowed;
      opacity: 0.4;
    }

    .race-btn-icon {
      display: flex;
      align-items: center;
    }
    .race-btn-flag-chip {
      width: 20px;
      height: 20px;
    }

    .pitstop-btn {
      width: 100%;
      height: 44px;
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-btn);
      background: var(--raised);
      color: var(--text);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 3px;
      text-transform: uppercase;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      transition: all 0.12s;
      -webkit-tap-highlight-color: transparent;
    }
    .pitstop-btn:active {
      background: var(--surface-2);
      border-color: var(--accent);
      transform: scale(0.97);
    }

    /* ─── PIT STOP PANEL ─── */
    /* Issue #194: navigation between routed views is deliberately instant —
       no cross-fade transition. closeEverything()'s fade-out of the old
       overlay used to overlap the new one's fade-in for ~250ms, letting the
       underlying card carousel show through both semi-transparent overlays.
       .active toggles opacity straight to 1/0 with no transition; the only
       animated view-change left is the profile-to-profile swipe (carousel/
       fullscreen scroll-snap) — a native scroll mechanic, untouched by this.
       Shared with .home-overlay/.create-overlay (issue #227 — previously
       three byte-identical copies; 9 other overlays in index.html already
       just use this class directly). .fullscreen-overlay stays its own rule
       (differs in z-index/background); .crop-overlay/.live-camera-overlay
       share their own separate identical pair further below. */
    .pitstop-overlay, .home-overlay, .create-overlay {
      position: fixed;
      inset: 0;
      z-index: 400;
      background: var(--bg);
      opacity: 0;
      pointer-events: none;
    }
    .pitstop-overlay.active, .home-overlay.active, .create-overlay.active {
      opacity: 1;
      pointer-events: auto;
    }

    /* .create-panel (Create dialog) composes onto this too (issue #205) —
       byte-identical to .overlay-panel, previously a full second copy. */
    .overlay-panel, .create-panel {
      width: 100%;
      height: 100%;
      padding: calc(var(--safe-top) + var(--space-lg)) var(--space-xl) calc(var(--safe-bottom) + var(--space-lg));
      display: flex;
      flex-direction: column;
      overflow-y: auto;
      -webkit-overflow-scrolling: touch;
      background-image: radial-gradient(circle at 1px 1px, rgba(255,255,255,0.02) 1px, transparent 0);
      background-size: 4px 4px;
    }

    .pitstop-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: var(--space-lg);
      flex-shrink: 0;
    }

    .pitstop-title {
      font-weight: 900;
      font-style: italic;
      font-size: 1.125rem;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--text);
    }

    .pitstop-close {
      width: 36px;
      height: 36px;
      border-radius: 50%;
      border: 1px solid var(--border-strong);
      background: var(--raised);
      color: var(--text);
      font-size: 1rem;
      font-weight: 700;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
    }
    .pitstop-close:active {
      transform: scale(0.9);
    }

    /* issue #138: the switcher is now a plain display (name editing moved to
       the Profil tab's own #pitstopNameInput below) — ‹/›-buttons flank a
       centered name + "N / M" position readout. */
    .pitstop-switcher {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-sm);
      flex-shrink: 0;
    }

    .pitstop-switcher-nav {
      width: 44px;
      height: 44px;
      border-radius: var(--radius-btn);
      border: 1px solid var(--border-strong);
      background: var(--raised);
      color: var(--text);
      font-size: 1.6rem;
      line-height: 1;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
      transition: all 0.12s;
    }
    .pitstop-switcher-nav:active {
      background: var(--surface-3);
      transform: scale(0.9);
    }
    .pitstop-switcher-nav:disabled {
      opacity: 0.15;
      cursor: not-allowed;
    }

    .pitstop-switcher-name-col {
      display: flex;
      flex-direction: column;
      align-items: center;
      min-width: 160px;
    }

    .pitstop-switcher-name {
      font-weight: 700;
      font-size: 1.1rem;
      letter-spacing: 2px;
      text-transform: uppercase;
      color: var(--text);
      max-width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .pitstop-switcher-position {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      letter-spacing: 1.5px;
      margin-top: 2px;
    }

    /* ─── PIT STOP TABS (issue #138) ─── */
    /* Shared "segmented control" component (issue #205): a full-width N-way
       toggle used by Pit Stop's Fahrten/Zeiten/Profil tabs here, Analytics'
       Tempo/Konstanz/Karts (.analytics-category-tabs below), and Create's
       Barcode/Foto (.create-segment, further down this file). Previously
       three near-identical but independently-drifted implementations
       (border ja/nein, --border vs --border-strong, a 12px literal instead
       of --radius-btn, active box-shadow ja/nein) — unified onto the tokens
       the newer two already agreed on. .pitstop-tabs keeps its own extra
       margin-top (its own layout context needs it; the other two don't). */
    .pitstop-tabs, .analytics-category-tabs, .create-segment {
      display: flex;
      gap: var(--space-2xs);
      background: var(--raised);
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-btn);
      padding: var(--space-2xs);
      margin-bottom: var(--space-lg);
      flex-shrink: 0;
    }
    .pitstop-tabs {
      margin-top: var(--space-lg);
    }
    .pitstop-tab, .analytics-category-tab, .create-segment button {
      flex: 1;
      padding: var(--space-sm) var(--space-md);
      border: none;
      border-radius: calc(var(--radius-btn) - 4px);
      background: none;
      color: var(--text-muted);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.15s, color 0.15s;
    }
    .pitstop-tab[aria-selected="true"],
    .analytics-category-tab.active,
    .create-segment button.active {
      background: var(--accent);
      color: #fff;
      box-shadow: var(--shadow-btn-primary);
    }

    .pitstop-tabpanel {
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
    }
    .pitstop-tabpanel[hidden] { display: none; }

    /* ─── PIT STOP CARDS (issue #138) ─── */
    .pitstop-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 14px;
      padding: var(--space-lg);
      margin-bottom: var(--space-lg);
      flex-shrink: 0;
    }
    .pitstop-card-title {
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 4px;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-bottom: 8px;
      flex-shrink: 0;
    }
    .pitstop-card .pitstop-history-list { flex: 1 1 auto; max-height: 260px; }


    .pitstop-count-display {
      text-align: center;
      margin: 4px 0 16px;
      flex-shrink: 0;
    }

    .pitstop-count {
      font-weight: 900;
      font-size: 4.5rem;
      line-height: 1;
      color: var(--text);
      font-variant-numeric: tabular-nums;
      letter-spacing: -1px;
    }

    .pitstop-count-label {
      display: block;
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 5px;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-top: -2px;
    }

    .pitstop-rides {
      display: flex;
      justify-content: center;
      gap: 10px;
      flex-shrink: 0;
      flex-wrap: wrap;
    }

    .pitstop-ride-btn {
      padding: 10px 20px;
      border: 1px solid var(--surface-3);
      border-radius: 10px;
      background: var(--surface-2);
      color: var(--text);
      font-weight: 700;
      font-size: 1rem;
      cursor: pointer;
      transition: all 0.12s;
      -webkit-tap-highlight-color: transparent;
      font-family: inherit;
    }
    .pitstop-ride-btn:active {
      transform: scale(0.93);
    }
    .pitstop-ride-btn.add:active {
      border-color: var(--green);
      color: var(--green);
    }
    .pitstop-ride-btn.remove:active {
      border-color: var(--accent);
      color: var(--accent);
    }

    .pitstop-title-link {
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-xs);
      letter-spacing: 1px;
      text-transform: none;
      color: var(--accent-lt);
      background: none;
      border: none;
      padding: 4px 0 4px 8px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .pitstop-title-link:active { opacity: 0.6; }

    /* Sortier-Chips (issue #140, Frame 4b) — redesign tokens (--raised/
       --border/--border-strong) rather than the older --surface-2/-3. */
    .history-sort-bar {
      display: flex;
      gap: var(--space-sm);
      margin-bottom: var(--space-md);
      flex-shrink: 0;
    }
    .history-sort-btn {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 4px;
      padding: 8px;
      border: 1px solid var(--border);
      border-radius: 999px;
      background: var(--raised);
      color: var(--text-muted);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-xs);
      letter-spacing: 0.5px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.15s, color 0.15s, border-color 0.15s;
    }
    .history-sort-btn.active {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
    }

    .history-list-full {
      flex: 1;
      min-height: 0;
      overflow-y: auto;
      scrollbar-width: thin;
      scrollbar-color: var(--surface-3) transparent;
    }
    /* issue #382: #analyticsCategoryFahrten holds TWO .history-list-full
       lists stacked in one flex-column panel, not one filling a whole
       dialog body the way #rideDetailLapsList (this class's other, still
       flex:1-correct user) does — the base rule's flex:1 would otherwise
       split the panel's available height evenly between them regardless of
       how many rows either actually has, leaving a large empty gap under a
       short list instead of both just sizing to their own content. */
    #analyticsCategoryFahrten .history-list-full {
      flex: 0 1 auto;
      max-height: 320px;
    }

    .pitstop-history-list {
      list-style: none;
      overflow-y: auto;
      flex: 1;
      min-height: 0;
      scrollbar-width: thin;
      scrollbar-color: var(--surface-3) transparent;
    }

    /* ─── BEST TIME (issue #139, Frame 4a) ─── */
    .pitstop-besttime-card {
      text-align: center;
      flex-shrink: 0;
    }
    .pitstop-besttime {
      font-weight: 900;
      font-size: 3.625rem;
      line-height: 1;
      color: var(--text);
      font-variant-numeric: tabular-nums;
      letter-spacing: -1px;
    }
    .pitstop-besttime.empty { color: var(--text-dim); }
    .pitstop-besttime-label {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 3px;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-top: 4px;
    }
    .pitstop-besttime-label::before {
      content: '';
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--accent);
      flex-shrink: 0;
    }

    /* Status row: connected/last-fetch (green) vs. not connected (dim) —
       same semantics as settings.js's own Gmail status row, just a second,
       more prominent surface for it per Frame 4a. */
    .pitstop-gmail-status {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin: var(--space-md) 0;
    }
    .pitstop-gmail-status-dot {
      width: 6px;
      height: 6px;
      border-radius: 50%;
      background: var(--green);
      flex-shrink: 0;
    }
    .pitstop-gmail-status-dot.offline { background: var(--text-dim); }

    .pitstop-fetch-times-btn {
      width: 100%;
      padding: var(--space-md);
      border: none;
      border-radius: var(--radius-btn);
      background: var(--accent);
      color: #fff;
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 0.5px;
      cursor: pointer;
      box-shadow: var(--shadow-btn-primary);
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
      margin-bottom: var(--space-lg);
    }
    .pitstop-fetch-times-btn:active { transform: scale(0.98); }
    .pitstop-fetch-times-btn:disabled { opacity: 0.4; cursor: not-allowed; }

    .pitstop-manual-time-link {
      display: block;
      width: 100%;
      margin-top: var(--space-md);
      padding: var(--space-sm) 0;
      border: none;
      background: none;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-sm);
      text-decoration: underline;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .pitstop-manual-time-link:active { color: var(--accent-lt); }

    .pitstop-times-list {
      list-style: none;
      flex-shrink: 0;
    }
    .time-item {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 0;
      border-bottom: 1px solid var(--border);
      animation: fadeSlideIn 0.3s ease-out;
    }
    .time-item:last-child { border-bottom: none; }
    .time-item-info {
      flex: 1;
      min-width: 0;
    }
    .time-item-value-row {
      display: flex;
      align-items: center;
      gap: 8px;
    }
    .time-item-value {
      font-weight: 700;
      font-size: 1.05rem;
    }
    .time-item-best-badge {
      flex-shrink: 0;
      padding: 2px 7px;
      border-radius: 5px;
      background: var(--accent);
      color: #fff;
      font-size: var(--fs-xs);
      font-weight: 900;
      font-style: italic;
    }
    .time-item-source {
      display: block;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    .time-item-date {
      flex-shrink: 0;
      font-size: var(--fs-sm);
      color: var(--text-muted);
    }
    .time-item-delete {
      flex-shrink: 0;
      width: 32px;
      height: 32px;
      border: 1px solid var(--border-strong);
      border-radius: 8px;
      background: var(--raised);
      color: var(--text-muted);
      font-size: 0.9rem;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
      transition: all 0.12s;
      font-family: inherit;
    }
    .time-item-delete:active {
      transform: scale(0.9);
      border-color: var(--accent);
      color: var(--accent);
    }
    .time-empty {
      color: var(--text-muted);
      font-size: var(--fs-xs);
      font-style: italic;
      padding: 8px 0;
      list-style: none;
    }

    /* ─── TIME INPUT DIALOG ─── */
    .timeinput-overlay {
      position: fixed;
      inset: 0;
      z-index: 650;
      background: rgba(0, 0, 0, 0.7);
      display: none;
      align-items: center;
      justify-content: center;
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
    }
    .timeinput-overlay.active { display: flex; }
    /* Shared dialog-card base (issue #137, epic #141) — every dialog card
       (this one, .confirm-dialog) inherits it via the same class in
       index.html, so a future dialog only needs to add "dialog-card" too to
       stay visually consistent, rather than repeating these four rules. */
    .dialog-card {
      background: var(--surface);
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-dialog);
      box-shadow: var(--shadow-dialog);
    }
    .timeinput-dialog {
      padding: 24px;
      margin: 0 20px;
      max-width: 340px;
      width: 100%;
      position: relative;
      cursor: text;
    }
    .timeinput-title {
      font-weight: 900;
      font-style: italic;
      font-size: 1.05rem;
      letter-spacing: 0.06em;
      text-transform: uppercase;
      color: var(--text);
      text-align: center;
      margin-bottom: 18px;
    }
    .timeinput-display {
      font-weight: 900;
      font-size: 3.4rem;
      line-height: 1;
      text-align: center;
      color: var(--text);
      font-variant-numeric: tabular-nums;
      margin-bottom: 6px;
    }
    /* Per-character coloring (issue #137) — a typed digit is full-strength
       --text, a still-empty placeholder position (the "·"/"-" characters
       formatPartial() pads with) is --border-strong, and the "." is
       --accent, same as .font-numeric's own .dot rule but written out here
       explicitly since ui.js assigns these classes itself rather than
       relying on that shared selector (this display isn't always showing a
       COMPLETE, valid number the way other .font-numeric displays are). */
    .timeinput-display .placeholder { color: var(--border-strong); }
    .timeinput-display .dot { color: var(--accent); }
    .timeinput-hint {
      text-align: center;
      font-size: var(--fs-xs);
      letter-spacing: 0.1em;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-bottom: 18px;
    }
    .timeinput-hidden-input {
      position: absolute;
      left: -9999px;
      top: 0;
      opacity: 0;
      width: 1px;
      height: 1px;
      font-size: 16px;
      border: none;
      background: transparent;
    }
    .timeinput-actions {
      display: flex;
      gap: 10px;
      cursor: default;
    }
    /* Shared "dialog-button" component (issue #205): the Abbrechen/primary-
       action pair every dialog uses (time input here, plus the delete/
       confirm dialog, the pdf417-decode confirm, and the crop cropper,
       further down this file). crop's/pdf417's own buttons predated the
       redesign token migration (--surface-2/--surface-3 background — pdf417
       additionally had its own literal 10px radius, crop was already on
       --radius-btn) — migrated onto the same --raised/--border-strong/
       --radius-btn tokens this pair already uses, rather than kept as an
       unexplained visual one-off (epic #141's own documented "each screen
       migrates its own selectors as it's rebuilt" applies here too).
       .dialog-btn/.dialog-btn-primary are included so either name is
       directly usable for a future dialog. Each pair's own flourish (crop's
       uppercase tracking, crop's/pdf417's own accept-button glow) stays a
       small residual override at that pair's own rule, not part of this
       shared base. */
    .timeinput-cancel, .timeinput-save,
    .confirm-cancel, .confirm-ok,
    .crop-cancel-btn, .crop-done-btn,
    .pdf417-confirm-decline, .pdf417-confirm-accept,
    .dialog-btn, .dialog-btn-primary {
      flex: 1;
      padding: 15px;
      border-radius: var(--radius-btn);
      font-family: inherit;
      font-weight: 700;
      font-size: 0.9rem;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s, opacity 0.2s, filter 0.1s;
    }
    .timeinput-cancel, .confirm-cancel, .crop-cancel-btn, .pdf417-confirm-decline, .dialog-btn {
      background: var(--raised);
      border: 1px solid var(--border-strong);
      color: var(--text);
    }
    .timeinput-save, .confirm-ok, .crop-done-btn, .pdf417-confirm-accept, .dialog-btn-primary {
      background: var(--accent);
      border: none;
      color: white;
    }
    .timeinput-save:disabled {
      opacity: 0.4;
      cursor: not-allowed;
      background: var(--border-strong);
    }
    .timeinput-cancel:active, .timeinput-save:active:not(:disabled),
    .confirm-cancel:active, .confirm-ok:active,
    .crop-cancel-btn:active, .crop-done-btn:active,
    .pdf417-confirm-decline:active, .pdf417-confirm-accept:active,
    .dialog-btn:active, .dialog-btn-primary:active {
      transform: scale(0.97);
      filter: brightness(0.9);
    }

    /* ─── HISTORY ─── */
    .history-item {
      padding: 7px 0;
      border-bottom: 1px solid var(--surface);
      font-size: var(--fs-sm);
      color: var(--text-muted);
      animation: fadeSlideIn 0.3s ease-out;
    }

    .history-item:last-child { border-bottom: none; }

    .history-item-row {
      display: flex;
      align-items: center;
      gap: 10px;
      width: 100%;
      /* Reset button chrome — this is a <div> for a plain manual ride, a
         <button> for a Gmail-synced one (tap anywhere to open its details). */
      background: none;
      border: none;
      padding: 0;
      font: inherit;
      text-align: left;
      color: inherit;
    }
    button.history-item-row {
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    button.history-item-row::after {
      content: '›';
      margin-left: 4px;
      color: var(--text-dim);
      flex-shrink: 0;
    }

    /* issue #138: a small muted flag-chip (see .flag-chip, issue #130)
       instead of the 🏁 emoji glyph this previously rendered as text. */
    .history-flag { width: 14px; height: 14px; opacity: 0.55; flex-shrink: 0; }

    /* Design frame 2b review fix (issue #184 follow-up): date+meta stacked,
       best-lap value in Saira Condensed on the right — this compact preview
       (Pit Stop's own "Letzte Fahrten", #historyList) was still the
       pre-redesign flat "flag + date + muted best-lap text" line; brought in
       line with historyItemFull()'s own meta-building convention (just
       without that page's date-tile/P-badge/PB-badge, which frame 2b's own
       compact row doesn't show either). */
    .history-item-info {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
    }
    .history-item-date {
      font-size: 0.9375rem;
      font-weight: 600;
      color: var(--text);
    }
    .history-item-meta {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 1px;
    }
    .history-item-value {
      flex-shrink: 0;
      font-size: 1.0625rem;
      font-weight: 700;
      color: var(--text);
    }

    /* ─── FAHRTEN-HISTORIE full page, "Timing Tower" row (issue #140) ───
       Deliberately its own class set (history-tt-*), not a restyle of
       .history-item-row above in place — that row is shared with Pit Stop's
       own compact "Letzte Fahrten" preview (#historyList), whose redesign is
       a separate, not-yet-built issue (#138); this scopes the new look to
       #rideHistoryList only. */
    .history-tt-row {
      display: flex;
      align-items: center;
      gap: var(--space-md);
      width: 100%;
      background: none;
      border: none;
      padding: var(--space-sm) 0;
      font: inherit;
      text-align: left;
      color: inherit;
    }
    button.history-tt-row {
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .history-tt-date {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 36px;
      flex-shrink: 0;
    }
    .history-tt-day {
      font-size: 19px;
      font-weight: 900;
      line-height: 1;
      color: var(--text);
    }
    .history-tt-month {
      font-size: var(--fs-xs); /* issue #205: was a sub-floor 10px literal */
      font-weight: 700;
      letter-spacing: 0.5px;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-top: 2px;
    }
    .history-tt-divider {
      align-self: stretch;
      width: 1px;
      background: var(--border);
      flex-shrink: 0;
    }
    .history-tt-main {
      flex: 1;
      min-width: 0;
    }
    .history-tt-best-line {
      display: flex;
      align-items: baseline;
      gap: var(--space-xs);
    }
    .history-tt-best {
      font-size: 1.1rem;
      font-weight: 700;
      color: var(--text);
    }
    /* Shared with .lap-tag-best further below (issue #227 — an identical
       8-declaration block, previously two independently-ordered copies). */
    .history-tt-pb-badge, .lap-tag-best {
      font-size: var(--fs-xs); /* issue #205: was a sub-floor 10px literal */
      font-weight: 700;
      letter-spacing: 0.06em;
      color: var(--green);
      background: var(--green-tint);
      padding: 2px 6px;
      border-radius: 4px;
      flex-shrink: 0;
    }
    .history-tt-meta {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    /* issue #386: the row's own source (Gmail vs manual), right-aligned as
       an icon instead of appended meta text — the wrapping span carries the
       actual aria-label (see historyItemFull()'s own comment); this is
       purely the icon's visual sizing/color. */
    .history-tt-source {
      display: flex;
      align-items: center;
      flex-shrink: 0;
    }
    .history-tt-source-icon {
      color: var(--text-dim);
      width: 16px;
      height: 16px;
    }
    /* .history-tt-position's own visual rules live in the shared P-badge
       component near .telemetry-p-badge (issue #205). */
    .history-tt-chevron {
      color: var(--text-dim);
      flex-shrink: 0;
    }
    /* issue #385: a standalone manual times[] entry's own delete affordance,
       merged into this row design in place of the chevron slot — same
       visual treatment as .time-item-delete (Pit Stop's own compact preview
       list), sized down slightly to sit comfortably in this denser row. */
    .history-tt-delete {
      flex-shrink: 0;
      width: 28px;
      height: 28px;
      border: 1px solid var(--border-strong);
      border-radius: 8px;
      background: var(--raised);
      color: var(--text-muted);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
      transition: all 0.12s;
      font-family: inherit;
    }
    .history-tt-delete:active {
      transform: scale(0.9);
      border-color: var(--accent);
      color: var(--accent);
    }

    /* Runden list — used on the Fahrt-Details page ("Alle Runden", issue
       #140/Frame 4c). Design frame review fix (issue #184 follow-up): a
       single-column list of plain rows (number · time · delta-or-BEST-badge),
       the best lap only a subtle rgba(225,6,0,.07) row tint — not the
       previous 2-column grid of individually-boxed, solid-fill-glow tiles. */
    .history-laps-list {
      list-style: none;
      margin-top: 8px;
      padding-left: 0;
      display: flex;
      flex-direction: column;
    }
    .history-laps-list li {
      display: flex;
      align-items: center;
      gap: var(--space-md);
      padding: 9px 0;
      border-top: 1px solid var(--border);
      font-variant-numeric: tabular-nums;
    }
    .history-laps-list li:first-child { border-top: none; }
    .history-laps-list li.history-lap-best {
      background: var(--accent-tint);
    }
    .lap-number {
      flex-shrink: 0;
      width: 24px;
      font-size: var(--fs-sm);
      font-weight: 700;
      color: var(--text-muted);
    }
    .history-lap-best .lap-number { color: var(--accent); }
    .lap-time {
      flex: 1;
      font-size: 1rem;
      font-weight: 700;
      color: var(--text-muted);
    }
    .history-lap-best .lap-time { color: var(--text); }
    .lap-tag-delta {
      flex-shrink: 0;
      font-size: var(--fs-xs);
      font-weight: 600;
      color: var(--text-muted);
    }
    /* .lap-tag-best's own rules live in the shared .history-tt-pb-badge rule
       above (issue #227). */

    /* ─── FAHRT-DETAILS header (issue #140, Frame 4c) ─── */
    .ride-detail-header {
      text-align: center;
      margin-bottom: 16px;
      flex-shrink: 0;
    }
    .ride-detail-date { font-size: var(--fs-sm); color: var(--text-muted); }
    .ride-detail-meta {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    .ride-detail-best-row {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-md);
      margin-top: var(--space-sm);
    }
    .ride-detail-best-col {
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    .ride-detail-best-label {
      font-size: var(--fs-xs);
      font-weight: 700;
      letter-spacing: 2px;
      text-transform: uppercase;
      color: var(--text-muted);
    }
    .ride-detail-best { font-size: 1.6rem; font-weight: 900; color: var(--accent); }
    /* .ride-detail-position-badge's own visual rules live in the shared
       P-badge component near .telemetry-p-badge (issue #205) — this used to
       be sized/rounded slightly differently (padding 4px 10px, radius 6px,
       no min-width) from its 5 siblings; normalized along with them rather
       than kept as an unexplained one-off. */

    .ride-detail-chart-wrap {
      flex-shrink: 0;
      margin-bottom: 12px;
    }
    .ride-detail-chart {
      width: 100%;
      height: 200px;
    }
    .ride-detail-heat-wrap {
      flex-shrink: 0;
      margin-top: 12px;
    }
    .ride-detail-heat-source {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin: -2px 0 8px;
    }
    /* Column order/widths (issue #140): POS/FAHRER/BESTE/KART, scoped to
       this specific table rather than changing .analytics-leaderboard's own
       base rules, which Analytics' Chart 7 (feldweite Bestenliste) also
       uses — that chart's own redesign is a separate issue (#132). Likewise
       the own-row highlight color below is a scoped override, not a change
       to the shared .analytics-leaderboard-mine rule. */
    #rideDetailHeatTable .analytics-leaderboard { table-layout: fixed; }
    #rideDetailHeatTable .analytics-leaderboard th:nth-child(1),
    #rideDetailHeatTable .analytics-leaderboard td:nth-child(1) { width: 44px; }
    #rideDetailHeatTable .analytics-leaderboard th:nth-child(3),
    #rideDetailHeatTable .analytics-leaderboard td:nth-child(3) { width: 76px; }
    /* GAP column (issue #189) — one char wider than BESTE for the '+'. */
    #rideDetailHeatTable .analytics-leaderboard th:nth-child(4),
    #rideDetailHeatTable .analytics-leaderboard td:nth-child(4) { width: 68px; }
    #rideDetailHeatTable .analytics-leaderboard th:nth-child(5),
    #rideDetailHeatTable .analytics-leaderboard td:nth-child(5) { width: 52px; }
    #rideDetailHeatTable .analytics-leaderboard-mine { background: var(--accent-tint); }

    /* .history-empty: genuinely no Fahrten recorded at all.
       .history-more (issue #202): the "+ N weitere Fahrten" overflow row in a
       LIMITED (non-full) list — a distinct semantic, previously misreusing
       .history-empty just because the visual style happened to match. */
    .history-empty,
    .history-more {
      color: var(--text-muted);
      font-size: var(--fs-xs);
      font-style: italic;
      padding: 10px 0;
      list-style: none;
    }

    /* ─── ANALYTICS (issue #49; regrouped into 3 Kategorien by issue #132) ─── */
    .analytics-content {
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
    }

    /* Kategorie-Segment (issue #132) — "Tempo | Konstanz | Karts", a
       full-width 3-way segmented control above the (unchanged) Zeitraum-
       Filter. Styled distinctly from .analytics-filter-chip's pill shape
       below it, so the two levels of filtering read as clearly separate.
       Its own base rules live in the shared segmented-control component
       near .pitstop-tabs (issue #205). */

    .analytics-category {
      display: flex;
      flex-direction: column;
      flex: 1;
      min-height: 0;
    }

    /* Karts category (Design-Frame 3b) — hero card for the profile's own
       fastest kart. */
    .analytics-kart-hero {
      display: flex;
      align-items: center;
      gap: var(--space-lg);
      background: var(--raised);
      border: 1px solid var(--border);
      border-radius: var(--radius-card);
      padding: var(--space-lg);
      margin-bottom: var(--space-lg);
      flex-shrink: 0;
    }
    .analytics-kart-hero-badge {
      flex-shrink: 0;
      padding: 8px 14px;
      border-radius: 10px;
      background: var(--accent);
      color: #fff;
      font-weight: 900;
      font-style: italic;
      font-size: 1.625rem;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .analytics-kart-hero-label {
      font-size: var(--fs-xs);
      text-transform: uppercase;
      letter-spacing: 0.5px;
      color: var(--text-muted);
    }
    .analytics-kart-hero-time {
      font-size: 1.6rem;
      font-weight: 800;
      color: var(--text);
      margin-top: 2px;
    }
    .analytics-kart-hero-rides {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }

    /* Bestenliste pro Kart (issue #132) — a progress-bar DOM-Liste replacing
       the former Chart 6 ECharts bar chart. */
    .analytics-kart-list-wrap {
      flex-shrink: 0;
    }
    .analytics-kart-list {
      display: flex;
      flex-direction: column;
      gap: var(--space-sm);
    }
    .analytics-kart-row {
      display: flex;
      align-items: center;
      gap: var(--space-md);
    }
    /* .analytics-kart-row-badge's own visual rules live in the shared
       P-badge component near .telemetry-p-badge (issue #205); its modifier
       was renamed from a chained .fastest to the same -lead suffix every
       other P-badge site uses (js/analytics-charts.js). */
    .analytics-kart-row-bar-wrap {
      flex: 1;
      height: 10px;
      border-radius: 999px;
      background: var(--surface-2);
      overflow: hidden;
    }
    .analytics-kart-row-bar {
      height: 100%;
      border-radius: 999px;
      background: var(--border-strong);
    }
    .analytics-kart-row-bar.fastest {
      background: var(--accent);
      box-shadow: 0 0 10px var(--accent-glow-strong);
    }
    .analytics-kart-row-right {
      flex-shrink: 0;
      width: 4.5em;
      text-align: right;
    }
    .analytics-kart-row-time {
      color: var(--text);
      font-weight: 700;
      font-size: var(--fs-sm);
    }
    /* Stichprobengröße unter der Bestzeit (issue #190) — mirrors
       .overall-standings-rides' muted small-print treatment. */
    .analytics-kart-row-rides {
      color: var(--text-muted);
      font-size: var(--fs-xs);
    }

    /* Gesamt-Analytics: Hausrangliste — ranks every LOCAL profile by its
       all-time bestMs (js/overall-analytics.js's renderStandings(), fed by
       analytics-core.js's houseStandingsFor()). Own class names rather than
       reusing .analytics-kart-row* (whose shape — badge + bar + time only,
       no name/rides-count — doesn't fit a profile row), but the same visual
       language/tokens throughout. */
    .overall-standings-list {
      display: flex;
      flex-direction: column;
      gap: var(--space-sm);
    }
    .overall-standings-row {
      display: flex;
      align-items: center;
      gap: var(--space-md);
      width: 100%;
      padding: var(--space-sm) 0;
      background: none;
      border: none;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      text-align: left;
      font-family: inherit;
    }
    /* .overall-standings-badge's own visual rules live in the shared
       P-badge component near .telemetry-p-badge (issue #205); its modifier
       was renamed from a chained .lead to the same -lead suffix every
       other P-badge site uses (js/overall-analytics.js) — NOT the same
       .lead class .overall-standings-bar-fill below still uses on its own
       separate element, untouched by this rename. */
    .overall-standings-main {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: 6px;
    }
    .overall-standings-name {
      color: var(--text);
      font-weight: 700;
      font-size: var(--fs-sm);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .overall-standings-bar-track {
      height: 5px;
      border-radius: 999px;
      background: var(--surface-2);
      overflow: hidden;
    }
    .overall-standings-bar-fill {
      height: 100%;
      border-radius: 999px;
      background: var(--border-strong);
    }
    .overall-standings-bar-fill.lead {
      background: var(--accent);
      box-shadow: 0 0 10px var(--accent-glow-strong);
    }
    .overall-standings-right {
      flex-shrink: 0;
      text-align: right;
    }
    .overall-standings-time {
      color: var(--text);
      font-weight: 700;
      font-size: 1rem;
    }
    .overall-standings-rides {
      color: var(--text-muted);
      font-size: var(--fs-xs);
      letter-spacing: 0.3px;
      margin-top: 2px;
    }

    /* Gesamt-Analytics: "Einzelnes Profil ansehen" (issue #383) — every
       profile as its own chevron row (mirrors .pitstop-list-row's chrome-
       less, tap-target shape) with a two-line label (name + a small-print
       meta subtitle) rather than .overall-standings-row's badge/bar/time,
       since this list has no ranking or bestMs to show — just enough detail
       (ride count, last activity) to tell profiles apart before tapping. */
    .overall-profile-links-list {
      display: flex;
      flex-direction: column;
    }
    .overall-profile-link-row {
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      width: 100%;
      padding: var(--space-sm) 0;
      background: none;
      border: none;
      border-bottom: 1px solid var(--border);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      text-align: left;
      font-family: inherit;
      transition: background 0.12s;
    }
    .overall-profile-links-list > :last-child { border-bottom: none; }
    .overall-profile-link-row:active { background: var(--raised); }
    .overall-profile-link-main {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: 4px;
    }
    .overall-profile-link-name {
      color: var(--text);
      font-weight: 700;
      font-size: var(--fs-sm);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .overall-profile-link-meta {
      color: var(--text-muted);
      font-size: var(--fs-xs);
    }
    .overall-profile-link-chevron {
      flex-shrink: 0;
      color: var(--text-dim);
    }

    .analytics-filter {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      margin-bottom: 16px;
      flex-shrink: 0;
    }
    /* Same pill shape/pattern as .history-sort-btn above, migrated onto the
       same redesign tokens (issue #205: this was still on the older
       --surface-2/--surface-3 pair) — kept as its own rule rather than
       merged outright since its font-weight/transition behavior is a
       deliberate difference (this chip only turns bold ON SELECTION,
       .history-sort-btn is always bold). */
    .analytics-filter-chip {
      padding: 6px 14px;
      border-radius: 999px;
      border: 1px solid var(--border);
      background: var(--raised);
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-xs);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .analytics-filter-chip.active {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
      font-weight: 700;
    }

    .analytics-kpis {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 10px;
      margin-bottom: 16px;
      flex-shrink: 0;
    }
    .analytics-kpi {
      background: var(--surface-2);
      border: 1px solid var(--surface-3);
      border-radius: 10px;
      padding: 10px 12px;
    }
    .analytics-kpi-tappable {
      cursor: pointer;
    }
    .analytics-kpi-tappable:focus-visible {
      outline: 2px solid var(--text);
      outline-offset: 2px;
    }
    .analytics-kpi-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }
    .analytics-kpi-value {
      font-size: 1.2rem;
      font-weight: 800;
      color: var(--text);
      margin-top: 2px;
    }
    .analytics-kpi-extra {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    .analytics-kpi-date {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 4px;
    }

    .analytics-empty-range {
      display: flex;
      flex-wrap: wrap;
      align-items: center;
      gap: 8px;
    }
    .analytics-show-all-btn {
      padding: 4px 12px;
      border-radius: 999px;
      border: 1px dashed var(--text-dim);
      background: none;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-xs);
      cursor: pointer;
    }

    .analytics-trend {
      font-size: 0.9rem;
      font-weight: 700;
      margin-bottom: 10px;
      flex-shrink: 0;
    }
    .analytics-trend-better { color: var(--green); }
    .analytics-trend-worse { color: var(--accent); }
    .analytics-trend-flat { color: var(--text-muted); }

    .analytics-legend {
      display: flex;
      gap: 20px;
      justify-content: center;
      margin-bottom: 10px;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      flex-shrink: 0;
    }
    .analytics-legend-item {
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .analytics-legend-swatch {
      width: 14px;
      height: 14px;
      flex-shrink: 0;
    }

    .analytics-chart-wrap {
      flex: 1;
      min-height: 280px;
    }
    .analytics-chart {
      width: 100%;
      height: 100%;
      min-height: 280px;
    }
    .analytics-chart-heading {
      font-size: var(--fs-xs);
      font-weight: 800;
      letter-spacing: 1px;
      text-transform: uppercase;
      color: var(--text-muted);
      margin: 16px 0 4px;
      flex-shrink: 0;
    }

    .analytics-leaderboard-wrap {
      flex-shrink: 0;
      overflow-x: auto;
    }
    .analytics-leaderboard {
      width: 100%;
      border-collapse: collapse;
      font-size: var(--fs-xs);
    }
    .analytics-leaderboard th {
      text-align: left;
      font-size: var(--fs-xs);
      text-transform: uppercase;
      letter-spacing: 0.5px;
      color: var(--text-muted);
      padding: 6px 8px;
      border-bottom: 1px solid var(--surface-3);
    }
    .analytics-leaderboard td {
      padding: 6px 8px;
      border-bottom: 1px solid var(--surface-2);
      color: var(--text);
    }
    .analytics-leaderboard-mine {
      background: var(--accent-glow);
      font-weight: 700;
    }

    @keyframes fadeSlideIn {
      from { opacity: 0; transform: translateY(-8px); }
      to { opacity: 1; transform: translateY(0); }
    }

    /* ─── FULLSCREEN OVERLAY ─── */
    /* Issue #194: instant, like every other routed overlay — see
       .pitstop-overlay's own comment above. */
    .fullscreen-overlay {
      position: fixed;
      inset: 0;
      z-index: 500;
      background: #000;
      opacity: 0;
      pointer-events: none;
    }
    .fullscreen-overlay.active {
      opacity: 1;
      pointer-events: auto;
    }

    .fullscreen-inner {
      position: fixed;
      top: 50%;
      left: 50%;
      width: 100dvw;
      height: 100dvh;
      transform: translate(-50%, -50%);
      transform-origin: 50% 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      /* Pinch-zoom is enabled app-wide now (see index.html's viewport meta),
         but a double-tap here should swipe/race, not zoom the fullscreen
         card — the one place a double-tap gesture is actually meaningful. */
      touch-action: manipulation;
    }

    .fullscreen-inner.force-landscape {
      width: 100dvh;
      height: 100dvw;
      transform: translate(-50%, -50%) rotate(90deg);
    }


    .fullscreen-track {
      width: 100%;
      height: 100%;
      display: flex;
      overflow-x: auto;
      overflow-y: hidden;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
      scroll-snap-type: x mandatory;
      overscroll-behavior: contain;
    }
    .fullscreen-track::-webkit-scrollbar { display: none; }

    .fullscreen-slide {
      flex: 0 0 100%;
      width: 100%;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 6px;
      box-sizing: border-box;
      scroll-snap-align: start;
      scroll-snap-stop: always;
    }

    .fullscreen-slide-card {
      max-width: 100%;
      height: 96%;
      display: flex;
      align-items: center;
      justify-content: center;
      background: #fff;
      border-radius: var(--radius-card);
      /* The redesign's "weiße Karte" glow (issue #137/#141) — a hairline
         dark outline (so the white card reads as a distinct object against
         the fullscreen view's own pure-black background) plus a soft red
         glow, same recipe as the main hauptscreen's card. */
      box-shadow: 0 0 0 1px #2c2c2e, 0 18px 40px rgba(225, 6, 0, 0.12);
    }

    .fullscreen-slide-card img {
      max-width: 100%;
      max-height: 100%;
      display: block;
      border-radius: 8px;
      object-fit: contain;
    }

    /* 44px, dark "raised" circles (issue #137/#141, Frame 3e) — one at each
       physical corner (js/fullscreen.js keeps the existing structure/
       rotate-for-landscape mechanics unchanged, this is styling only): no
       longer accent-red (that's reserved for the primary race action), a
       calmer neutral chrome that still clears the ≥44px touch-target rule. */
    .close-fullscreen {
      position: absolute;
      top: calc(var(--space-md) + var(--safe-side));
      right: calc(var(--space-md) + var(--safe-side));
      width: 44px;
      height: 44px;
      border-radius: 50%;
      border: 1px solid var(--border-strong);
      background: var(--raised);
      color: var(--text);
      font-size: 1.1rem;
      font-weight: 700;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      z-index: 10;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .close-fullscreen:active {
      transform: scale(0.9);
    }
    .close-fullscreen-left {
      right: auto;
      left: calc(var(--space-md) + var(--safe-side));
      top: auto;
      bottom: calc(var(--space-md) + var(--safe-side));
    }
    .fullscreen-rides {
      position: absolute;
      top: calc(var(--space-md) + var(--safe-side));
      left: calc(var(--space-md) + var(--safe-side));
      box-sizing: border-box;
      display: flex;
      align-items: baseline;
      gap: 8px;
      z-index: 10;
      pointer-events: none;
      transform-origin: 0 0;
      transform: rotate(-90deg) translate(-100%, 0);
    }
    .fullscreen-rides-num {
      color: #fff;
      font-weight: 900;
      font-size: 1.9rem;
      line-height: 1;
    }
    .fullscreen-rides-label {
      color: rgba(255, 255, 255, 0.6);
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 0.2em;
    }

    .race-btn.fullscreen-race-btn {
      position: absolute;
      bottom: calc(var(--space-md) + var(--safe-side));
      right: calc(var(--space-md) + var(--safe-side));
      width: auto;
      max-width: 260px;
      height: 54px;
      padding: 0 26px;
      font-size: 1.05rem;
      letter-spacing: 0.1em;
      border-radius: var(--radius-btn);
      box-shadow: var(--shadow-btn-primary);
      z-index: 10;
      transform-origin: 100% 100%;
      transform: rotate(-90deg) translate(100%, 0);
    }
    .race-btn.fullscreen-race-btn:active:not(:disabled) {
      transform: rotate(-90deg) translate(100%, 0) scale(0.97);
    }

    /* ─── RACE CELEBRATION ─── */
    .race-celebration {
      position: fixed;
      /* A bit above dead-center (issue #137) — this overlays directly on the
         live card/button view (there's no dedicated full-screen "race
         moment" surface), and true 50% put the context line right on top of
         the race button underneath on the compact main-screen layout. */
      top: 42%;
      left: 50%;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      transform: translate(-50%, -50%) scale(0.6);
      z-index: 200;
      pointer-events: none;
      opacity: 0;
      text-align: center;
      padding: 24px;
    }
    /* A soft dark halo (issue #137) — this overlays directly on top of
       whatever the live card/button view underneath happens to show (there
       is no dedicated full-screen "race moment" surface), so the icon/text
       needs its own contrast floor rather than relying on text-shadow alone
       against an arbitrary light background (e.g. the white card). */
    .race-celebration::before {
      content: '';
      position: absolute;
      inset: -20px -10px;
      background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.8) 0%, rgba(0, 0, 0, 0.5) 60%, transparent 85%);
      border-radius: 32px;
      z-index: -1;
    }
    /* The app's own brand mark (issue #137/#141) — the OS 🏁 emoji issue
       #104 kept as a deliberate exception is replaced here per the
       redesign's rule that the kart, not the flag, is the celebration icon.
       -6deg is a static tilt (independent of the pop-in keyframe below,
       which only animates the outer container's scale/opacity). */
    .race-celebration-icon {
      width: 110px;
      height: 110px;
      border-radius: 24px;
      transform: rotate(-6deg);
      box-shadow: 0 14px 34px var(--accent-glow-strong);
    }
    .race-celebration-title {
      color: var(--text);
      font-weight: 900;
      font-style: italic;
      font-size: 2.1rem;
      letter-spacing: 0.04em;
      text-transform: uppercase;
      text-shadow: 0 2px 14px rgba(0, 0, 0, 0.7);
    }
    .race-celebration-sub {
      color: var(--text-muted);
      font-size: 0.9rem;
      text-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
    }
    .race-celebration.active {
      animation: flagPop 0.65s ease-out forwards;
    }
    @keyframes flagPop {
      0% { transform: translate(-50%, -50%) scale(0.4); opacity: 0; }
      35% { transform: translate(-50%, -50%) scale(1.08); opacity: 1; }
      65% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
      100% { transform: translate(-50%, -50%) scale(0.94); opacity: 0; }
    }

    .race-border-flash {
      position: fixed;
      inset: 0;
      pointer-events: none;
      z-index: 150;
      border: 2px solid var(--accent);
      box-shadow: inset 0 0 60px var(--accent-glow-strong);
      opacity: 0;
    }
    .race-border-flash.active {
      animation: borderFlash 0.5s ease-out;
    }
    @keyframes borderFlash {
      0% { opacity: 0.9; }
      100% { opacity: 0; }
    }

    /* ─── TOAST ─── */
    .toast {
      position: fixed;
      bottom: calc(var(--safe-bottom) + 20px);
      left: 20px;
      right: 20px;
      background: var(--raised);
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-card);
      padding: 12px 16px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      font-size: var(--fs-sm);
      color: var(--text);
      /* Above every overlay (highest is .confirm-overlay at 700) — a toast
         fired while any dialog is open (e.g. Settings' Gmail fetch buttons)
         must stay visible instead of rendering invisibly behind it. */
      z-index: 800;
      transform: translateY(100px);
      opacity: 0;
      transition: all 0.3s cubic-bezier(0.2, 0.9, 0.3, 1);
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      box-shadow: 0 8px 32px rgba(0,0,0,0.4);
    }
    .toast.visible {
      transform: translateY(0);
      opacity: 1;
    }

    /* --accent-lt (issue #137) — the design's convention for links/
       destructive-flavored text (the same token .pitstop-action-danger
       etc. use), not the base --accent this used before. */
    .toast-undo {
      background: none;
      border: 1px solid var(--accent-lt);
      color: var(--accent-lt);
      font-weight: 700;
      padding: 5px 14px;
      border-radius: 6px;
      font-family: inherit;
      font-size: var(--fs-xs);
      letter-spacing: 1px;
      cursor: pointer;
      flex-shrink: 0;
      margin-left: 12px;
    }
    .toast-undo:active {
      background: var(--accent-lt);
      color: #fff;
    }

    /* ─── INSTALL BANNER ─── */
    .install-banner {
      position: fixed;
      bottom: calc(var(--safe-bottom) + 20px);
      left: 20px;
      right: 20px;
      background: var(--raised);
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-card);
      padding: 14px 16px;
      display: flex;
      align-items: center;
      gap: 12px;
      z-index: 350;
      transform: translateY(120px);
      opacity: 0;
      transition: all 0.35s cubic-bezier(0.2, 0.9, 0.3, 1);
      backdrop-filter: blur(12px);
      -webkit-backdrop-filter: blur(12px);
      box-shadow: 0 8px 32px rgba(0,0,0,0.5);
    }
    .install-banner.visible {
      transform: translateY(0);
      opacity: 1;
    }
    .install-banner-icon {
      width: 40px;
      height: 40px;
      border-radius: 8px;
      flex-shrink: 0;
    }
    .install-banner-text {
      flex: 1;
      min-width: 0;
    }
    .install-banner-title {
      font-weight: 700;
      font-size: var(--fs-sm);
      color: var(--text);
    }
    .install-banner-sub {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    .install-banner-btn {
      flex-shrink: 0;
      padding: 8px 16px;
      border: none;
      /* Code review follow-up (#137): the container/close-button already
         migrated to --raised/--border-strong/--radius-card above — this
         button was the one piece still on the pre-redesign hardcoded
         radius/glow instead of the same primary-button tokens every other
         accent-colored button in this PR uses (e.g. .fullscreen-race-btn). */
      border-radius: var(--radius-btn);
      background: var(--accent);
      color: #fff;
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-xs);
      letter-spacing: 1px;
      text-transform: uppercase;
      cursor: pointer;
      box-shadow: var(--shadow-btn-primary);
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .install-banner-btn:active {
      transform: scale(0.93);
    }
    .install-banner-close {
      flex-shrink: 0;
      width: 28px;
      height: 28px;
      border: none;
      border-radius: 50%;
      background: var(--border-strong);
      color: var(--text-muted);
      font-size: var(--fs-sm);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
    }
    .install-banner-close:active {
      background: var(--surface);
    }

    /* ─── RESPONSIVE (portrait only) ─── */
    @media (orientation: portrait) and (max-height: 700px) {
      .carousel-section { padding-top: 10px; }
      .telemetry-card { margin-top: 8px; }
      .action-section { padding-top: 12px; }
    }

    @media (orientation: portrait) and (max-height: 600px) {
      header { padding: 6px 20px 0; }
      .checkered-stripe { margin-top: 4px; }
    }

    /* ─── HOME OVERLAY ─── */
    /* .home-overlay's own base rules live in the shared .pitstop-overlay
       rule above (issue #227). */

    /* Redesign "Timing Tower" — Home als "Startaufstellung" (issue #134,
       epic #141): the section-label eyebrow above the profile list. */
    .home-section-label {
      font-weight: 800;
      font-size: var(--fs-xs);
      letter-spacing: 2px;
      text-transform: uppercase;
      color: var(--text-muted);
      margin: 0 0 var(--space-sm) 2px;
    }

    .home-list {
      display: flex;
      flex-direction: column;
      gap: var(--space-sm);
    }

    /* Grid-Karte statt Listenzeile: P-Badge | Name+Bestzeit | Fahrten-Zahl.
       Reorder/Klick-Verhalten (-> Carousel-Position wechseln, js/ui.js's
       buildHome()) ist unverändert — nur das Styling/Markup der Zeile. */
    .home-row {
      display: flex;
      align-items: center;
      gap: var(--space-md);
      width: 100%;
      padding: var(--space-md) var(--space-lg);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-card);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s, border-color 0.15s, background 0.15s;
    }
    .home-row:active {
      transform: scale(0.99);
      background: var(--raised);
      border-color: var(--border-strong);
    }

    /* .home-row-badge's own visual rules live in the shared P-badge
       component near .telemetry-p-badge (issue #205). */

    .home-row-left {
      display: flex;
      flex-direction: column;
      gap: 4px;
      min-width: 0;
      flex: 1;
      text-align: left;
    }

    .home-row-name {
      font-weight: 700;
      font-size: 1.05rem;
      color: var(--text);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .home-row-best {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .home-row-best.empty {
      color: var(--text-dim);
    }

    .home-row-rides {
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0;
    }

    .home-row-rides-count {
      font-weight: 900;
      font-size: 26px;
      line-height: 1;
      color: var(--text);
    }
    .home-row-rides.zero .home-row-rides-count {
      color: var(--text-muted);
      opacity: 0.6;
    }

    .home-row-rides-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      letter-spacing: 0.3px;
    }

    .home-empty {
      text-align: center;
      color: var(--text-muted);
      padding: 40px 20px;
      font-size: 0.95rem;
    }

    /* "Profil hinzufügen" als eigene Karte mit gestricheltem Rahmen statt
       Button/Zeile. */
    .home-add-card {
      margin-top: var(--space-lg);
      width: 100%;
      padding: var(--space-md) var(--space-lg);
      border: 2px dashed var(--border-strong);
      border-radius: var(--radius-card);
      background: transparent;
      color: var(--text-muted);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-base);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-sm);
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s, border-color 0.15s, color 0.15s;
    }
    .home-add-card:active {
      transform: scale(0.98);
      border-color: var(--accent);
      color: var(--text);
    }
    .home-add-card-icon {
      display: flex;
      color: var(--accent);
    }

    /* Gesamt-Analytics-Einstieg (issue #184 follow-up, review fix): same
       sparkline-teaser visual language as the card view's own Analytics
       teaser (.telemetry-teaser) — a design-frame-2f pattern, not the plain
       chevron-row style .pitstop-list-row uses elsewhere in Settings-like
       lists. Unlike the per-profile teaser, this one is static/decorative
       (no real per-house trend data backs it — the design's own subtitle
       text is fixed, not computed), so its sparkline bars are plain markup
       reusing .telemetry-teaser-bar's existing level classes, not JS-built. */
    .home-overall-teaser {
      width: 100%;
      display: flex;
      align-items: center;
      gap: var(--space-md);
      text-align: left;
      font-family: inherit;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .home-overall-teaser:active { transform: scale(0.98); }
    .home-overall-teaser-text {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
    }
    .home-overall-teaser-title {
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 0.06em;
      color: var(--text);
    }
    .home-overall-teaser-sub {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-top: 2px;
    }
    .home-overall-teaser-chevron {
      flex-shrink: 0;
      color: var(--accent);
    }

    /* "Einstellungen" gedämpft (kein Karten-Look, nur Icon + Text) — bewusst
       niedrigere Emphase als die beiden Karten darüber. */
    .home-settings-link {
      margin-top: var(--space-sm);
      width: 100%;
      padding: var(--space-md) var(--space-lg);
      border: none;
      background: transparent;
      color: var(--text-muted);
      font-family: inherit;
      font-weight: 600;
      font-size: var(--fs-sm);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: var(--space-sm);
      -webkit-tap-highlight-color: transparent;
      transition: color 0.15s;
    }
    .home-settings-link:active {
      color: var(--text);
    }

    /* ─── EMPTY STATE ─── */
    .empty-state {
      display: none;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      flex: 1;
      padding: var(--space-2xl) var(--space-xl);
      text-align: center;
    }
    .empty-state.active {
      display: flex;
    }
    .empty-state-icon {
      width: 96px;
      height: 96px;
      border-radius: 24px;
      margin-bottom: var(--space-lg);
      box-shadow: var(--shadow-btn-primary);
    }
    .empty-state-title {
      font-weight: 900;
      font-style: italic;
      font-size: 28px;
      text-transform: uppercase;
      margin-bottom: var(--space-sm);
    }
    .empty-state-sub {
      color: var(--text-muted);
      font-size: var(--fs-sm);
      max-width: 320px;
      margin: 0 auto var(--space-2xl);
    }

    /* issue #135: two equally-reachable, clearly differentiated options —
       Gmail-Connect visually primary (red, elevated), manual creation a
       secondary card of the same shape/size rather than a plain text link,
       per the ticket's "zwei klare Optionen" framing. */
    .empty-state-options {
      display: flex;
      flex-direction: column;
      gap: var(--space-md);
      width: 100%;
      max-width: 340px;
    }
    .empty-state-option {
      display: flex;
      align-items: center;
      gap: var(--space-md);
      width: 100%;
      background: var(--raised);
      border: 1px solid var(--border);
      border-radius: var(--radius-card);
      padding: var(--space-lg);
      text-align: left;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .empty-state-option:active {
      transform: scale(0.98);
    }
    .empty-state-option-icon {
      width: 28px;
      height: 28px;
      flex-shrink: 0;
      color: var(--text);
    }
    .empty-state-option-text {
      display: flex;
      flex-direction: column;
      gap: var(--space-2xs);
      min-width: 0;
    }
    .empty-state-option-title {
      font-weight: 700;
      font-size: var(--fs-base);
      color: var(--text);
    }
    .empty-state-option-sub {
      font-size: var(--fs-xs);
      color: var(--text-muted);
    }
    .empty-state-option-primary {
      background: var(--accent);
      border-color: var(--accent);
      box-shadow: var(--shadow-btn-primary);
    }
    .empty-state-option-primary .empty-state-option-icon,
    .empty-state-option-primary .empty-state-option-title {
      color: #fff;
    }
    .empty-state-option-primary .empty-state-option-sub {
      color: rgba(255, 255, 255, 0.85);
    }

    /* The boot-error screen keeps its single primary CTA ("Neu laden") in
       this shared button style — same tokens as the options above, no
       second button (issue #135: unchanged function, consistent styling
       only). */
    .empty-state-btn {
      background: var(--accent);
      color: #fff;
      border: none;
      border-radius: var(--radius-btn);
      padding: 14px 32px;
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 1px;
      text-transform: uppercase;
      cursor: pointer;
      box-shadow: var(--shadow-btn-primary);
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s;
    }
    .empty-state-btn:active {
      transform: scale(0.95);
    }

    .empty-state-settings-link {
      margin-top: var(--space-lg);
      background: none;
      border: none;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-xs);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }

    /* ─── CREATE OVERLAY ─── */
    /* .create-overlay's own base rules live in the shared .pitstop-overlay
       rule above (issue #227); .create-panel's own rules live in the shared
       .overlay-panel rule above (issue #205). */
    .create-header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-bottom: 24px;
      flex-shrink: 0;
    }
    .create-title {
      font-weight: 800;
      font-size: 1rem;
      letter-spacing: 3px;
      text-transform: uppercase;
      color: var(--text);
    }
    .create-close {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      border: none;
      background: var(--accent);
      color: #fff;
      font-size: 1.2rem;
      font-weight: 700;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 2px 8px var(--accent-glow);
      -webkit-tap-highlight-color: transparent;
    }
    .create-close:active { transform: scale(0.9); }
    .create-form {
      display: flex;
      flex-direction: column;
      flex: 1;
    }
    .create-label {
      font-size: var(--fs-xs);
      color: var(--text-muted);
      letter-spacing: 2px;
      text-transform: uppercase;
      margin-bottom: 6px;
      font-weight: 600;
    }
    .create-name-input {
      background: var(--surface-2);
      border: 1px solid var(--surface-3);
      border-radius: 8px;
      padding: 12px 14px;
      color: var(--text);
      font-family: inherit;
      font-size: 1rem;
      user-select: text;
      -webkit-user-select: text;
      margin-bottom: 6px;
      -webkit-tap-highlight-color: transparent;
      transition: border-color 0.2s;
    }
    .create-name-input:focus {
      outline: none;
      border-color: var(--accent);
    }
    .create-name-input:focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
    }
    .create-name-input::placeholder {
      color: var(--text-dim);
    }
    /* Zeichen-Zähler "N / 24" (issue #136) — right-aligned under the input,
       hidden together with it in the "Karte ändern" edit flow (no name field
       there, see js/ui.js's openCreate()). */
    .create-name-counter {
      align-self: flex-end;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      margin-bottom: 20px;
    }
    .create-upload-area {
      background: var(--surface-2);
      border: 2px dashed var(--surface-3);
      border-radius: 12px;
      /* No longer itself a click target (issue #101) — the two explicit
         .create-upload-btn buttons below it now own that instead, since
         there are two separate actions (camera vs. gallery) rather than one. */
      position: relative;
      overflow: hidden;
      aspect-ratio: 16 / 10;
      margin-bottom: 12px;
      -webkit-tap-highlight-color: transparent;
    }
    .create-upload-area.has-preview .create-upload-placeholder {
      display: none;
    }
    .create-upload-area.has-preview {
      border-style: solid;
      border-color: var(--surface-3);
    }
    .create-upload-placeholder {
      position: absolute;
      inset: 0;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 8px;
    }
    .create-upload-icon {
      font-size: 2rem;
    }
    .create-upload-text {
      color: var(--text-muted);
      font-size: var(--fs-xs);
    }
    .create-upload-preview {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: none;
      border-radius: 10px;
    }
    .create-upload-area.has-preview .create-upload-preview {
      display: block;
    }
    .create-upload-actions {
      display: flex;
      flex-direction: column;
      gap: var(--space-sm);
      margin-bottom: var(--space-xl);
    }
    .create-upload-btn {
      background: var(--surface-2);
      border: 1px solid var(--surface-3);
      border-radius: 10px;
      color: var(--text);
      font-family: inherit;
      font-weight: 600;
      font-size: var(--fs-base);
      padding: var(--space-md);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .create-upload-btn:active {
      background: var(--surface-3);
    }
    /* Unlike the photo <img> (object-fit: cover is fine, deliberately
       cropped to the card's aspect ratio), a PDF417 canvas must never be
       cropped — override to contain + a light background so the whole code
       is visible, matching the same treatment .card-barcode gives it
       elsewhere (issue #56). */
    #createPdf417Preview {
      object-fit: contain;
      background: #fff;
      image-rendering: pixelated;
    }
    .create-save-btn {
      margin-top: auto;
      background: var(--accent);
      color: white;
      border: none;
      border-radius: var(--radius-btn);
      padding: 14px;
      font-family: inherit;
      font-weight: 800;
      font-size: 1rem;
      letter-spacing: 3px;
      text-transform: uppercase;
      cursor: pointer;
      box-shadow: var(--shadow-btn-primary);
      -webkit-tap-highlight-color: transparent;
      transition: transform 0.1s, opacity 0.2s;
    }
    .create-save-btn:active:not(:disabled) {
      transform: scale(0.97);
    }
    .create-save-btn:disabled {
      background: var(--surface-3);
      box-shadow: none;
      cursor: not-allowed;
      opacity: 0.4;
    }

    /* ─── PDF417 retrofit + confirm dialog (issue #56) ─── */
    .create-pdf417-retrofit-btn {
      margin-top: 12px;
      padding: 10px 14px;
      border-radius: 10px;
      border: 1px dashed var(--surface-3);
      background: none;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-xs);
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .pdf417-confirm-text {
      color: var(--text-muted);
      font-size: var(--fs-sm);
      line-height: 1.5;
      margin: 0 0 16px;
    }
    #pdf417ConfirmPreviewWrap {
      margin-bottom: 20px;
    }
    .pdf417-confirm-actions {
      display: flex;
      gap: 12px;
    }
    /* Base rules (padding/radius/colors/:active) live in the shared
       dialog-button component near .timeinput-cancel (issue #205) — the
       redesign-token migration mentioned there. Only this pair's own
       tighter tracking and the accept button's glow stay here. */
    .pdf417-confirm-decline, .pdf417-confirm-accept {
      letter-spacing: 1px;
    }
    .pdf417-confirm-accept {
      box-shadow: 0 4px 20px var(--accent-glow);
    }

    /* ─── CROP OVERLAY ─── */
    /* Shared with .live-camera-overlay further below (issue #227 — a second
       byte-identical overlay-base pair, distinct from .pitstop-overlay's own
       group above since these use display:none/flex, not opacity). */
    .crop-overlay, .live-camera-overlay {
      position: fixed;
      inset: 0;
      z-index: 450;
      background: #000;
      display: none;
      flex-direction: column;
    }
    .crop-overlay.active, .live-camera-overlay.active {
      display: flex;
    }
    /* Hinweiszeile (issue #136) — the pan/zoom gestures below are unchanged,
       this is a purely decorative header. */
    .crop-hint {
      flex-shrink: 0;
      padding: calc(var(--safe-top) + var(--space-md)) var(--space-lg) var(--space-sm);
      text-align: center;
      background: rgba(0, 0, 0, 0.8);
      color: var(--text-muted);
      font-weight: 700;
      font-size: var(--fs-xs);
      letter-spacing: 2px;
      text-transform: uppercase;
    }
    .crop-container {
      flex: 1;
      position: relative;
      overflow: hidden;
      touch-action: none;
    }
    .crop-canvas {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
    }
    /* Kreditkarten-Ratio (js/crop.js's RATIO constant), 2px red border,
       darkened surroundings (issue #136) — geometry/pan-zoom math untouched,
       purely the visual layer. */
    .crop-frame {
      position: absolute;
      border: 2px solid var(--accent);
      box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
      pointer-events: none;
      border-radius: 8px;
    }
    /* Drittel-Hilfslinien (rule-of-thirds guides), white 25% opacity. Two
       pseudo-elements (vertical/horizontal) rather than one, since a single
       background-image can't cleanly express both axes' explicit line
       positions in one declaration. */
    .crop-frame::before,
    .crop-frame::after {
      content: '';
      position: absolute;
      inset: 0;
      pointer-events: none;
    }
    .crop-frame::before {
      background-image:
        linear-gradient(to right,
          transparent calc(33.333% - 0.5px), rgba(255, 255, 255, 0.25) calc(33.333% - 0.5px),
          rgba(255, 255, 255, 0.25) calc(33.333% + 0.5px), transparent calc(33.333% + 0.5px),
          transparent calc(66.666% - 0.5px), rgba(255, 255, 255, 0.25) calc(66.666% - 0.5px),
          rgba(255, 255, 255, 0.25) calc(66.666% + 0.5px), transparent calc(66.666% + 0.5px));
    }
    .crop-frame::after {
      background-image:
        linear-gradient(to bottom,
          transparent calc(33.333% - 0.5px), rgba(255, 255, 255, 0.25) calc(33.333% - 0.5px),
          rgba(255, 255, 255, 0.25) calc(33.333% + 0.5px), transparent calc(33.333% + 0.5px),
          transparent calc(66.666% - 0.5px), rgba(255, 255, 255, 0.25) calc(66.666% - 0.5px),
          rgba(255, 255, 255, 0.25) calc(66.666% + 0.5px), transparent calc(66.666% + 0.5px));
    }
    .crop-actions {
      display: flex;
      gap: 12px;
      padding: var(--space-lg) var(--space-xl) calc(var(--safe-bottom) + var(--space-lg));
      background: rgba(0,0,0,0.8);
      flex-shrink: 0;
    }
    /* Base rules (padding/radius/colors/:active) live in the shared
       dialog-button component near .timeinput-cancel (issue #205) — the
       redesign-token migration mentioned there. Only this pair's own
       uppercase-tracking flourish and the accept button's glow stay here. */
    .crop-cancel-btn, .crop-done-btn {
      letter-spacing: 2px;
      text-transform: uppercase;
    }
    .crop-done-btn {
      box-shadow: var(--shadow-btn-primary);
    }

    /* ─── LIVE CAMERA OVERLAY (issue #102) ─── */
    /* .live-camera-overlay's own base rules live in the shared .crop-overlay
       rule above (issue #227). */
    .live-camera-container {
      flex: 1;
      position: relative;
      overflow: hidden;
      background: #000;
    }
    .live-camera-video {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .live-camera-hint {
      position: absolute;
      inset: var(--space-lg);
      display: flex;
      align-items: center;
      justify-content: center;
      text-align: center;
      color: var(--text);
      background: rgba(0, 0, 0, 0.7);
      border-radius: 12px;
      padding: var(--space-lg);
    }
    /* A brief flash on a live PDF417 hit (issue #102) — the visual feedback
       between detection and the overlay closing into the #56 confirm dialog. */
    .live-camera-hit .live-camera-video {
      outline: 4px solid var(--green);
      outline-offset: -4px;
    }

    /* ─── CONFIRM DIALOG ─── */
    .confirm-overlay {
      position: fixed;
      inset: 0;
      z-index: 700;
      background: rgba(0, 0, 0, 0.7);
      display: none;
      align-items: center;
      justify-content: center;
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
    }
    .confirm-overlay.active {
      display: flex;
    }
    .confirm-dialog {
      padding: 24px 20px;
      margin: 0 20px;
      max-width: 340px;
      width: 100%;
    }
    /* Red trash-icon circle (issue #137) — hidden by default in index.html,
       shown only for the default "Löschen" confirm (ui/dialogs.js's own
       confirmOnce()). */
    .confirm-icon-circle {
      width: 52px;
      height: 52px;
      margin: 0 auto 14px;
      border-radius: 50%;
      background: rgba(225, 6, 0, 0.12);
      border: 1px solid rgba(225, 6, 0, 0.5);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .confirm-icon-circle .icon {
      width: 22px;
      height: 22px;
      color: var(--accent-lt);
    }
    .confirm-text {
      color: var(--text);
      font-weight: 700;
      font-size: 1.05rem;
      line-height: 1.5;
      margin-bottom: 20px;
      text-align: center;
    }
    .confirm-actions {
      display: flex;
      gap: 10px;
    }
    /* .confirm-cancel/.confirm-ok's own visual rules live in the shared
       dialog-button component near .timeinput-cancel (issue #205). */

    /* ─── PIT STOP PROFIL TAB (issue #138) ─── */
    .pitstop-field-label {
      display: flex;
      align-items: center;
      gap: 6px;
      font-weight: 600;
      font-size: var(--fs-xs);
      letter-spacing: 2px;
      color: var(--text-muted);
      text-transform: uppercase;
      margin-bottom: 8px;
    }
    .pitstop-field-label:not(:first-child) { margin-top: var(--space-lg); }
    .pitstop-field-input {
      width: 100%;
      background: var(--bg);
      border: 1px solid var(--border-strong);
      border-radius: 10px;
      color: var(--text);
      font-family: inherit;
      font-weight: 600;
      /* 16px minimum (issue #93) — anything below that on a real text input
         makes iOS Safari zoom the whole viewport in on focus. */
      font-size: var(--fs-base);
      padding: 10px 12px;
      -webkit-tap-highlight-color: transparent;
      transition: border-color 0.2s;
    }
    .pitstop-field-input:focus {
      outline: none;
      border-color: var(--accent);
    }
    .pitstop-field-input:focus-visible {
      outline: 2px solid var(--accent-lt);
      outline-offset: 2px;
    }

    /* Design review fix: the edit-pencil affordance sits as a trailing icon
       inside the Name field itself (frame 2c), not as a prefix on the field
       label above it. */
    .pitstop-field-wrap {
      position: relative;
    }
    .pitstop-field-wrap .pitstop-field-input {
      padding-right: 38px;
    }
    .pitstop-field-wrap-icon {
      position: absolute;
      right: 12px;
      top: 50%;
      transform: translateY(-50%);
      color: var(--text-dim);
      pointer-events: none;
    }

    .pitstop-list-card { padding: 0; }
    .pitstop-list-row {
      width: 100%;
      display: flex;
      align-items: center;
      gap: var(--space-sm);
      padding: var(--space-md) var(--space-lg);
      border: none;
      border-bottom: 1px solid var(--border);
      background: none;
      color: var(--text);
      font-family: inherit;
      font-size: var(--fs-sm);
      text-align: left;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.12s;
    }
    .pitstop-list-card > :last-child { border-bottom: none; }
    .pitstop-list-row:active { background: var(--raised); }
    .pitstop-list-row-label { flex: 1; }
    .pitstop-list-row-chevron { color: var(--text-dim); }

    .pitstop-reorder-actions {
      display: flex;
      gap: 6px;
      flex-shrink: 0;
    }
    .pitstop-reorder-btn {
      width: 34px;
      height: 34px;
      border-radius: 50%;
      border: 1px solid var(--border-strong);
      background: var(--raised);
      color: var(--text);
      font-size: 1.2rem;
      line-height: 1;
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      -webkit-tap-highlight-color: transparent;
      transition: all 0.12s;
    }
    .pitstop-reorder-btn:active { transform: scale(0.9); }
    .pitstop-reorder-btn:disabled { opacity: 0.2; cursor: not-allowed; }

    .pitstop-action-btn {
      width: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      padding: 12px 16px;
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-btn);
      background: var(--raised);
      color: var(--text);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 0.5px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: all 0.12s;
    }
    .pitstop-analytics-btn {
      margin-top: 4px;
    }
    .pitstop-action-btn:active {
      transform: scale(0.97);
    }
    .pitstop-action-danger {
      background: rgba(225, 6, 0, 0.08);
      border-color: rgba(225, 6, 0, 0.4);
      color: var(--accent-lt);
      margin-top: var(--space-lg);
    }
    .pitstop-action-danger:active {
      border-color: var(--accent);
    }
    .pitstop-version {
      text-align: center;
      font-size: var(--fs-xs);
      color: var(--text-dim);
      letter-spacing: 2px;
      margin-top: 8px;
      opacity: 0.5;
    }
    .home-version {
      text-align: center;
      font-size: var(--fs-xs);
      font-weight: 600;
      color: var(--text-muted);
      letter-spacing: 2px;
      margin-top: 16px;
    }
  
    /* ─── v2: BARCODE CARD ─── */
    .card-barcode {
      width: 100%;
      aspect-ratio: 16 / 10;
      background: #fff;
      border-radius: 12px;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 14px 18px;
      box-shadow:
        0 8px 32px rgba(0,0,0,0.6),
        0 0 0 1px rgba(255,255,255,0.06),
        0 0 60px -10px var(--accent-glow);
    }
    .card-barcode svg { width: 100%; height: 100%; display: block; }
    /* PDF417.draw() sizes the canvas to its own small native module grid
       (often well under the card's real pixel size) — max-width/max-height
       alone only ever SHRINKS an oversized element, it can't enlarge a
       smaller one, unlike the SVG rule above which always fills via
       viewBox + preserveAspectRatio. object-fit does the same "fill without
       distorting" job for a canvas (issue #56 PR review). */
    .card-barcode canvas { width: 100%; height: 100%; object-fit: contain; display: block; image-rendering: pixelated; }
    .fullscreen-slide-card .card-barcode {
      width: 100%; height: 100%; aspect-ratio: auto; box-shadow: none; border-radius: 14px;
    }

    /* ─── v2: CARD TYPE TOGGLE + NUMBER INPUT (create) ─── */
    /* issue #136: restyled onto the "Timing Tower" design tokens — base
       rules live in the shared segmented-control component near
       .pitstop-tabs (issue #205). Only this one's own uppercase-tracking
       flourish stays here. Ids/markup unchanged. */
    .create-segment button {
      letter-spacing: 1px;
      text-transform: uppercase;
    }
    /* Kartennummer: Saira Condensed 18px, tracking 0.2em (issue #136) — same
       "numbers get the numeric display font" convention as .font-numeric
       elsewhere, applied inline here since the input also needs the wider
       letter-spacing that .font-numeric itself doesn't define. */
    .create-number-input {
      background: var(--surface-2);
      border: 1px solid var(--surface-3);
      border-radius: 8px;
      padding: 12px 14px;
      color: var(--text);
      font-family: 'Saira Condensed', 'Titillium Web', system-ui, sans-serif;
      font-size: 18px;
      font-weight: 600;
      letter-spacing: 0.2em;
      font-variant-numeric: tabular-nums;
      user-select: text;
      -webkit-user-select: text;
      margin-bottom: 14px;
      -webkit-tap-highlight-color: transparent;
      transition: border-color 0.2s;
    }
    .create-number-input:focus { outline: none; border-color: var(--accent); }
    .create-number-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    .create-barcode-preview {
      background: #fff;
      border-radius: var(--radius-card);
      padding: 14px;
      margin-bottom: 20px;
      min-height: 90px;
      display: flex;
      align-items: center;
      justify-content: center;
      box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
    }
    .create-barcode-preview svg { width: 100%; max-height: 80px; }
    .create-barcode-preview.empty { background: var(--surface-2); color: var(--text-muted); font-size: var(--fs-xs); box-shadow: none; }
    [hidden] { display: none !important; }

    /* ─── DIAGNOSTICS / SETTINGS (issue #133, epic #141) ─── */
    /* Three grouped cards (Daten / RC-Timing über Gmail / Mehrgeräte-Sync)
       replacing the previous flat list of individually-boxed .settings-row
       elements — an iOS-Settings-style grouped list, one bordered card per
       group with a thin divider between each direct-child row instead of
       each row carrying its own background/border/margin. */
    .settings-section { margin-bottom: var(--space-lg); }
    .settings-section-label {
      font-size: var(--fs-xs); /* issue #205: was a sub-floor 11px literal */
      font-weight: 600;
      letter-spacing: 0.18em;
      text-transform: uppercase;
      color: var(--text-muted);
      margin-bottom: var(--space-sm);
      padding-left: 2px;
    }
    .settings-card {
      display: flex;
      flex-direction: column;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-card);
      overflow: hidden;
    }
    .settings-card > *:not(:last-child) { border-bottom: 1px solid var(--border); }

    .settings-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 14px 16px;
      gap: 12px;
    }
    .settings-row-label { font-size: 0.9rem; color: var(--text); }
    .settings-row-sub { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 2px; }
    .settings-row-main { flex: 1; min-width: 0; }
    .settings-btn {
      padding: 10px 16px;
      border: 1px solid var(--border-strong);
      border-radius: var(--radius-btn);
      background: var(--raised);
      color: var(--text);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-xs);
      letter-spacing: 1px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      flex-shrink: 0;
    }
    .settings-btn:active { transform: scale(0.96); }
    .settings-btn-primary {
      background: var(--accent);
      border-color: var(--accent);
      color: #fff;
    }
    /* "Trennen" — a text link (--accent-lt) instead of a boxed button,
       matching Frame 2e's status-row treatment. */
    .settings-link-btn {
      flex-shrink: 0;
      background: none;
      border: none;
      padding: 0;
      color: var(--accent-lt);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 0.5px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .settings-link-btn:active { opacity: 0.7; }

    /* Connection status dot (Verbunden/Nicht verbunden), left of the label. */
    .settings-status-line { display: flex; align-items: center; gap: 8px; }
    .settings-status-dot {
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: var(--text-dim);
      flex-shrink: 0;
    }
    .settings-status-dot.is-connected { background: var(--green); }

    /* Export | Import — one row, two equal-width buttons split by a hairline
       divider, instead of two separate boxed rows. */
    .settings-row-split { padding: 0; gap: 0; }
    .settings-split-btn {
      flex: 1;
      padding: 14px 16px;
      background: none;
      border: none;
      color: var(--text);
      font-family: inherit;
      font-weight: 700;
      font-size: var(--fs-sm);
      letter-spacing: 1px;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .settings-split-btn:active { background: var(--raised); }
    .settings-split-divider { width: 1px; align-self: stretch; background: var(--border); flex-shrink: 0; }

    /* "Dauerhafter Speicher" toggle switch — this browser API can only ever
       be REQUESTED (never revoked from the app), so once on it's shown
       checked+disabled rather than reverting to a button/disappearing. */
    .settings-toggle {
      position: relative;
      box-sizing: border-box;
      width: 48px;
      height: 29px;
      flex-shrink: 0;
      border-radius: 999px;
      border: 1px solid var(--border-strong);
      background: var(--raised);
      padding: 0;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
      transition: background 0.15s, border-color 0.15s;
    }
    .settings-toggle[aria-checked="true"] {
      background: var(--green);
      border-color: var(--green);
    }
    .settings-toggle:disabled { cursor: default; }
    .settings-toggle-knob {
      position: absolute;
      top: 2px;
      left: 2px;
      width: 23px;
      height: 23px;
      border-radius: 50%;
      background: #fff;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
      transition: transform 0.15s;
    }
    .settings-toggle[aria-checked="true"] .settings-toggle-knob { transform: translateX(21px); }

    /* "Mail-Filter"/"Beim Start abrufen"/"Profile & Fahrten-Stand teilen" —
       a row showing the current value + a trailing › chevron (same decorative
       affordance/color as elsewhere in the app), instead of an always-visible
       select inline with a label. The first is a real <button> that expands
       an inline detail panel; the latter two keep their native <select> (so
       existing tests can still drive them directly via selectOption()) with
       only its OWN appearance restyled to sit in the chevron-value slot. */
    .settings-chevron-row {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 12px;
      width: 100%;
      padding: 14px 16px;
      background: none;
      border: none;
      color: var(--text);
      font-family: inherit;
      text-align: left;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    button.settings-chevron-row:active { background: var(--raised); }
    .settings-chevron-value {
      display: flex;
      align-items: center;
      gap: 6px;
      min-width: 0;
      flex-shrink: 1;
      color: var(--text-muted);
      font-size: var(--fs-sm);
    }
    .settings-chevron-value span:first-child {
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .settings-chevron {
      color: var(--text-dim);
      font-size: 1rem;
      line-height: 1;
      transition: transform 0.15s;
    }
    .settings-chevron-row[aria-expanded="true"] .settings-chevron { transform: rotate(90deg); }
    .settings-chevron-select {
      appearance: none;
      -webkit-appearance: none;
      background: none;
      border: none;
      max-width: 140px;
      color: var(--text-muted);
      font-family: inherit;
      font-size: var(--fs-sm);
      text-align: right;
      padding: 0;
      cursor: pointer;
    }

    .settings-gmail-config {
      padding: 14px 16px;
    }
    #gmailFetchSection.settings-gmail-config { padding: 0; }
    .settings-gmail-filter-detail { padding: 0 16px 14px; }
    .settings-gmail-filter-row {
      display: flex;
      gap: 8px;
    }
    .settings-select,
    .settings-text-input {
      background: var(--raised);
      border: 1px solid var(--border-strong);
      border-radius: 8px;
      color: var(--text);
      font-family: inherit;
      font-size: var(--fs-sm);
      padding: 8px 10px;
    }
    /* 16px minimum on the actual typed-into text field (issue #93) — .settings-select
       is a native <select> picker, not a typing field, so it doesn't trigger
       iOS Safari's zoom-on-focus the way a real <input> does and is left as-is. */
    .settings-text-input {
      font-size: var(--fs-base);
    }
    .settings-select { flex-shrink: 0; }
    .settings-text-input { flex: 1; min-width: 0; }
    .settings-gmail-actions {
      display: flex;
      gap: 8px;
      padding: 12px 16px;
    }
    .settings-gmail-actions .settings-btn { flex: 1; }

    .settings-ignored-list {
      list-style: none;
      margin-top: 10px;
      padding-left: 0;
    }
    .settings-ignored-item {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 10px;
      padding: 8px 0;
      border-bottom: 1px solid var(--border);
      font-size: var(--fs-sm);
      color: var(--text);
    }
    .settings-ignored-item:last-child { border-bottom: none; }
    .settings-ignored-remove {
      flex-shrink: 0;
      width: 32px;
      height: 32px;
      border: 1px solid var(--border-strong);
      border-radius: 8px;
      background: var(--raised);
      color: var(--text-muted);
      font-size: 0.9rem;
      cursor: pointer;
      -webkit-tap-highlight-color: transparent;
    }
    .settings-ignored-remove:active { transform: scale(0.9); border-color: var(--accent); color: var(--accent); }

    .storage-meter {
      height: 6px;
      border-radius: 3px;
      background: var(--raised);
      overflow: hidden;
      margin-top: 8px;
    }
    .storage-meter-fill { height: 100%; background: var(--accent); width: 0%; transition: width 0.3s; }

    /* Centered footer, replacing the plain .pitstop-version this overlay used
       to share with Pit Stop — its own border-strong hairline separates it
       from the last card above. */
    .settings-footer {
      text-align: center;
      font-size: var(--fs-xs);
      color: var(--text-muted);
      letter-spacing: 0.12em;
      padding-top: var(--space-lg);
      margin-top: var(--space-sm);
      border-top: 1px solid var(--border-strong);
      flex-shrink: 0;
    }

    /* ─── v2: A11Y FOCUS ─── */
    :focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
    }

    /* ─── v2: SELECTABLE VALUE DISPLAYS ─── */
    /* Carves out real, copyable content from the app-wide user-select: none
       default (issue #92) — the card number and every best-lap/time display,
       the two examples the ticket names explicitly ("kann keine Zahl
       kopieren"). Everything else (buttons, labels, nav controls) stays
       non-selectable so swipe/drag gestures over their text keep working. */
    .card-barcode svg text,
    /* .telemetry-stat-value covers both #ridesCount and #profileBesttimeValue
       (issue #131) — they share this one class now instead of each needing
       its own selector here. */
    .telemetry-stat-value,
    .pitstop-besttime,
    .pitstop-count,
    .ride-detail-best,
    /* Scoped to the value/date spans specifically, not the whole <li> — a
       .pitstop-times-list li also contains a .time-item-delete button
       (review finding on this PR), and making that button's own hit area
       selectable-text too would risk a touch-drag starting on the adjacent
       value text turning into a text-selection instead of reaching the
       delete tap cleanly. */
    .time-item-value,
    .time-item-date,
    .history-laps-list li {
      user-select: text;
      -webkit-user-select: text;
    }

    /* ─── v2: REDUCED MOTION ─── */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
      }
    }
