@bytebrand/fe-ui-core-autobahn 1.0.114 → 1.0.116

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/cards/CarCard.tsx CHANGED
@@ -240,7 +240,7 @@ const CarCard: React.FunctionComponent<CarCardProps> = ({
240
240
  <GalleryActivityPill viewsCount={car.viewsCount} favoritesCount={car.favoritesCount} />
241
241
  </div>
242
242
  </div>
243
- <div style={{ padding: '10px 18px', display: 'flex', flexDirection: 'column', gap: 0, flex: 1 }}>
243
+ <div style={{ padding: '8px 18px', display: 'flex', flexDirection: 'column', gap: 0, flex: 1 }}>
244
244
  <div>
245
245
  <div className="md-title" style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
246
246
  {car.make} {car.model}
@@ -294,7 +294,7 @@ const CarCard: React.FunctionComponent<CarCardProps> = ({
294
294
  />
295
295
  {/* legacy VehicleSmallCard's "SeoText" footer line (full spec + equipment list) —
296
296
  renders nothing when the caller hasn't populated car.seoText. */}
297
- <SeoLine car={car} style={{ marginTop: 6 }} />
297
+ <SeoLine car={car} style={{ marginTop: 0 }} />
298
298
  {adminAction && (
299
299
  <div style={{ marginTop: 8 }}>
300
300
  <Button
@@ -19,7 +19,7 @@ import { TranslationFunction } from '../types';
19
19
  import { defaultConfig } from '@bytebrand/fe-financing';
20
20
 
21
21
  import { Icon, Button } from '../ui';
22
- import { DocCheckIcon, EvalBar } from '../vdp/FinanceCalculator';
22
+ import { EvalBar } from '../vdp/FinanceCalculator';
23
23
  import { CO2_CLASSES, CO2_COLORS, TIER_KEYS, TIER_COLORS } from '../constants';
24
24
  import { AutobahnCar } from './types';
25
25
  // pure, non-React — shared with the Meta AIA marketing-image renderer (a plain Node service).
@@ -784,37 +784,144 @@ export interface CardCtaRowProps {
784
784
  export const CardCtaRow: React.FunctionComponent<CardCtaRowProps> = ({ car, mode, onRequestOffer, onCall, onOpenRateAdjust, t }) => (
785
785
  <div
786
786
  className="card-cta-row show-mobile"
787
- style={{ display: 'flex', gap: 8, borderTop: '1px solid var(--md-outline-variant)', paddingTop: 8 }}
787
+ style={{ display: 'flex', gap: 6, borderTop: '1px solid var(--md-outline-variant)', paddingTop: 8 }}
788
788
  >
789
789
  <Button
790
790
  variant="filled"
791
- style={{ flex: 2 }}
791
+ style={{
792
+ // Figma width (157px) as a STARTING size, not a hard fixed one — flex-grow:1 so this
793
+ // button (and "Adjust rate" below) still fill extra row width on wider cards instead of
794
+ // leaving dead space next to the fixed 40px phone button; flex-shrink:1 lets it narrow
795
+ // gracefully (text ellipsizes via the existing .card-cta-row .mbtn overflow rule) rather
796
+ // than overflow on very narrow cards.
797
+ flex: '1 1 157px',
798
+ height: 40,
799
+ background: '#005cff',
800
+ borderRadius: 16,
801
+ padding: '0 8px',
802
+ fontFamily: 'Arial',
803
+ fontStyle: 'normal',
804
+ fontWeight: 700,
805
+ fontSize: 13,
806
+ lineHeight: '15px',
807
+ color: '#ffffff',
808
+ }}
792
809
  onClick={(e) => {
793
810
  e.stopPropagation();
794
811
  onRequestOffer();
795
812
  }}
796
813
  >
797
- <DocCheckIcon size={16} />
814
+ {/* exact design icon (single self-contained SVG) — replaces the composed lucide
815
+ file-text + check-circle badge (DocCheckIcon), which is still used as-is on VDP's own
816
+ "Angebot anfragen" buttons; this swap is scoped to the search-card CTA only.
817
+ flex:none for the same shrink reason as the "Adjust rate" icon above. */}
818
+ <svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ flex: 'none' }}>
819
+ <path
820
+ d="M6.90625 3.65625H15.275L19.9062 8.2875V20.7188C19.9062 21.6162 19.1787 22.3438 18.2812 22.3438H6.90625C6.00879 22.3438 5.28125 21.6162 5.28125 20.7188V5.28125C5.28125 4.38379 6.00879 3.65625 6.90625 3.65625Z"
821
+ stroke="white"
822
+ strokeLinecap="round"
823
+ strokeLinejoin="round"
824
+ />
825
+ <path d="M15.0312 3.85938V8.53125H19.7031" stroke="white" strokeLinecap="round" strokeLinejoin="round" />
826
+ <path d="M8.9375 11.375H14.625" stroke="white" strokeLinecap="round" />
827
+ <path d="M8.9375 14.625H13.4062" stroke="white" strokeLinecap="round" />
828
+ <path d="M8.9375 17.875H12.5938" stroke="white" strokeLinecap="round" />
829
+ <path
830
+ d="M18.4844 23.1562C20.8402 23.1562 22.75 21.2465 22.75 18.8906C22.75 16.5348 20.8402 14.625 18.4844 14.625C16.1285 14.625 14.2188 16.5348 14.2188 18.8906C14.2188 21.2465 16.1285 23.1562 18.4844 23.1562Z"
831
+ fill="#005CFF"
832
+ stroke="white"
833
+ />
834
+ <path d="M17.0625 18.3219L18.5656 19.825L23.7656 14.625" stroke="white" strokeLinecap="round" strokeLinejoin="round" />
835
+ <path d="M21.6533 17.875L23.2783 16.25" stroke="#005CFF" strokeLinecap="round" strokeLinejoin="round" />
836
+ <path d="M20.1904 17.0625L21.8154 15.4375" stroke="#005CFF" strokeLinecap="round" strokeLinejoin="round" />
837
+ </svg>
798
838
  {t('autobahn:finance.secureOffer')}
799
839
  </Button>
800
840
  <Button
801
841
  variant="outlined"
802
- icon="phone"
803
- style={{ flex: 'none', width: 44, padding: 0 }}
842
+ style={{
843
+ flex: '0 0 auto',
844
+ width: 40,
845
+ height: 40,
846
+ background: '#ffffff',
847
+ border: '1.6px solid #005cff',
848
+ borderRadius: 16,
849
+ padding: 0,
850
+ }}
804
851
  onClick={(e) => {
805
852
  e.stopPropagation();
806
853
  onCall();
807
854
  }}
808
- />
855
+ >
856
+ {/* exact design icon (not lucide's "phone") — no class="lucide", so the
857
+ .card-cta-row .mbtn .lucide 15px override doesn't touch it; the rect-clip from the
858
+ source SVG was a full-viewBox no-op (already inside 0-20/0-20) and dropped, since a
859
+ static clipPath id would collide across the many CarCard instances on one search page. */}
860
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
861
+ <path
862
+ d="M18.3332 14.1004V16.6004C18.3341 16.8325 18.2866 17.0622 18.1936 17.2749C18.1006 17.4875 17.9643 17.6784 17.7933 17.8353C17.6222 17.9922 17.4203 18.1116 17.2005 18.186C16.9806 18.2603 16.7477 18.288 16.5165 18.2671C13.9523 17.9881 11.4892 17.1119 9.32486 15.7087C7.31139 14.4293 5.60431 12.7222 4.32486 10.7087C2.91679 8.5346 2.04037 6.0595 1.76653 3.48374C1.7457 3.2533 1.77309 3.02104 1.84695 2.80176C1.9208 2.58248 2.03951 2.38098 2.1955 2.21009C2.3515 2.0392 2.54137 1.90266 2.75302 1.80917C2.96468 1.71569 3.19348 1.66729 3.42486 1.66707H5.92486C6.32928 1.66309 6.72136 1.80631 7.028 2.07002C7.33464 2.33373 7.53493 2.69995 7.59153 3.10041C7.69153 3.85041 7.86653 4.57541 8.11653 5.26707C8.22865 5.56534 8.25291 5.8895 8.18645 6.20114C8.11999 6.51278 7.96558 6.79883 7.74153 7.02541L6.74153 8.03374C7.95617 10.2329 9.76739 12.0441 11.9665 13.2587L12.9749 12.2587C13.2014 12.0347 13.4875 11.8803 13.7991 11.8138C14.1108 11.7474 14.4349 11.7716 14.7332 11.8837C15.4249 12.1337 16.1499 12.3087 16.8999 12.4087C17.3047 12.4658 17.6744 12.6697 17.9386 12.9817C18.2029 13.2936 18.3433 13.6917 18.3332 14.1004Z"
863
+ stroke="#005cff"
864
+ strokeWidth={1.66667}
865
+ strokeLinecap="round"
866
+ strokeLinejoin="round"
867
+ />
868
+ </svg>
869
+ </Button>
809
870
  <Button
810
871
  variant="outlined"
811
- icon="sliders-horizontal"
812
- style={{ flex: 1.5 }}
872
+ style={{
873
+ // same "starting size that can still grow/shrink" reasoning as "Request offer" above.
874
+ flex: '1 1 125px',
875
+ height: 40,
876
+ background: '#ffffff',
877
+ border: '1.6px solid #5b6b7f',
878
+ borderRadius: 16,
879
+ padding: '0 6px',
880
+ fontFamily: 'Arial',
881
+ fontStyle: 'normal',
882
+ fontWeight: 700,
883
+ fontSize: 13,
884
+ lineHeight: '15px',
885
+ color: '#5b6b7f',
886
+ }}
813
887
  onClick={(e) => {
814
888
  e.stopPropagation();
815
889
  onOpenRateAdjust(mode);
816
890
  }}
817
891
  >
892
+ {/* exact design icon (not lucide's "sliders-horizontal") — colors are baked into the SVG
893
+ itself (matches the button's #5b6b7f border/text), not currentColor. flex:none is
894
+ load-bearing: this button is a flex row (icon + the "Adjust rate" label) inside a fixed
895
+ 125px width, and without it the icon was a shrinkable flex item — flexbox squeezed its
896
+ WIDTH down to fit the text (rendering ~12.94x18, squashed) while its literal height
897
+ attribute stayed 18. Lucide icons here dodge this via the unrelated .card-cta-row .mbtn
898
+ .lucide CSS rule (flex: none), which doesn't match a plain <svg> with no lucide class. */}
899
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" style={{ flex: 'none' }}>
900
+ <path d="M3 5.25H15" stroke="#5B6B7F" strokeWidth={1.91667} strokeLinecap="round" strokeLinejoin="round" />
901
+ <path
902
+ d="M6 6.75C6.82843 6.75 7.5 6.07843 7.5 5.25C7.5 4.42157 6.82843 3.75 6 3.75C5.17157 3.75 4.5 4.42157 4.5 5.25C4.5 6.07843 5.17157 6.75 6 6.75Z"
903
+ fill="#FFFEFE"
904
+ stroke="#5B6B7F"
905
+ strokeLinecap="round"
906
+ strokeLinejoin="round"
907
+ />
908
+ <path d="M3 9H15" stroke="#5B6B7F" strokeWidth={1.91667} strokeLinecap="round" strokeLinejoin="round" />
909
+ <path
910
+ d="M12.4565 10.5C13.285 10.5 13.9565 9.82843 13.9565 9C13.9565 8.17157 13.285 7.5 12.4565 7.5C11.6281 7.5 10.9565 8.17157 10.9565 9C10.9565 9.82843 11.6281 10.5 12.4565 10.5Z"
911
+ fill="white"
912
+ stroke="#5B6B7F"
913
+ strokeLinecap="round"
914
+ strokeLinejoin="round"
915
+ />
916
+ <path d="M3 12.75H15" stroke="#5B6B7F" strokeWidth={1.91667} strokeLinecap="round" strokeLinejoin="round" />
917
+ <path
918
+ d="M9.81543 14.25C10.6439 14.25 11.3154 13.5784 11.3154 12.75C11.3154 11.9216 10.6439 11.25 9.81543 11.25C8.987 11.25 8.31543 11.9216 8.31543 12.75C8.31543 13.5784 8.987 14.25 9.81543 14.25Z"
919
+ fill="white"
920
+ stroke="#5B6B7F"
921
+ strokeLinecap="round"
922
+ strokeLinejoin="round"
923
+ />
924
+ </svg>
818
925
  {t('autobahn:finance.adjustRate')}
819
926
  </Button>
820
927
  </div>
@@ -86,7 +86,7 @@
86
86
  "priceRatingAria": "Preisbewertung: {{rating}}",
87
87
  "detailsShow": "Details anzeigen",
88
88
  "paymentLabel": "Zahlung",
89
- "paymentValue": "Zahlung per Überweisung",
89
+ "paymentValue": "Überweisung",
90
90
  "warrantyLabel": "Garantie",
91
91
  "warrantyValue": "1 Jahr inklusive",
92
92
  "deliveryLabel": "Lieferung",
@@ -105,6 +105,7 @@
105
105
  "callCta": "Anrufen",
106
106
  "finalInstallmentToggle": "Mit Schlussrate",
107
107
  "addServicesToggle": "Rundum-Sorglos-Paket",
108
+ "moreInfoAria": "Weitere Informationen zu {{label}}",
108
109
  "addServicesPerMonth": "+{{value}} € / Monat",
109
110
  "addServicesOnce": "+{{value}} €",
110
111
  "addServicesIncluded": "inklusive",
@@ -105,6 +105,7 @@
105
105
  "callCta": "Call",
106
106
  "finalInstallmentToggle": "With final installment",
107
107
  "addServicesToggle": "All-round care package",
108
+ "moreInfoAria": "More information about {{label}}",
108
109
  "addServicesPerMonth": "+€{{value}} / month",
109
110
  "addServicesOnce": "+€{{value}}",
110
111
  "addServicesIncluded": "included",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/fe-ui-core-autobahn",
3
- "version": "1.0.114",
3
+ "version": "1.0.116",
4
4
  "description": "auto.de md3 (Autobahn) design system — UI primitives, cards, chrome, VDP modules, theme CSS and locales. Ships raw TypeScript source (like fe-ui-core before it); each consumer's bundler compiles it.",
5
5
  "main": "index.ts",
6
6
  "license": "UNLICENSED",
package/theme/md3.css CHANGED
@@ -3398,11 +3398,17 @@ line-height: 31px;
3398
3398
  transform: translateY(-50%);
3399
3399
  width: 32px;
3400
3400
  height: 32px;
3401
- border-radius: var(--md-r-full);
3402
- border: 0;
3403
- 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);
3404
3411
  color: var(--md-on-surface);
3405
- box-shadow: var(--md-e1);
3406
3412
  display: flex;
3407
3413
  align-items: center;
3408
3414
  justify-content: center;
@@ -3411,11 +3417,20 @@ line-height: 31px;
3411
3417
  transition: opacity var(--dur-1);
3412
3418
  z-index: 2;
3413
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
+ }
3414
3429
  .md3 .car-arrow--l {
3415
- left: 22px;
3430
+ left: 10px;
3416
3431
  }
3417
3432
  .md3 .car-arrow--r {
3418
- right: 22px;
3433
+ right: 10px;
3419
3434
  }
3420
3435
  .md3 .cardcar:hover .car-arrow {
3421
3436
  opacity: 1;
@@ -3435,6 +3450,14 @@ line-height: 31px;
3435
3450
  border: 1px solid #D4DDE7;
3436
3451
  box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.08);
3437
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
+ }
3438
3461
 
3439
3462
  /* ==========================================================================
3440
3463
  RESPONSIVE LAYOUT LAYER
@@ -3458,7 +3481,15 @@ line-height: 31px;
3458
3481
  }
3459
3482
  .md3 .field-grid-2 {
3460
3483
  display: grid;
3461
- 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);
3462
3493
  gap: 12px;
3463
3494
  }
3464
3495
  .md3 .cards-3 {
@@ -5305,6 +5336,11 @@ line-height: 31px;
5305
5336
  .md3 .card-cta-row .mbtn { font-size: 12.5px; padding: 0 10px; gap: 5px; min-width: 0; }
5306
5337
  .md3 .card-cta-row .mbtn span, .md3 .card-cta-row .mbtn { overflow: hidden; text-overflow: ellipsis; }
5307
5338
  .md3 .card-cta-row .mbtn .lucide { width: 15px; height: 15px; flex: none; }
5339
+ /* the phone/"Rate anpassen" outlined buttons set their own explicit `border` (1.6px solid) inline —
5340
+ .mbtn--outlined's inset box-shadow (the DEFAULT way that class draws its border everywhere else)
5341
+ stacks on top of it as a second, slightly offset ring ("double border"). Suppress just the
5342
+ shadow here; the real border keeps rendering from each button's own inline style. */
5343
+ .md3 .card-cta-row .mbtn--outlined { box-shadow: none; }
5308
5344
  /* muted buyer activity (#5) */
5309
5345
  .md3 .pp-activity {
5310
5346
  display: flex;
@@ -6297,14 +6333,29 @@ line-height: 31px;
6297
6333
  /* Visible icon glyphs 24-28px (spec range) — the shared .icon-btn/.mbtn rules force 20px/18px
6298
6334
  everywhere else in the app, which is fine there but read visibly smaller than the Figma
6299
6335
  mobile header reference. */
6300
- .md3 header .icon-btn {
6301
- width: 48px;
6302
- height: 48px;
6303
- }
6304
6336
  .md3 header .icon-btn .lucide {
6305
6337
  width: 24px;
6306
6338
  height: 24px;
6307
6339
  }
6340
+ /* Bug: "Header distribution" — Figma has the logo starting at x=49 and the search/phone/account
6341
+ icon group starting at x=246 (390px reference), but the logo was rendering ~25px too far
6342
+ right and the icon group ~15-17px too far left. Root cause: this rule used to ALSO force
6343
+ hamburger/search/phone's own icon-btn box to 48×48 (copied from .topbar-account-btn just
6344
+ below, whose 48×48 IS spec'd — WCAG/Header-System touch-target — but nothing here documented
6345
+ the same for these three). That extra 8px of box per icon (48 vs the base .icon-btn's 40px)
6346
+ pushed the logo right (hamburger wider) and pulled the action-icon group left (search/phone
6347
+ wider, eating into the row before the right-aligned account button). Dropped back to the
6348
+ base 40×40 box — glyph size above is unaffected, so nothing looks visually smaller — and
6349
+ restored the lost touch-target room with an invisible ::before hit-slop instead (inset:-4px
6350
+ = 48×48 effective tap zone, same technique as the SRP card-carousel arrows use). */
6351
+ .md3 header .icon-btn {
6352
+ position: relative;
6353
+ }
6354
+ .md3 header .icon-btn::before {
6355
+ content: '';
6356
+ position: absolute;
6357
+ inset: -4px;
6358
+ }
6308
6359
  .md3 header .topbar-account-btn .lucide {
6309
6360
  width: 24px;
6310
6361
  height: 24px;
@@ -6319,6 +6370,13 @@ line-height: 31px;
6319
6370
  .md3 .topbar-logo {
6320
6371
  width: 122px;
6321
6372
  height: auto;
6373
+ /* pulls the logo from x≈68 (12px padding + the hamburger's 40px box + the .topbar-container
6374
+ row's own 16px gap, set inline and shared with other pairs in that row — not touched here,
6375
+ see the icon-btn comment above for why) to Figma's x=49. A negative margin on THIS one item
6376
+ is deliberate: shrinking the shared row gap instead would have also pulled the search/
6377
+ phone/account cluster on the right closer together, which is already correct after the
6378
+ icon-btn fix above and must stay untouched. */
6379
+ margin-left: -19px;
6322
6380
  }
6323
6381
  /* Gap between action icons 2-6px (spec: "Abstand Aktionen") — separate from the wider gap the
6324
6382
  hamburger/logo/nav still use (that's the outer .container's own gap, untouched here). */
@@ -6640,6 +6698,13 @@ line-height: 31px;
6640
6698
  .md3 .cards-2 {
6641
6699
  grid-template-columns: 1fr;
6642
6700
  }
6701
+ /* fe-landing (/angebot) TopResults + Alternatives grids — shared side gutter,
6702
+ same 12px pattern as .md3 .container elsewhere. .cards-3 is untouched (not
6703
+ used with this gutter treatment; only .cards-2 callers asked for it). */
6704
+ .md3 .cards-2 {
6705
+ padding-left: 12px !important;
6706
+ padding-right: 12px !important;
6707
+ }
6643
6708
  /* listing card stacks: image (4:3) → info → price */
6644
6709
  .md3 .listcard {
6645
6710
  grid-template-columns: 1fr;
@@ -6707,16 +6772,30 @@ line-height: 31px;
6707
6772
  }
6708
6773
  .md3 .srp-bar {
6709
6774
  position: sticky;
6710
- /* was 64px (docked under the always-visible header) — now 0: once the header slides away
6711
- (autoHideOnScroll, TopBar.tsx) this bar takes its place at the very top instead of leaving
6712
- a gap, and sits flush under it again the moment the header slides back in. */
6713
- top: 0px;
6775
+ /* default: docked directly under the always-visible header (its 64px height). Same 0.25s
6776
+ duration as .topbar's own transform transition (below) — the header slides in/out over
6777
+ 0.25s, so this bar's top must animate in step with it, not jump instantly, or a gap (bar
6778
+ already at 64px, header still sliding in) or an overlap (bar already at 0, header still
6779
+ sliding out) flashes for that quarter-second. */
6780
+ top: 64px;
6781
+ transition: top 0.25s;
6714
6782
  z-index: 20;
6715
6783
  background: #fff;
6716
6784
  padding: 8px 0;
6717
6785
  margin: 0 0 8px;
6718
6786
  box-shadow: 0 6px 8px -8px rgba(0, 0, 0, 0.3);
6719
6787
  }
6788
+ /* once the header slides away on scroll-down (autoHideOnScroll, TopBar.tsx adds
6789
+ .topbar--hidden), this bar takes its place at the very top instead of leaving a 64px gap.
6790
+ The header's box stays in the layout flow even while hidden (transform doesn't affect
6791
+ `position: sticky`'s slot — see the .topbar--hidden rule above), so without this override
6792
+ the bar's own sticky top would also need to jump — and it must jump BACK to 64px the moment
6793
+ scroll-up brings the header back (topbar--hidden removed), or the reappearing header
6794
+ (z-index 50) sticks at the same top:0 slot and paints over/hides this bar (z-index 20)
6795
+ underneath it instead of sitting above it. */
6796
+ .md3:has(.topbar--hidden) .srp-bar {
6797
+ top: 0px;
6798
+ }
6720
6799
  /* full-bleed white to the true viewport edges, WITHOUT moving .srp-bar's own text — it stays
6721
6800
  inside .srp-main's own side padding, this pseudo-element just paints white past it on both
6722
6801
  sides so no gap of page background shows while the bar is sticky. NOT a box-shadow spread
@@ -6738,7 +6817,7 @@ line-height: 31px;
6738
6817
  z-index: -1;
6739
6818
  }
6740
6819
  .md3 .field-grid-2 {
6741
- grid-template-columns: 1fr;
6820
+ grid-template-columns: minmax(0, 1fr);
6742
6821
  gap: 10px;
6743
6822
  }
6744
6823
  .md3 .hero-search {
@@ -7261,11 +7340,13 @@ line-height: 31px;
7261
7340
  z-index: 3;
7262
7341
  }
7263
7342
 
7264
- /* VDP main wrapper: phone gets a tighter top gap (10px) and narrower side gutters
7265
- (14px) than the 14/24px desktop default set inline. */
7343
+ /* VDP main wrapper: phone gets a tighter top gap (0, was 15px) and narrower side gutters
7344
+ (10px, matches production) than the 15/24px desktop default set inline — title, highlight
7345
+ chips and the pricing card all inherit this from their shared .vdp-main ancestor, so there's
7346
+ no separate per-element rule to keep in sync with this one. */
7266
7347
  @media (max-width: 600px) {
7267
7348
  .md3 .vdp-main {
7268
- padding: 0 10px 48px !important;
7349
+ padding: 0 14px 48px !important;
7269
7350
  }
7270
7351
  .md3 .vdp-push-badges {
7271
7352
  gap: 6px;
@@ -7445,6 +7526,28 @@ color: #005CFF;
7445
7526
  line-height: 17px;
7446
7527
  color: #1D2A38;
7447
7528
  }
7529
+ /* all three tabs (Finanzierung/Leasing/Kaufen), desktop + mobile — ToggleRow is the one shared
7530
+ render for all of them. Info icon sits raised near the top of the label, like a small
7531
+ superscript badge, instead of centered against the text. */
7532
+ .md3 .fin-toggle-addservices .md-title-sm .lucide {
7533
+ position: relative;
7534
+ top: -7px;
7535
+ left: -2px;
7536
+ }
7537
+ /* tooltip-trigger touch target (all 3 tabs, incl. --cash — an a11y correctness fix, not a
7538
+ per-tab visual choice like the icon nudge above). Trigger is now the 15x15 icon alone (the
7539
+ label text beside it is inert), so BOTH axes need padding out to 48px, not just the vertical
7540
+ one. ::after is positioned off layout flow (doesn't grow the row or push the label/price/
7541
+ switch siblings over) but still real estate for hit-testing — clicks/taps anywhere in its box
7542
+ count as hitting the trigger span itself. (15 + 17 + 17 = 49, clears the 48px minimum.) */
7543
+ .md3 .fin-toggle-info-trigger {
7544
+ position: relative;
7545
+ }
7546
+ .md3 .fin-toggle-info-trigger::after {
7547
+ content: '';
7548
+ position: absolute;
7549
+ inset: -17px;
7550
+ }
7448
7551
  .md3 .fin-toggle-addservices > div {
7449
7552
  gap: 13px !important;
7450
7553
  }
@@ -7485,6 +7588,12 @@ color: #005CFF;
7485
7588
  top: auto;
7486
7589
  bottom: auto;
7487
7590
  }
7591
+ /* Kaufen tab, desktop sidebar card (.fin-calc-card, NOT the .fin-modal sheet above) — the row
7592
+ sits lower in this card than the financing/leasing rows do, so the base "flush above" bottom:
7593
+ 100% opens noticeably higher than the trigger; drops it closer instead. */
7594
+ .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .fin-toggle-tooltip {
7595
+ bottom: -10%;
7596
+ }
7488
7597
  .md3 .fin-modal .show-mobile .fin-toggle-tooltip {
7489
7598
  border-radius: 11px;
7490
7599
  }
@@ -7535,9 +7644,6 @@ color: #005CFF;
7535
7644
  letter-spacing: 0.4px;
7536
7645
  color: var(--md-on-surface-variant);
7537
7646
  }
7538
- .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .md-title-sm .lucide {
7539
- display: none;
7540
- }
7541
7647
  .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash .num,
7542
7648
  .md3 .fin-calc-card .fin-toggle-addservices.fin-toggle-addservices--cash > div > span:last-child {
7543
7649
  font-family: var(--md-font-display) !important;
@@ -8765,6 +8871,23 @@ color: #005CFF;
8765
8871
  grid-template-areas: "rate evalcol" "cond cond";
8766
8872
  align-items: start;
8767
8873
  }
8874
+ /* Bug: switching the Financing/Leasing/Buy tabs made the price row, rating pill and the two
8875
+ CTA buttons below jump up/down, because the row above them had a different rendered height
8876
+ per tab: (a) .fin-cond (the "0 € Anzahlung · 60 Monate · ..." terms line) renders NOTHING on
8877
+ Buy (isCash hides it), collapsing to 0 height instead of one text line, and (b)
8878
+ .fin-price-grid--cash below zeroes the row-gap that the other two tabs keep. The two rules
8879
+ below cancel each of those, so Financing/Leasing/Buy all reserve the same total height.
8880
+ (A first pass also force-reserved height on .fin-rate-main itself, to additionally cover the
8881
+ rare case of an extra struck list-price line on Buy when the car has a discount — isCash &&
8882
+ showStrike in FinanceCalculator.tsx. Dropped: since Financing/Leasing/undiscounted-Buy don't
8883
+ need that extra room, it left a visible empty gap under the price on every normal tab just to
8884
+ pre-cover one edge case. That specific combination can still shift by the strike line's own
8885
+ height; every other tab switch is now jump-free without the dead space.) */
8886
+ .md3 .fin-calc-card .fin-price-grid.fin-price-grid--cash {
8887
+ /* same 12px row-gap as Financing/Leasing (the base .fin-price-grid rule) — the 0px gap on
8888
+ .fin-price-grid--cash further up this file otherwise pulls the CTA row 12px closer on Buy. */
8889
+ gap: 12px 15px;
8890
+ }
8768
8891
  .md3 .fin-eval-col {
8769
8892
  display: flex;
8770
8893
  flex-direction: column;
@@ -8799,6 +8922,11 @@ color: #005CFF;
8799
8922
  font-weight: 400;
8800
8923
  font-size: 13px;
8801
8924
  line-height: 15px;
8925
+ /* matches the line-height above: on the Buy tab this div renders empty (its "0 € Anzahlung ·
8926
+ 60 Monate · ..." text is Financing/Leasing-only), so without a floor it collapsed to 0
8927
+ height instead of the one text line the other two tabs occupy — part of the cross-tab
8928
+ layout-jump fix, see the .fin-rate-main comment above. */
8929
+ min-height: 15px;
8802
8930
  color: #111820;
8803
8931
  margin: 0px 0px;
8804
8932
  margin-bottom: 12px;
@@ -10583,7 +10711,7 @@ color: #005CFF;
10583
10711
  right: 11px !important;
10584
10712
  }
10585
10713
  .md3 .cc-card .cc-badges {
10586
- top: 15px !important;
10714
+ top: 18px !important;
10587
10715
  }
10588
10716
 
10589
10717
  /* 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[];
@@ -147,10 +145,16 @@ const CardCarousel: React.FunctionComponent<CardCarouselProps> = ({
147
145
  {n > 1 && (
148
146
  <>
149
147
  <button className="car-arrow car-arrow--l" aria-label="zurück" onClick={(e) => go(-1, e)}>
150
- <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>
151
153
  </button>
152
154
  <button className="car-arrow car-arrow--r" aria-label="weiter" onClick={(e) => go(1, e)}>
153
- <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>
154
158
  </button>
155
159
  <div
156
160
  className="cardcar-dots"