patx/demo

update jdb

Commit 1f157a7 · patx · 2026-06-15T18:26:08-04:00

Changeset
1f157a7b1fc2e3da259506b517c574af48fc5329
Parents
1078efa0c3bb4e7cbb00df806a504eb5101a0ac8

View source at this commit

Comments

No comments yet.

Log in to comment

Diff

diff --git a/docs/jdb-11-hero.webp b/docs/jdb-11-hero.webp
new file mode 100644
index 0000000..f590641
Binary files /dev/null and b/docs/jdb-11-hero.webp differ
diff --git a/docs/jdb-11.webp b/docs/jdb-11.webp
new file mode 100644
index 0000000..ba00672
Binary files /dev/null and b/docs/jdb-11.webp differ
diff --git a/docs/jdb-12-hero.webp b/docs/jdb-12-hero.webp
new file mode 100644
index 0000000..df5281e
Binary files /dev/null and b/docs/jdb-12-hero.webp differ
diff --git a/docs/jdb-12.webp b/docs/jdb-12.webp
new file mode 100644
index 0000000..790ebcc
Binary files /dev/null and b/docs/jdb-12.webp differ
diff --git a/docs/jdb-13-hero.webp b/docs/jdb-13-hero.webp
new file mode 100644
index 0000000..1b9df64
Binary files /dev/null and b/docs/jdb-13-hero.webp differ
diff --git a/docs/jdb-13.webp b/docs/jdb-13.webp
new file mode 100644
index 0000000..0b22044
Binary files /dev/null and b/docs/jdb-13.webp differ
diff --git a/docs/jdb-14-hero.webp b/docs/jdb-14-hero.webp
new file mode 100644
index 0000000..1ade7c5
Binary files /dev/null and b/docs/jdb-14-hero.webp differ
diff --git a/docs/jdb-14.webp b/docs/jdb-14.webp
new file mode 100644
index 0000000..48fa8f6
Binary files /dev/null and b/docs/jdb-14.webp differ
diff --git a/docs/jdb-15-hero.webp b/docs/jdb-15-hero.webp
new file mode 100644
index 0000000..42042f5
Binary files /dev/null and b/docs/jdb-15-hero.webp differ
diff --git a/docs/jdb-15.webp b/docs/jdb-15.webp
new file mode 100644
index 0000000..f03e08c
Binary files /dev/null and b/docs/jdb-15.webp differ
diff --git a/docs/jdb-after.webp b/docs/jdb-after.webp
new file mode 100644
index 0000000..827c5ed
Binary files /dev/null and b/docs/jdb-after.webp differ
diff --git a/docs/jdb-before.webp b/docs/jdb-before.webp
new file mode 100644
index 0000000..95d826d
Binary files /dev/null and b/docs/jdb-before.webp differ
diff --git a/docs/jdb-hero-2-optimized.webp b/docs/jdb-hero-2-optimized.webp
deleted file mode 100644
index be7457a..0000000
Binary files a/docs/jdb-hero-2-optimized.webp and /dev/null differ
diff --git a/docs/jdb-hero-3.webp b/docs/jdb-hero-3.webp
deleted file mode 100644
index a473cd2..0000000
Binary files a/docs/jdb-hero-3.webp and /dev/null differ
diff --git a/docs/jdb-hero-4.webp b/docs/jdb-hero-4.webp
deleted file mode 100644
index 37374f7..0000000
Binary files a/docs/jdb-hero-4.webp and /dev/null differ
diff --git a/docs/jdb-hero-after.webp b/docs/jdb-hero-after.webp
index faa82e5..6bef335 100644
Binary files a/docs/jdb-hero-after.webp and b/docs/jdb-hero-after.webp differ
diff --git a/docs/jdb-logo.png b/docs/jdb-logo.png
deleted file mode 100644
index dcdcab1..0000000
Binary files a/docs/jdb-logo.png and /dev/null differ
diff --git a/docs/jdb-logo.webp b/docs/jdb-logo.webp
new file mode 100644
index 0000000..3cc42f8
Binary files /dev/null and b/docs/jdb-logo.webp differ
diff --git a/docs/jdb-og.jpg b/docs/jdb-og.jpg
deleted file mode 100644
index df679e9..0000000
Binary files a/docs/jdb-og.jpg and /dev/null differ
diff --git a/docs/jdb-og.webp b/docs/jdb-og.webp
new file mode 100644
index 0000000..f3172d6
Binary files /dev/null and b/docs/jdb-og.webp differ
diff --git a/docs/jdb.html b/docs/jdb.html
index 33d7924..c2473c6 100644
--- a/docs/jdb.html
+++ b/docs/jdb.html
@@ -8,14 +8,14 @@
 <meta property="og:type" content="website">
 <meta property="og:title" content="JDB Landscape — Burlington, ON">
 <meta property="og:description" content="Owner-operated landscaping, hardscapes and lawn care across Burlington and nearby service areas.">
-<meta property="og:image" content="jdb-og.jpg">
+<meta property="og:image" content="jdb-og.webp">
 <meta property="og:image:width" content="1200">
 <meta property="og:image:height" content="630">
 <meta property="og:image:alt" content="JDB Landscape services in Burlington, Ontario">
 <meta name="twitter:card" content="summary_large_image">
 <meta name="twitter:title" content="JDB Landscape — Burlington, ON">
 <meta name="twitter:description" content="Owner-operated landscaping, hardscapes and lawn care across Burlington and nearby service areas.">
-<meta name="twitter:image" content="jdb-og.jpg">
+<meta name="twitter:image" content="jdb-og.webp">
 <meta name="twitter:image:alt" content="JDB Landscape services in Burlington, Ontario">
 <link rel="preconnect" href="https://fonts.googleapis.com">
 <link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,600;0,700;1,300;1,600&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
@@ -185,25 +185,23 @@ nav.menu-open .nav-menu-toggle span:nth-child(3) { transform: translateY(-7px) r
   opacity: 0;
   transform: scale(1.12);
   filter: saturate(.9) contrast(1.04);
-  animation: heroZoomFade 32s infinite ease-in-out;
+  animation: heroZoomFade 20s infinite ease-in-out;
   will-change: opacity, transform;
 }
-.hero-slide:nth-child(1) { animation-delay: -.64s; }
-.hero-slide:nth-child(2) { animation-delay: 7.36s; }
-.hero-slide:nth-child(3) { animation-delay: 15.36s; }
-.hero-slide:nth-child(4) { animation-delay: 23.36s; }
+.hero-slide:nth-child(1) { animation-delay: -.4s; }
+.hero-slide:nth-child(2) { animation-delay: 9.6s; }
 @keyframes heroZoomFade {
   0% {
     opacity: 0;
     transform: scale(1.12);
   }
-  2% {
+  3% {
     opacity: 1;
   }
-  22% {
+  45% {
     opacity: 1;
   }
-  27% {
+  52% {
     opacity: 0;
     transform: scale(1);
   }
@@ -543,7 +541,7 @@ nav.menu-open .nav-menu-toggle span:nth-child(3) { transform: translateY(-7px) r
 .gallery-bento {
   display: grid;
   grid-template-columns: repeat(12, 1fr);
-  grid-template-rows: 240px 180px 280px;
+  grid-template-rows: 260px 280px;
   gap: 8px;
 }
 .gitem {
@@ -572,14 +570,11 @@ nav.menu-open .nav-menu-toggle span:nth-child(3) { transform: translateY(-7px) r
 }
 .gitem:hover .gitem-label { opacity: 1; transform: translateY(0); }
 
-.ga { grid-column: 1 / 6; grid-row: 1; }
-.gb { grid-column: 6 / 9; grid-row: 1; }
+.ga { grid-column: 1 / 5; grid-row: 1; }
+.gb { grid-column: 5 / 9; grid-row: 1; }
 .gc { grid-column: 9 / 13; grid-row: 1 / 3; }
-.gd { grid-column: 1 / 4; grid-row: 2 / 4; }
-.ge { grid-column: 4 / 7; grid-row: 2; }
-.gf { grid-column: 7 / 9; grid-row: 2; }
-.gg { grid-column: 4 / 9; grid-row: 3; }
-.gh { grid-column: 9 / 13; grid-row: 3; }
+.gd { grid-column: 1 / 7; grid-row: 2; }
+.ge { grid-column: 7 / 9; grid-row: 2; }
 .image-modal {
   position: fixed; inset: 0; z-index: 1000;
   display: grid; place-items: center;
@@ -933,9 +928,107 @@ footer {
   #services { padding: 72px 24px; }
   .services-watermark { top: 26px; right: 18px; font-size: 118px; }
   .services-header { grid-template-columns: 1fr; gap: 24px; }
-  .service-row { grid-template-columns: 48px 1fr; }
-  .service-desc-col, .service-tag { display: none; }
-  .service-row:hover { margin: 0 -24px; padding: 32px 24px; }
+  .service-list {
+    display: grid;
+    gap: 12px;
+  }
+  .service-row {
+    position: relative;
+    grid-template-columns: 48px 1fr;
+    align-items: start;
+    gap: 14px 16px;
+    min-height: 94px;
+    padding: 22px 18px;
+    background: var(--white);
+    border: 1px solid rgba(200,191,176,.55);
+    border-radius: 8px;
+    box-shadow: 0 10px 26px rgba(27,31,23,.04);
+    cursor: pointer;
+    transform-origin: center;
+    transition: background .2s, border-color .2s, box-shadow .2s;
+  }
+  .service-row:first-child { border-top: 1px solid rgba(200,191,176,.55); }
+  .service-row:hover {
+    margin: 0;
+    padding: 22px 18px;
+    background: var(--white);
+  }
+  .service-row::after {
+    content: "+";
+    position: absolute; top: 18px; right: 18px;
+    width: 28px; height: 28px;
+    display: flex; align-items: center; justify-content: center;
+    border: 1px solid rgba(27,31,23,.16);
+    border-radius: 50%;
+    color: var(--green);
+    font-size: 19px; line-height: 1;
+    transition: transform .25s, background .2s, color .2s, border-color .2s;
+  }
+  .service-row.is-flipped {
+    background: var(--bone);
+    border-color: rgba(59,91,60,.28);
+    box-shadow: 0 16px 36px rgba(27,31,23,.08);
+  }
+  .service-row.is-flipping { animation: serviceMobileFlip .34s ease; }
+  .service-row.is-flipped::after {
+    content: "×";
+    transform: rotate(90deg);
+    background: var(--green);
+    color: var(--white);
+    border-color: var(--green);
+  }
+  .service-row.is-flipped .service-icon-wrap,
+  .service-row:hover .service-icon-wrap {
+    background: var(--green);
+    color: var(--white);
+    border-color: var(--green);
+  }
+  .service-name-col {
+    padding-right: 34px;
+    line-height: 1.1;
+  }
+  .service-desc-col,
+  .service-tag {
+    grid-column: 2 / -1;
+    display: block;
+    overflow: hidden;
+    max-height: 0;
+    opacity: 0;
+    transform: rotateX(-8deg) translateY(-4px);
+    transform-origin: top;
+    transition: max-height .28s ease, opacity .2s, transform .28s ease, margin .2s;
+  }
+  .service-row.is-flipped .service-desc-col {
+    max-height: 150px;
+    opacity: 1;
+    transform: none;
+  }
+  .service-tag {
+    width: fit-content;
+    margin-top: -8px;
+    padding: 7px 10px;
+    background: rgba(199,122,66,.12);
+    color: var(--rust);
+    border: 1px solid rgba(199,122,66,.18);
+    border-radius: 999px;
+  }
+  .service-row.is-flipped .service-tag {
+    max-height: 34px;
+    opacity: 1;
+    transform: none;
+    margin-top: -4px;
+  }
+  @keyframes serviceMobileFlip {
+    0% { transform: rotateX(0) scale(1); }
+    45% { transform: rotateX(7deg) scale(.985); }
+    100% { transform: rotateX(0) scale(1); }
+  }
+
+  @media (prefers-reduced-motion: reduce) {
+    .service-row.is-flipping { animation: none; }
+    .service-desc-col,
+    .service-tag { transition: none; }
+  }
 
   #about { grid-template-columns: 1fr; }
   .about-img-col { height: 300px; }
@@ -951,9 +1044,9 @@ footer {
   .before-after-handle { width: 42px; height: 42px; }
   .gallery-bento {
     grid-template-columns: 1fr 1fr;
-    grid-template-rows: 180px 180px 180px 180px;
+    grid-template-rows: 180px 180px 180px;
   }
-  .ga,.gb,.gc,.gd,.ge,.gf,.gg,.gh { grid-column: auto; grid-row: auto; }
+  .ga,.gb,.gc,.gd,.ge { grid-column: auto; grid-row: auto; }
 
   #areas { padding: 72px 24px; }
   .areas-inner { grid-template-columns: 1fr; }
@@ -975,7 +1068,7 @@ footer {
 <!-- ─── NAV ─── -->
 <nav id="mainNav" aria-label="Primary navigation">
   <a href="#hero" class="nav-wordmark">
-    <img class="nav-logo-image" src="jdb-logo.png" width="400" height="398" alt="JDB Landscape">
+    <img class="nav-logo-image" src="jdb-logo.webp" width="400" height="398" alt="JDB Landscape">
   </a>
   <ul class="nav-links" id="primaryNav">
     <li><a href="#services" data-nav-link>Services</a></li>
@@ -1014,9 +1107,7 @@ footer {
 <section id="hero">
   <div class="hero-slideshow" aria-hidden="true">
     <img class="hero-slide" src="jdb-hero-after.webp" width="1320" height="990" alt="" fetchpriority="high" decoding="async">
-    <img class="hero-slide" src="jdb-hero-2-optimized.webp" width="1920" height="1080" alt="" decoding="async">
-    <img class="hero-slide" src="jdb-hero-3.webp" width="1440" height="1082" alt="" decoding="async">
-    <img class="hero-slide" src="jdb-hero-4.webp" width="678" height="452" alt="" decoding="async">
+    <img class="hero-slide" src="jdb-11-hero.webp" width="1920" height="1080" alt="" decoding="async">
   </div>
 
   <div class="hero-content">
@@ -1032,7 +1123,7 @@ footer {
       </h1>
     </div>
     <div class="hero-right">
-      <p class="hero-sub">Hardscapes, lawn care, garden beds, sod & more. Owner-operated by James B. — on every job, every time.</p>
+      <p class="hero-sub">Hardscapes, lawn care, garden beds, sod & more. Built for clean edges, solid work, and lasting curb appeal.</p>
       <a href="#estimate" class="hero-cta">
         Get a free estimate
         <span class="hero-cta-arrow">→</span>
@@ -1073,7 +1164,7 @@ footer {
       <span class="eyebrow">What we do</span>
       <h2 class="section-h2">Every service,<br><em>done properly.</em></h2>
     </div>
-    <p class="section-desc sr sr-right">From the first consultation to the last stone set — James is hands-on through the entire process. No sub-contractors, no surprises.</p>
+    <p class="section-desc sr sr-right">From the first consultation to the last stone set, the work stays clear, practical, and detail-focused. No surprises.</p>
   </div>
 
   <div class="service-list">
@@ -1153,11 +1244,11 @@ footer {
   <div class="about-text-col sr sr-right">
     <span class="eyebrow">About JDB</span>
     <h2 class="about-h2">
-      James shows up.<br>
-      <em>Every single time.</em>
+      On Time,<br>
+      <em>Every Time.</em>
     </h2>
     <p class="about-body">
-      JDB Landscape is owner-operated by James B. — which means when you call, you talk to the person doing the work. Not a sales rep, not a dispatcher. James.
+      JDB Landscape is owner-operated by James B. — which means when you call, you talk to the person doing the work. Not a sales rep, not a dispatcher.
       <br><br>
       Built on word-of-mouth and repeat clients across Burlington and the surrounding region, JDB has earned a reputation for showing up on time, giving honest quotes, and not leaving until the job looks right.
     </p>
@@ -1182,9 +1273,9 @@ footer {
   </div>
 
   <div class="before-after sr sr-scale" data-before-after>
-    <img class="before-after-img" src="jdb_after.jpg" alt="Finished landscape project after JDB Landscape work">
+    <img class="before-after-img" src="jdb-after.webp" alt="Finished landscape project after JDB Landscape work">
     <div class="before-after-overlay" aria-hidden="true">
-      <img class="before-after-img" src="jdb_before.jpg" alt="">
+      <img class="before-after-img" src="jdb-before.webp" alt="">
     </div>
     <span class="before-after-label before">Before</span>
     <span class="before-after-label after">After</span>
@@ -1198,38 +1289,26 @@ footer {
   </div>
 
   <div class="gallery-bento">
-    <div class="gitem ga sr sr-scale sr-d1" role="button" tabindex="0" aria-label="View full size image of Interlock Patio in Burlington">
-      <img src="https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=800&q=80" alt="Interlock patio">
-      <div class="gitem-label">Interlock Patio · Burlington</div>
+    <div class="gitem ga sr sr-scale sr-d1" role="button" tabindex="0" aria-label="View full size image of Finished hardscape project">
+      <img src="jdb-11.webp" width="1320" height="1632" alt="Finished hardscape and landscape project" loading="lazy" decoding="async">
+      <div class="gitem-label">Hardscape Finish</div>
     </div>
-    <div class="gitem gb sr sr-scale sr-d2" role="button" tabindex="0" aria-label="View full size image of Garden Beds in Mississauga">
-      <img src="https://images.unsplash.com/photo-1416331108676-a22ccb276e35?w=600&q=80" alt="Mulch bed">
-      <div class="gitem-label">Garden Beds · Mississauga</div>
+    <div class="gitem gb sr sr-scale sr-d2" role="button" tabindex="0" aria-label="View full size image of Clean landscape edging">
+      <img src="jdb-12.webp" width="1320" height="1681" alt="Clean landscape edging and yard finish" loading="lazy" decoding="async">
+      <div class="gitem-label">Clean Edges</div>
     </div>
-    <div class="gitem gc sr sr-scale sr-d3" role="button" tabindex="0" aria-label="View full size image of Lawn Install in Oakville">
-      <img src="https://images.unsplash.com/photo-1585320806297-9794b3e4eeae?w=600&q=80" alt="Lawn">
-      <div class="gitem-label">Lawn Install · Oakville</div>
+    <div class="gitem gc sr sr-scale sr-d3" role="button" tabindex="0" aria-label="View full size image of Landscape project detail">
+      <img src="jdb-13.webp" width="1320" height="1693" alt="Landscape project detail with finished outdoor work" loading="lazy" decoding="async">
+      <div class="gitem-label">Project Detail</div>
     </div>
-    <div class="gitem gd sr sr-scale sr-d4" role="button" tabindex="0" aria-label="View full size image of Garden Reno in Burlington">
-      <img src="https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=600&q=80" alt="Garden planting">
-      <div class="gitem-label">Garden Reno · Burlington</div>
+    <div class="gitem gd sr sr-scale sr-d4" role="button" tabindex="0" aria-label="View full size image of Finished yard work">
+      <img src="jdb-14.webp" width="1299" height="1582" alt="Finished yard work and landscape cleanup" loading="lazy" decoding="async">
+      <div class="gitem-label">Yard Finish</div>
     </div>
-    <div class="gitem ge sr sr-scale sr-d5" role="button" tabindex="0" aria-label="View full size image of Property Clean-Up">
-      <img src="https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=600&q=80" alt="Property">
+    <div class="gitem ge sr sr-scale sr-d5" role="button" tabindex="0" aria-label="View full size image of Property cleanup">
+      <img src="jdb-15.webp" width="1193" height="1561" alt="Property cleanup and landscape refresh" loading="lazy" decoding="async">
       <div class="gitem-label">Property Clean-Up</div>
     </div>
-    <div class="gitem gf sr sr-scale sr-d6" role="button" tabindex="0" aria-label="View full size image of Lawn Maintenance">
-      <img src="https://images.unsplash.com/photo-1556909114-f6e7ad7d3136?w=500&q=80" alt="Edging">
-      <div class="gitem-label">Lawn Maintenance</div>
-    </div>
-    <div class="gitem gg sr sr-scale sr-d7" role="button" tabindex="0" aria-label="View full size image of Hardscape Walkway in Hamilton">
-      <img src="https://images.unsplash.com/photo-1513694203232-719a280e022f?w=900&q=80" alt="Hardscape walkway">
-      <div class="gitem-label">Hardscape Walkway · Hamilton</div>
-    </div>
-    <div class="gitem gh sr sr-scale sr-d8" role="button" tabindex="0" aria-label="View full size image of Sod Installation in Milton">
-      <img src="https://images.unsplash.com/photo-1462275646964-a0e3386b89fa?w=600&q=80" alt="Sod">
-      <div class="gitem-label">Sod Installation · Milton</div>
-    </div>
   </div>
 </section>
 
@@ -1313,7 +1392,7 @@ footer {
         <em>your property.</em>
       </h2>
       <p class="estimate-tagline">
-        No obligation. No sales pitch. Just James, giving you an honest look at what your project needs and what it'll cost — before you commit to anything.
+        No obligation. No sales pitch. Just a clear look at what your project needs and what it'll cost — before you commit to anything.
       </p>
       <div class="contact-block">
         <div class="contact-line">
@@ -1379,11 +1458,11 @@ footer {
       </div>
       <div class="form-group">
         <label class="form-label">Tell us about the project</label>
-        <textarea class="form-textarea" placeholder="Size of the area, current state, what you're hoping for — anything helps James prepare for the visit."></textarea>
+        <textarea class="form-textarea" placeholder="Size of the area, current state, what you're hoping for — anything that helps us understand the project."></textarea>
       </div>
       <button type="submit" class="form-submit">Send Request — It's Free</button>
       <div class="form-success" id="formSuccess" role="status" tabindex="-1">
-        ✓ &nbsp;Request received. James will be in touch within 24 hours.
+        ✓ &nbsp;Request received. We'll be in touch within 24 hours.
       </div>
     </form>
   </div>
@@ -1394,7 +1473,7 @@ footer {
   <div class="footer-top">
     <div>
       <a href="#hero" class="footer-logo-link" aria-label="JDB Landscape home">
-        <img class="footer-logo-image" src="jdb-logo.png" width="400" height="398" alt="JDB Landscape">
+        <img class="footer-logo-image" src="jdb-logo.webp" width="400" height="398" alt="JDB Landscape">
       </a>
       <p class="footer-tagline">Quality work. Fair prices.<br>Expectations exceeded.</p>
       <div class="footer-social">
@@ -1417,7 +1496,7 @@ footer {
     <div>
       <div class="footer-col-title">Navigate</div>
       <ul class="footer-links">
-        <li><a href="#about">About James</a></li>
+        <li><a href="#about">About JDB</a></li>
         <li><a href="#gallery">Our Work</a></li>
         <li><a href="#areas">Service Areas</a></li>
         <li><a href="#estimate">Free Estimate</a></li>
@@ -1539,6 +1618,69 @@ window.addEventListener('scroll', () => {
 window.addEventListener('resize', updateNav);
 updateNav();
 
+// ─── MOBILE SERVICE DETAILS ───
+const serviceRows = Array.from(document.querySelectorAll('.service-row'));
+const mobileServiceCards = window.matchMedia('(max-width: 960px)');
+
+function setServiceRowOpen(row, open) {
+  row.classList.toggle('is-flipped', open);
+  row.setAttribute('aria-expanded', String(open));
+  row.querySelectorAll('.service-desc-col, .service-tag').forEach(detail => {
+    detail.setAttribute('aria-hidden', String(!open && mobileServiceCards.matches));
+  });
+}
+
+function toggleServiceRow(row) {
+  if (!mobileServiceCards.matches) return;
+
+  const shouldOpen = !row.classList.contains('is-flipped');
+  serviceRows.forEach(otherRow => {
+    if (otherRow !== row) setServiceRowOpen(otherRow, false);
+  });
+  setServiceRowOpen(row, shouldOpen);
+
+  row.classList.remove('is-flipping');
+  void row.offsetWidth;
+  row.classList.add('is-flipping');
+}
+
+function syncServiceRows() {
+  serviceRows.forEach(row => {
+    if (mobileServiceCards.matches) {
+      row.setAttribute('role', 'button');
+      row.setAttribute('tabindex', '0');
+      setServiceRowOpen(row, row.classList.contains('is-flipped'));
+    } else {
+      row.classList.remove('is-flipped', 'is-flipping');
+      row.removeAttribute('role');
+      row.removeAttribute('tabindex');
+      row.removeAttribute('aria-expanded');
+      row.querySelectorAll('.service-desc-col, .service-tag').forEach(detail => {
+        detail.removeAttribute('aria-hidden');
+      });
+    }
+  });
+}
+
+serviceRows.forEach(row => {
+  row.addEventListener('click', () => toggleServiceRow(row));
+  row.addEventListener('keydown', e => {
+    if (e.key !== 'Enter' && e.key !== ' ') return;
+    if (!mobileServiceCards.matches) return;
+
+    e.preventDefault();
+    toggleServiceRow(row);
+  });
+  row.addEventListener('animationend', () => row.classList.remove('is-flipping'));
+});
+
+if (mobileServiceCards.addEventListener) {
+  mobileServiceCards.addEventListener('change', syncServiceRows);
+} else {
+  mobileServiceCards.addListener(syncServiceRows);
+}
+syncServiceRows();
+
 // ─── BEFORE / AFTER SLIDER ───
 document.querySelectorAll('[data-before-after]').forEach(slider => {
   const range = slider.querySelector('.before-after-range');
diff --git a/docs/jdb_after.jpg b/docs/jdb_after.jpg
deleted file mode 100644
index 48ac995..0000000
Binary files a/docs/jdb_after.jpg and /dev/null differ
diff --git a/docs/jdb_before.jpg b/docs/jdb_before.jpg
deleted file mode 100644
index 59dd4d5..0000000
Binary files a/docs/jdb_before.jpg and /dev/null differ