@bytebrand/fe-ui-core-autobahn 1.0.113 → 1.0.115

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/theme/md3.css CHANGED
@@ -453,6 +453,44 @@ line-height: 31px;
453
453
  width: 14px;
454
454
  height: 14px;
455
455
  }
456
+ /* Figma spec for the rating chip in its two "Guter Preis" locations — the VDP header
457
+ (VdpHeaderSummary, .vdp-headrow) and the SRP list-card's price column (CarRow.tsx,
458
+ .listcard-price, both its mobile Finanzierung row and desktop Kaufen row share this one class).
459
+ Scoped to THESE locations only, not every .statchip site-wide (e.g. the price-history "drop"
460
+ chip a few sections down reuses tone="good" for a completely different purpose — a €-amount,
461
+ not a price rating — and keeps the theme-aware --md-good-container tokens untouched). min-width
462
+ (not a hard width) + white-space:nowrap: 108px is tight for icon+"Guter Preis"+⁴ on one line,
463
+ so this grows past it instead of wrapping/clipping rather than clamping to a width that doesn't fit. */
464
+ .md3 .vdp-headrow .statchip,
465
+ .md3 .listcard-price .statchip {
466
+ min-width: 108px;
467
+ height: 27px;
468
+ border-radius: 11px;
469
+ font-family: 'Arial';
470
+ font-weight: 700;
471
+ font-size: 12px;
472
+ line-height: 14px;
473
+ text-align: center;
474
+ white-space: nowrap;
475
+ justify-content: center;
476
+ }
477
+ .md3 .vdp-headrow .statchip sup,
478
+ .md3 .listcard-price .statchip sup {
479
+ position: relative;
480
+ top: -3px;
481
+ left: -4px;
482
+ font-size: 9px;
483
+ line-height: 9px;
484
+ }
485
+ /* 'good' tone here is specifically the "Guter Preis" rating (same canonical green as the
486
+ fin-eval-pill "good" tier elsewhere on this page) — literal color, not the --md-good-container
487
+ token, matching that other pill's own literal-color treatment. --warn (Fairer Preis) is
488
+ untouched, still tokenized. */
489
+ .md3 .vdp-headrow .statchip--good,
490
+ .md3 .listcard-price .statchip--good {
491
+ color: rgb(22, 138, 69);
492
+ background: color-mix(in srgb, rgb(22, 138, 69) 16%, transparent);
493
+ }
456
494
 
457
495
  /* small image label (views, photo count) */
458
496
  .md3 .imglabel {
@@ -3360,11 +3398,17 @@ line-height: 31px;
3360
3398
  transform: translateY(-50%);
3361
3399
  width: 32px;
3362
3400
  height: 32px;
3363
- border-radius: var(--md-r-full);
3364
- border: 0;
3365
- background: rgba(255, 255, 255, 0.92);
3401
+ border-radius: 50%;
3402
+ border: 1px solid #E8EDF3;
3403
+ /* same "glass" translucency (0.5 alpha + light border + blur) as the sibling overlays on this
3404
+ card — .cc-photo-pill (image count) and .cc-activity-pill (views) — so the arrows read as
3405
+ part of the same overlay family instead of a heavier, opaque control. Dropped the old
3406
+ box-shadow: var(--md-e1) for the same reason — neither sibling pill carries one, and a drop
3407
+ shadow under a low-alpha glass circle looked muddy. The blur is what keeps the dark #08162A
3408
+ chevron legible against busy vehicle photos underneath (contrast doesn't depend on the photo). */
3409
+ background: rgba(255, 255, 255, 0.5);
3410
+ backdrop-filter: blur(2px);
3366
3411
  color: var(--md-on-surface);
3367
- box-shadow: var(--md-e1);
3368
3412
  display: flex;
3369
3413
  align-items: center;
3370
3414
  justify-content: center;
@@ -3373,11 +3417,20 @@ line-height: 31px;
3373
3417
  transition: opacity var(--dur-1);
3374
3418
  z-index: 2;
3375
3419
  }
3420
+ /* Touch target: the visible circle stays 32px (matches the design), but that's below the ~44px
3421
+ minimum recommended for a reliable touch target (WCAG 2.5.5 / Apple HIG). This invisible
3422
+ pseudo-element pads the actual hit area out to 44px (32 + 6 + 6) without changing how the
3423
+ button looks — clicks/taps anywhere in the padded zone still hit the real <button>. */
3424
+ .md3 .car-arrow::before {
3425
+ content: '';
3426
+ position: absolute;
3427
+ inset: -6px;
3428
+ }
3376
3429
  .md3 .car-arrow--l {
3377
- left: 22px;
3430
+ left: 10px;
3378
3431
  }
3379
3432
  .md3 .car-arrow--r {
3380
- right: 22px;
3433
+ right: 10px;
3381
3434
  }
3382
3435
  .md3 .cardcar:hover .car-arrow {
3383
3436
  opacity: 1;
@@ -3397,6 +3450,14 @@ line-height: 31px;
3397
3450
  border: 1px solid #D4DDE7;
3398
3451
  box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.08);
3399
3452
  }
3453
+ /* pin the VDP gallery's own edge offset — the base .car-arrow--l/--r rule above was moved to
3454
+ 10px for the SRP card carousels; the VDP viewer keeps its original, larger offset. */
3455
+ .md3 .vdp-gallerybox .car-arrow--l {
3456
+ left: 22px;
3457
+ }
3458
+ .md3 .vdp-gallerybox .car-arrow--r {
3459
+ right: 22px;
3460
+ }
3400
3461
 
3401
3462
  /* ==========================================================================
3402
3463
  RESPONSIVE LAYOUT LAYER
@@ -3420,7 +3481,15 @@ line-height: 31px;
3420
3481
  }
3421
3482
  .md3 .field-grid-2 {
3422
3483
  display: grid;
3423
- grid-template-columns: 1fr 1fr;
3484
+ /* minmax(0,1fr), not plain 1fr: a bare 1fr track's implicit minimum is auto (= its content's
3485
+ min-content), and a searchable Dropdown's open/editing state swaps the trigger <button> for
3486
+ a <div><input></div> — inputs carry their own browser-default intrinsic min-content width
3487
+ that flex's min-width:0 on the input doesn't fully suppress once a CSS Grid track (not just
3488
+ the inner flex row) is the one asking "how small can you get". That let the Make/Model
3489
+ column widen the moment it opened, shoving into its sibling column instead of staying put.
3490
+ Same fix/diagnosis as .fin-price-grid above (see its own comment) — minmax(0,1fr) overrides
3491
+ the track's floor directly, so it can't happen regardless of what's inside. */
3492
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
3424
3493
  gap: 12px;
3425
3494
  }
3426
3495
  .md3 .cards-3 {
@@ -3747,7 +3816,7 @@ line-height: 31px;
3747
3816
  min-width: 0;
3748
3817
  }
3749
3818
  /* "NEU" badge on car cards (legacy new-car label). */
3750
- .md3 .neu-badge { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: var(--md-r-full); background: var(--md-primary); color: #fff; font-size: 10.5px; font-weight: 700; letter-spacing: .3px; white-space: nowrap; box-shadow: var(--md-e1); }
3819
+ .md3 .neu-badge { display: inline-flex; align-items: center; height: 22px; padding: 0 8px; border-radius: var(--md-r-full); background: var(--md-primary); color: #fff; font-size: 10.5px; font-weight: 700; line-height: 14px; letter-spacing: .3px; white-space: nowrap; box-shadow: var(--md-e1); }
3751
3820
  /* "⚡ Angebot" badge — dark neutral pill above the gallery (card mockup), distinct from NEU. */
3752
3821
  .md3 .offer-badge { display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 9px; border-radius: var(--md-r-full); background: var(--md-on-surface); color: #fff; font-size: 11px; font-weight: 600; white-space: nowrap; }
3753
3822
  .md3 .offer-badge .lucide { width: 13px; height: 13px; }
@@ -5164,37 +5233,50 @@ line-height: 31px;
5164
5233
  width: 112px;
5165
5234
  flex: none;
5166
5235
  }
5167
- .md3 .cc-price .fin-eval-pill {
5168
- font-family: Arial, Helvetica, sans-serif;
5169
- font-style: normal;
5236
+ /* Figma spec, now identical across all three "Guter Preis" pill locations (CarCard's own
5237
+ .cc-price, incl. the VDP "Ähnliche Fahrzeuge" rail; .fin-calc-card; .fin-modal) — one shared
5238
+ rule instead of three copies. `background`/`color` are deliberately NOT set here: they stay
5239
+ driven by the tier-color inline style each render site sets (see PriceEvalCol in
5240
+ cardParts.tsx / FinanceCalculator.tsx / VdpModules.tsx, all of which already resolve to the
5241
+ canonical rgb(22,138,69) for the 'good' tier) — a `!important` used to hardcode this pill's
5242
+ background to a fixed #dff3e5 here, silently overriding that inline value regardless of tier. */
5243
+ .md3 .cc-price .fin-eval-pill,
5244
+ .md3 .fin-calc-card .fin-eval-pill,
5245
+ .md3 .fin-modal .fin-eval-pill {
5246
+ font-family: 'Arial';
5170
5247
  font-weight: 700;
5171
5248
  font-size: 12px;
5172
5249
  line-height: 14px;
5173
5250
  text-align: center;
5174
5251
  }
5175
- .md3 .cc-price .fin-eval-pill--good {
5176
- background: #dff3e5 !important;
5177
- border-radius: 11px !important;
5178
- width: 100%;
5252
+ .md3 .cc-price .fin-eval-pill--good,
5253
+ .md3 .fin-calc-card .fin-eval-pill--good,
5254
+ .md3 .fin-modal .fin-eval-pill--good {
5255
+ width: 108px;
5179
5256
  height: 27px;
5257
+ border-radius: 11px;
5180
5258
  box-sizing: border-box;
5259
+ display: flex;
5260
+ align-items: center;
5181
5261
  justify-content: center;
5182
5262
  }
5183
5263
  /* footnote 4 ("Top-Preis" claim) — .fin-eval-pill is a flex container, so vertical-align has
5184
5264
  no effect on the sup (it's a flex item, not inline content); position:relative/top raises
5185
- it instead, same technique as the hero price's own 1/2/3 footnote markers. Shared by the
5186
- CarCard hero (.cc-price), the VDP FinanceCalculator's own pill (.fin-calc-card), and the
5187
- FinanceDetailsModal's embedded mobile rate sheet (.fin-modal). */
5265
+ it instead, same technique as the hero price's own 1/2/3 footnote markers. Same -3px lift now
5266
+ shared by all three locations above. */
5188
5267
  .md3 .cc-price .fin-eval-pill sup,
5189
5268
  .md3 .fin-calc-card .fin-eval-pill sup,
5190
5269
  .md3 .fin-modal .fin-eval-pill sup {
5191
5270
  position: relative;
5192
- top: 0px;
5271
+ top: -3px;
5193
5272
  left: -4px;
5194
5273
  font-size: 9px;
5195
5274
  line-height: 9px;
5196
5275
  }
5197
5276
  .md3 .cc-price .fin-eval-bar {
5277
+ /* matches the pill's own fixed 108px right above (both sit in the same 112px .fin-eval-col),
5278
+ so the two stay visually aligned instead of the bar stretching to the full column width. */
5279
+ width: 108px;
5198
5280
  height: 7px;
5199
5281
  }
5200
5282
  .md3 .cc-price .fin-eval-seg:first-child {
@@ -5424,14 +5506,8 @@ line-height: 31px;
5424
5506
  width: 112px;
5425
5507
  justify-self: end;
5426
5508
  }
5427
- /* pill now sits above the bar in a real flex column (see above) — stretch it to the bar's own
5428
- width instead of the base .fin-eval-pill's inline-flex/content-hugging size. */
5429
- .md3 .fin-calc-card .fin-eval-pill--good {
5430
- width: 100%;
5431
- display: flex;
5432
- align-items: center;
5433
- justify-content: center;
5434
- }
5509
+ /* .fin-calc-card/.fin-modal's own "Guter Preis" pill text/box/sup sizing now lives in the one
5510
+ shared rule (with .cc-price) further up this file, near .cc-price's own .fin-eval-* rules. */
5435
5511
  /* .fin-calc-card's own "Rate anpassen" panel is compact — the mobile-sheet/modal padding
5436
5512
  (22/28px top, 20px bottom) is too roomy here. */
5437
5513
  .md3 .fin-calc-card .fin-toggle-final,
@@ -6252,14 +6328,29 @@ line-height: 31px;
6252
6328
  /* Visible icon glyphs 24-28px (spec range) — the shared .icon-btn/.mbtn rules force 20px/18px
6253
6329
  everywhere else in the app, which is fine there but read visibly smaller than the Figma
6254
6330
  mobile header reference. */
6255
- .md3 header .icon-btn {
6256
- width: 48px;
6257
- height: 48px;
6258
- }
6259
6331
  .md3 header .icon-btn .lucide {
6260
6332
  width: 24px;
6261
6333
  height: 24px;
6262
6334
  }
6335
+ /* Bug: "Header distribution" — Figma has the logo starting at x=49 and the search/phone/account
6336
+ icon group starting at x=246 (390px reference), but the logo was rendering ~25px too far
6337
+ right and the icon group ~15-17px too far left. Root cause: this rule used to ALSO force
6338
+ hamburger/search/phone's own icon-btn box to 48×48 (copied from .topbar-account-btn just
6339
+ below, whose 48×48 IS spec'd — WCAG/Header-System touch-target — but nothing here documented
6340
+ the same for these three). That extra 8px of box per icon (48 vs the base .icon-btn's 40px)
6341
+ pushed the logo right (hamburger wider) and pulled the action-icon group left (search/phone
6342
+ wider, eating into the row before the right-aligned account button). Dropped back to the
6343
+ base 40×40 box — glyph size above is unaffected, so nothing looks visually smaller — and
6344
+ restored the lost touch-target room with an invisible ::before hit-slop instead (inset:-4px
6345
+ = 48×48 effective tap zone, same technique as the SRP card-carousel arrows use). */
6346
+ .md3 header .icon-btn {
6347
+ position: relative;
6348
+ }
6349
+ .md3 header .icon-btn::before {
6350
+ content: '';
6351
+ position: absolute;
6352
+ inset: -4px;
6353
+ }
6263
6354
  .md3 header .topbar-account-btn .lucide {
6264
6355
  width: 24px;
6265
6356
  height: 24px;
@@ -6274,6 +6365,13 @@ line-height: 31px;
6274
6365
  .md3 .topbar-logo {
6275
6366
  width: 122px;
6276
6367
  height: auto;
6368
+ /* pulls the logo from x≈68 (12px padding + the hamburger's 40px box + the .topbar-container
6369
+ row's own 16px gap, set inline and shared with other pairs in that row — not touched here,
6370
+ see the icon-btn comment above for why) to Figma's x=49. A negative margin on THIS one item
6371
+ is deliberate: shrinking the shared row gap instead would have also pulled the search/
6372
+ phone/account cluster on the right closer together, which is already correct after the
6373
+ icon-btn fix above and must stay untouched. */
6374
+ margin-left: -19px;
6277
6375
  }
6278
6376
  /* Gap between action icons 2-6px (spec: "Abstand Aktionen") — separate from the wider gap the
6279
6377
  hamburger/logo/nav still use (that's the outer .container's own gap, untouched here). */
@@ -6595,6 +6693,13 @@ line-height: 31px;
6595
6693
  .md3 .cards-2 {
6596
6694
  grid-template-columns: 1fr;
6597
6695
  }
6696
+ /* fe-landing (/angebot) TopResults + Alternatives grids — shared side gutter,
6697
+ same 12px pattern as .md3 .container elsewhere. .cards-3 is untouched (not
6698
+ used with this gutter treatment; only .cards-2 callers asked for it). */
6699
+ .md3 .cards-2 {
6700
+ padding-left: 12px !important;
6701
+ padding-right: 12px !important;
6702
+ }
6598
6703
  /* listing card stacks: image (4:3) → info → price */
6599
6704
  .md3 .listcard {
6600
6705
  grid-template-columns: 1fr;
@@ -6662,15 +6767,38 @@ line-height: 31px;
6662
6767
  }
6663
6768
  .md3 .srp-bar {
6664
6769
  position: sticky;
6665
- top: 64px;
6770
+ /* was 64px (docked under the always-visible header) — now 0: once the header slides away
6771
+ (autoHideOnScroll, TopBar.tsx) this bar takes its place at the very top instead of leaving
6772
+ a gap, and sits flush under it again the moment the header slides back in. */
6773
+ top: 0px;
6666
6774
  z-index: 20;
6667
- background: var(--md-surface);
6775
+ background: #fff;
6668
6776
  padding: 8px 0;
6669
6777
  margin: 0 0 8px;
6670
6778
  box-shadow: 0 6px 8px -8px rgba(0, 0, 0, 0.3);
6671
6779
  }
6780
+ /* full-bleed white to the true viewport edges, WITHOUT moving .srp-bar's own text — it stays
6781
+ inside .srp-main's own side padding, this pseudo-element just paints white past it on both
6782
+ sides so no gap of page background shows while the bar is sticky. NOT a box-shadow spread
6783
+ (that bleeds in all 4 directions equally, including a huge slab below the bar covering the
6784
+ cards underneath it — that was tried and broke the page) — an absolutely-positioned layer
6785
+ confined to top:0/bottom:0 of .srp-bar's own box (position:sticky is a containing block for
6786
+ it, same as position:relative) stays exactly as tall as the bar, only wider. z-index:-1 is
6787
+ scoped to .srp-bar's own stacking context (it has z-index:20), so it paints behind the bar's
6788
+ text/buttons but still above the page content scrolling underneath. */
6789
+ .md3 .srp-bar::before {
6790
+ content: '';
6791
+ position: absolute;
6792
+ top: 0;
6793
+ bottom: 0;
6794
+ left: 50%;
6795
+ width: 100vw;
6796
+ transform: translateX(-50%);
6797
+ background: #fff;
6798
+ z-index: -1;
6799
+ }
6672
6800
  .md3 .field-grid-2 {
6673
- grid-template-columns: 1fr;
6801
+ grid-template-columns: minmax(0, 1fr);
6674
6802
  gap: 10px;
6675
6803
  }
6676
6804
  .md3 .hero-search {
@@ -7193,8 +7321,10 @@ line-height: 31px;
7193
7321
  z-index: 3;
7194
7322
  }
7195
7323
 
7196
- /* VDP main wrapper: phone gets a tighter top gap (10px) and narrower side gutters
7197
- (14px) than the 14/24px desktop default set inline. */
7324
+ /* VDP main wrapper: phone gets a tighter top gap (0, was 15px) and narrower side gutters
7325
+ (10px, matches production) than the 15/24px desktop default set inline — title, highlight
7326
+ chips and the pricing card all inherit this from their shared .vdp-main ancestor, so there's
7327
+ no separate per-element rule to keep in sync with this one. */
7198
7328
  @media (max-width: 600px) {
7199
7329
  .md3 .vdp-main {
7200
7330
  padding: 0 10px 48px !important;
@@ -7377,6 +7507,28 @@ color: #005CFF;
7377
7507
  line-height: 17px;
7378
7508
  color: #1D2A38;
7379
7509
  }
7510
+ /* all three tabs (Finanzierung/Leasing/Kaufen), desktop + mobile — ToggleRow is the one shared
7511
+ render for all of them. Info icon sits raised near the top of the label, like a small
7512
+ superscript badge, instead of centered against the text. */
7513
+ .md3 .fin-toggle-addservices .md-title-sm .lucide {
7514
+ position: relative;
7515
+ top: -7px;
7516
+ left: -2px;
7517
+ }
7518
+ /* tooltip-trigger touch target (all 3 tabs, incl. --cash — an a11y correctness fix, not a
7519
+ per-tab visual choice like the icon nudge above). Trigger is now the 15x15 icon alone (the
7520
+ label text beside it is inert), so BOTH axes need padding out to 48px, not just the vertical
7521
+ one. ::after is positioned off layout flow (doesn't grow the row or push the label/price/
7522
+ switch siblings over) but still real estate for hit-testing — clicks/taps anywhere in its box
7523
+ count as hitting the trigger span itself. (15 + 17 + 17 = 49, clears the 48px minimum.) */
7524
+ .md3 .fin-toggle-info-trigger {
7525
+ position: relative;
7526
+ }
7527
+ .md3 .fin-toggle-info-trigger::after {
7528
+ content: '';
7529
+ position: absolute;
7530
+ inset: -17px;
7531
+ }
7380
7532
  .md3 .fin-toggle-addservices > div {
7381
7533
  gap: 13px !important;
7382
7534
  }
@@ -7417,6 +7569,12 @@ color: #005CFF;
7417
7569
  top: auto;
7418
7570
  bottom: auto;
7419
7571
  }
7572
+ /* Kaufen tab, desktop sidebar card (.fin-calc-card, NOT the .fin-modal sheet above) — the row
7573
+ sits lower in this card than the financing/leasing rows do, so the base "flush above" bottom:
7574
+ 100% opens noticeably higher than the trigger; drops it closer instead. */
7575
+ .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .fin-toggle-tooltip {
7576
+ bottom: -10%;
7577
+ }
7420
7578
  .md3 .fin-modal .show-mobile .fin-toggle-tooltip {
7421
7579
  border-radius: 11px;
7422
7580
  }
@@ -7467,9 +7625,6 @@ color: #005CFF;
7467
7625
  letter-spacing: 0.4px;
7468
7626
  color: var(--md-on-surface-variant);
7469
7627
  }
7470
- .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .md-title-sm .lucide {
7471
- display: none;
7472
- }
7473
7628
  .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .num,
7474
7629
  .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash > div > span:last-child {
7475
7630
  font-family: var(--md-font-display) !important;
@@ -8697,20 +8852,33 @@ color: #005CFF;
8697
8852
  grid-template-areas: "rate evalcol" "cond cond";
8698
8853
  align-items: start;
8699
8854
  }
8855
+ /* Bug: switching the Financing/Leasing/Buy tabs made the price row, rating pill and the two
8856
+ CTA buttons below jump up/down, because the row above them had a different rendered height
8857
+ per tab: (a) .fin-cond (the "0 € Anzahlung · 60 Monate · ..." terms line) renders NOTHING on
8858
+ Buy (isCash hides it), collapsing to 0 height instead of one text line, and (b)
8859
+ .fin-price-grid--cash below zeroes the row-gap that the other two tabs keep. The two rules
8860
+ below cancel each of those, so Financing/Leasing/Buy all reserve the same total height.
8861
+ (A first pass also force-reserved height on .fin-rate-main itself, to additionally cover the
8862
+ rare case of an extra struck list-price line on Buy when the car has a discount — isCash &&
8863
+ showStrike in FinanceCalculator.tsx. Dropped: since Financing/Leasing/undiscounted-Buy don't
8864
+ need that extra room, it left a visible empty gap under the price on every normal tab just to
8865
+ pre-cover one edge case. That specific combination can still shift by the strike line's own
8866
+ height; every other tab switch is now jump-free without the dead space.) */
8867
+ .md3 .fin-calc-card .fin-price-grid.fin-price-grid--cash {
8868
+ /* same 12px row-gap as Financing/Leasing (the base .fin-price-grid rule) — the 0px gap on
8869
+ .fin-price-grid--cash further up this file otherwise pulls the CTA row 12px closer on Buy. */
8870
+ gap: 12px 15px;
8871
+ }
8700
8872
  .md3 .fin-eval-col {
8701
8873
  display: flex;
8702
8874
  flex-direction: column;
8703
8875
  gap: 16px;
8704
8876
  grid-area: evalcol;
8705
8877
  }
8706
- .md3 .fin-eval-pill--good {
8707
- background: #dff3e5 !important;
8708
- border-radius: 11px !important;
8709
- width: 100%;
8710
- height: 27px;
8711
- box-sizing: border-box;
8712
- justify-content: center;
8713
- }
8878
+ /* box sizing for this pill (all three of its locations) lives in the shared, higher-specificity
8879
+ .cc-price/.fin-calc-card/.fin-modal rule near the top of this file — this generic, unscoped
8880
+ copy used to ALSO force `background: #dff3e5 !important`, silently overriding every tier's
8881
+ real inline color (the 'good' tier's canonical rgb(22,138,69) included) at this breakpoint. */
8714
8882
  .md3 .fin-eval-bar {
8715
8883
  height: 7px;
8716
8884
  }
@@ -8735,6 +8903,11 @@ color: #005CFF;
8735
8903
  font-weight: 400;
8736
8904
  font-size: 13px;
8737
8905
  line-height: 15px;
8906
+ /* matches the line-height above: on the Buy tab this div renders empty (its "0 € Anzahlung ·
8907
+ 60 Monate · ..." text is Financing/Leasing-only), so without a floor it collapsed to 0
8908
+ height instead of the one text line the other two tabs occupy — part of the cross-tab
8909
+ layout-jump fix, see the .fin-rate-main comment above. */
8910
+ min-height: 15px;
8738
8911
  color: #111820;
8739
8912
  margin: 0px 0px;
8740
8913
  margin-bottom: 12px;
@@ -10334,10 +10507,14 @@ color: #005CFF;
10334
10507
  }
10335
10508
  .md3 .vdp-photosheet {
10336
10509
  position: absolute;
10337
- left: 0;
10338
- right: 0;
10339
- bottom: 0;
10340
- height: 100vh;
10510
+ /* was `left/right/bottom: 0` + `height: 100vh` — over-constrained (top was the free value, so
10511
+ it was computed as containerHeight - 100vh) and, on the affected mobile browsers, 100vh can
10512
+ exceed the CURRENT visible height (e.g. address bar still shown), which solved to a NEGATIVE
10513
+ top: the sheet's top edge — its own header, with a close button — rendered above y=0 of this
10514
+ already-safely-sized fixed overlay (see VdpViewer.tsx's own top-of-file portal comment), i.e.
10515
+ behind the browser's UI. `inset: 0` instead just fills the (correctly-sized) positioned
10516
+ ancestor exactly, no viewport unit involved. */
10517
+ inset: 0;
10341
10518
  z-index: 211;
10342
10519
  display: flex;
10343
10520
  flex-direction: column;
@@ -10356,7 +10533,9 @@ color: #005CFF;
10356
10533
  align-items: center;
10357
10534
  justify-content: center;
10358
10535
  min-height: 88px;
10359
- padding-top: 10px;
10536
+ /* safe-area-aware: with the `inset: 0` fix above this sits flush against the true top edge of
10537
+ the fullscreen overlay — same notch/status-bar territory as the main viewer's own header. */
10538
+ padding-top: max(10px, env(safe-area-inset-top));
10360
10539
  touch-action: none;
10361
10540
  }
10362
10541
  .md3 .vdp-photosheet__handle {
@@ -10371,7 +10550,8 @@ color: #005CFF;
10371
10550
  }
10372
10551
  .md3 .vdp-photosheet__close {
10373
10552
  position: absolute;
10374
- left: 12px;
10553
+ /* safe-area-aware: in the landscape layout the notch can rotate onto this side. */
10554
+ left: max(12px, env(safe-area-inset-left));
10375
10555
  top: 50%;
10376
10556
  transform: translateY(-50%);
10377
10557
  }
@@ -10385,7 +10565,8 @@ color: #005CFF;
10385
10565
  }
10386
10566
  .md3 .vdp-photosheet__titles {
10387
10567
  flex: none;
10388
- padding: 0 16px 12px;
10568
+ /* safe-area-aware left/right: landscape can rotate the notch onto either side. */
10569
+ padding: 0 max(16px, env(safe-area-inset-right)) 12px max(16px, env(safe-area-inset-left));
10389
10570
  text-align: left;
10390
10571
  }
10391
10572
  .md3 .vdp-photosheet__title {
@@ -10408,7 +10589,9 @@ color: #005CFF;
10408
10589
  flex: 1;
10409
10590
  min-height: 0;
10410
10591
  overflow-y: auto;
10411
- padding: 0 12px 12px;
10592
+ /* safe-area-aware left/right — otherwise the outer grid tiles can sit partly under a
10593
+ landscape-rotated notch, tappable in theory but visually cut off. */
10594
+ padding: 0 max(12px, env(safe-area-inset-right)) 12px max(12px, env(safe-area-inset-left));
10412
10595
  }
10413
10596
  .md3 .vdp-photosheet__grid {
10414
10597
  display: grid;
@@ -10454,8 +10637,10 @@ color: #005CFF;
10454
10637
  align-items: center;
10455
10638
  justify-content: flex-start;
10456
10639
  gap: 6px;
10457
- height: 44px;
10458
- padding: 0 16px;
10640
+ /* was a fixed `height: 44px` — swapped for `min-height` so the safe-area bottom padding below
10641
+ (home indicator) grows the bar instead of pushing its text outside a clipped fixed box. */
10642
+ min-height: 44px;
10643
+ padding: 0 max(16px, env(safe-area-inset-right)) max(0px, env(safe-area-inset-bottom)) max(16px, env(safe-area-inset-left));
10459
10644
  /* rgba(0,0,0,0.5) was invisible here — it darkens whatever's behind it, and the sheet's own
10460
10645
  panel (rgb(6,11,18)) is already almost black, so a 50%-black overlay on top of near-black
10461
10646
  reads as no change at all. #1c222c is lighter/bluer and already used elsewhere in this same
@@ -10507,7 +10692,7 @@ color: #005CFF;
10507
10692
  right: 11px !important;
10508
10693
  }
10509
10694
  .md3 .cc-card .cc-badges {
10510
- top: 15px !important;
10695
+ top: 18px !important;
10511
10696
  }
10512
10697
 
10513
10698
  /* hero price — Figma's rate figure (Finanzierung/Leasing) is much larger than the shared
@@ -1,7 +1,5 @@
1
1
  import React, { useState, useRef, useEffect } from 'react';
2
2
 
3
- import Icon from './Icon';
4
-
5
3
  export interface CardCarouselProps {
6
4
  /** ready-to-use image URLs (already resolved by the caller — no asset/window lookup here). */
7
5
  images: string[];
@@ -132,14 +130,31 @@ const CardCarousel: React.FunctionComponent<CardCarouselProps> = ({
132
130
  ))}
133
131
  </div>
134
132
  {/* arrows + dots only when there is actually something to switch to — with a single
135
- image (e.g. SRP cards before their gallery is lazy-loaded) they are dead controls. */}
133
+ image (e.g. SRP cards before their gallery is lazy-loaded) they are dead controls.
134
+ States: single-image → n<=1 → hidden here (same "nothing to expand into" case as
135
+ GalleryCounter, cardParts.tsx). Loading → `images` starts as a 1-element array (the
136
+ card's main thumbnail) until the caller's lazy-load resolves, so this rule ALSO covers
137
+ "still loading" — arrows/dots simply aren't rendered yet, no skeleton, same discipline
138
+ as the other overlay controls. On touch devices this used to mean "hidden until the
139
+ user's first hover/touch on this exact card" (mouseenter/touchstart was the ONLY lazy-
140
+ load trigger) — CarCard's `eagerMedia` (see its own doc comment) starts that same load
141
+ on scroll-into-view instead, so this loading window is normally already closed by the
142
+ time the user reaches the card, without changing anything in this component. Desktop
143
+ hover-to-reveal (`.cardcar:hover .car-arrow`) vs. always-visible on touch is a CSS-only
144
+ concern — see `@media (hover: none)` in md3.css, not component state. */}
136
145
  {n > 1 && (
137
146
  <>
138
147
  <button className="car-arrow car-arrow--l" aria-label="zurück" onClick={(e) => go(-1, e)}>
139
- <Icon name="chevron-left" size={20} />
148
+ {/* exact icon from the design (not the lucide chevron-left) — 20x20 viewBox, dark
149
+ #08162A stroke so it stays legible on the translucent glass background above. */}
150
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
151
+ <path d="M12.0835 5L7.0835 10L12.0835 15" stroke="#08162A" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
152
+ </svg>
140
153
  </button>
141
154
  <button className="car-arrow car-arrow--r" aria-label="weiter" onClick={(e) => go(1, e)}>
142
- <Icon name="chevron-right" size={20} />
155
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
156
+ <path d="M7.9165 5L12.9165 10L7.9165 15" stroke="#08162A" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
157
+ </svg>
143
158
  </button>
144
159
  <div
145
160
  className="cardcar-dots"