/* Prevent FOUT: header nav fades in after fonts have time to load */
@keyframes navFadeIn {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}
.nav-menu,
.header .info-btn {
  animation: navFadeIn 0.25s ease 0.2s both;
}

/* Screen reader only - accessible hidden content (Google-approved pattern) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.testimonials-section .testimonial-content p {
  position: relative;
  padding: 0 28px;
}
.testimonials-section .testimonial-content p::before,
.testimonials-section .testimonial-content p::after {
  position: absolute;
  color: var(--secondary-color);
  font-family: "Playfair Display", serif;
  line-height: 1;
  opacity: 0.9;
}
.testimonials-section .testimonial-content p::before {
  content: "\201C";
  left: 0;
  top: -12px;
  font-size: 42px;
}
.testimonials-section .testimonial-content p::after {
  content: "\201D";
  right: 0;
  bottom: -18px;
  font-size: 38px;
}
@media (max-width: 968px) {
  .testimonials-section .testimonial-content p {
    padding: 0 20px;
  }
  .testimonials-section .testimonial-content p::before {
    font-size: 34px;
    top: -10px;
  }
  .testimonials-section .testimonial-content p::after {
    font-size: 30px;
    bottom: -14px;
  }
}

/* ── Stop the testimonial box from resizing between slides (desktop) ──
   On desktop the carousel only had min-height:189px with no max-height, so a
   long review (up to ~232px) expanded the box while a short one snapped back
   to 189px. On a 15" 1080p laptop that jump was very visible when clicking
   next/prev. The .testimonials-carousel is capped at max-width:900px, so text
   wrapping (and therefore height) is the same across every desktop width — a
   single fixed 240px height fits the tallest review and keeps the box steady.
   Mirrors the fixed height that already exists for very large (2500px+) screens. */
@media (min-width: 969px) {
  .testimonials-section .testimonials-carousel {
    min-height: 240px;
    max-height: 240px;
    overflow: hidden;
  }
  .testimonials-section .testimonial-item {
    min-height: 240px;
    justify-content: center;
  }
}

/* Hero slider: .slide-heading replicates all h2 styles so the tag can be <p> for SEO */
.slide-content .slide-heading {
  font-size: 48px;
  margin-bottom: 20px;
  opacity: 0;
  transform: translateY(30px);
  animation: fadeInUp 1s ease forwards;
  animation-delay: 0.5s;
  text-shadow:
    0 2px 12px rgba(0, 0, 0, 0.6),
    0 0 40px rgba(0, 0, 0, 0.3);
  font-family: "Playfair Display", serif;
  font-weight: 400;
  letter-spacing: 2px;
  line-height: 1.2;
}
.slide.active .slide-content .slide-heading {
  animation: fadeInUp 1s ease forwards;
  animation-delay: 0.5s;
}
.slide-content p {
  text-shadow:
    0 1px 8px rgba(0, 0, 0, 0.6),
    0 0 30px rgba(0, 0, 0, 0.25);
}
/* Responsive: match original h2 breakpoints exactly */
@media (max-width: 968px) {
  .slide-content .slide-heading {
    font-size: 36px;
  }
}
@media (max-width: 480px) {
  .slide-content .slide-heading {
    font-size: 28px;
  }
}
@media (min-width: 1366px) and (max-width: 1920px) {
  .slide-content .slide-heading {
    font-size: 44px;
  }
}
@media (min-width: 1921px), (min-width: 1024px) and (min-resolution: 2dppx) {
  .slide-content .slide-heading {
    font-size: 53px;
  }
}

/* Home slide 1 (bridal portrait): on ~1080p / 15" laptop screens the large heading
   crept onto the couple's faces. Shift it further right so it clears them.
   Scoped to slide 1 only, so the other text-right slides keep their layout. */
@media (min-width: 969px) and (max-width: 1920px) {
  .slider-container .slide:nth-child(1).text-right .slide-content {
    margin-right: 4%;
    transform: translateY(70px);
  }
  /* Slide 6 (bridal close-up "Every Frame, A Feeling"): nudge text right and down
     so it clears the bride's face on laptop screens. */
  .slider-container .slide:nth-child(6).text-right .slide-content {
    margin-right: 4%;
    transform: translateY(95px);
  }
}

/* Home films carousel arrows: match films page main section position */
body.home-page .films-carousel-wrapper .carousel-arrow {
  position: absolute !important;
  top: 50% !important;
  transform: translateY(-50%) !important;
}
body.home-page #filmsPrev {
  left: -70px !important;
  right: auto !important;
}
body.home-page #filmsNext {
  right: -70px !important;
  left: auto !important;
}
@media (max-width: 968px) {
  body.home-page #filmsPrev {
    left: -8px !important;
  }
  body.home-page #filmsNext {
    right: -8px !important;
    left: auto !important;
  }
}

/* Mobile: fix blog card image-to-text gap on home page */
@media (max-width: 968px) {
  body.home-page .blog-section .blog-image {
    height: auto !important;
  }
  body.home-page .blog-section .blog-image img {
    height: auto !important;
    object-fit: contain !important;
  }
  body.home-page .blog-section .blog-content {
    padding: 15px !important;
  }
  body.home-page .booking-section + .portfolio-section {
    margin-top: -15% !important;
  }
  body.home-page .portfolio-section {
    margin-bottom: -15% !important;
  }
  .photography-gallery-section {
    margin-top: 0 !important;
    padding-top: 50px !important;
    margin-bottom: -12% !important;
  }
  body.home-page .portfolio-section + .films-section {
    margin-top: 10% !important;
  }
}

/* Mobile: keep all 4 trusted platform cards equal size */
@media (max-width: 968px) {
  .trusted-grid {
    align-items: stretch;
  }
  .trusted-card {
    width: 100%;
    min-height: 170px;
    height: 170px;
    justify-content: space-between;
  }
}

/* ── Form success message & field validation (moved from inline <style>) ── */
.success-wrap {
  text-align: center;
  padding: 60px 20px 80px;
  animation: successFadeIn 0.8s ease;
}

@keyframes successFadeIn {
  from {
    opacity: 0;
    transform: translateY(30px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.success-icon {
  width: 90px;
  height: 90px;
  border-radius: 50%;
  background: linear-gradient(135deg, #c9a84c, #dfc06f);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 30px;
  box-shadow: 0 8px 30px rgba(201, 168, 76, 0.35);
}

.success-icon i {
  font-size: 40px;
  color: #fff;
}

.success-title {
  font-family: "Playfair Display", serif;
  font-size: clamp(20px, 0.81vw + 6px, 31px);
  color: #1a1a1a;
  margin-bottom: 15px;
  font-weight: 600;
  letter-spacing: 1px;
}

.success-sub {
  font-family: "Cormorant Garamond", serif;
  font-size: clamp(12px, 0.53vw + 5px, 20px);
  color: #666;
  max-width: min(74vw, 760px);
  margin: 0 auto 30px;
  line-height: 1.6;
}

@media (min-width: 1366px) and (max-width: 1920px) {
  .success-sub {
    font-size: clamp(12px, 0.61vw, 18px) !important;
    max-width: min(72vw, 760px) !important;
  }
}

@media (min-width: 1921px), (min-width: 1024px) and (min-resolution: 2dppx) {
  .success-sub {
    font-size: clamp(20px, 1.11vw, 37px) !important;
    max-width: min(68vw, 1200px) !important;
    line-height: 1.5;
  }
}

.success-divider {
  width: 60px;
  height: 2px;
  background: linear-gradient(90deg, #c9a84c, #dfc06f);
  margin: 0 auto 25px;
  border-radius: 2px;
}

.success-contact {
  font-family: "Montserrat", sans-serif;
  font-size: clamp(11px, 0.35vw + 8px, 16px);
  color: #888;
  letter-spacing: 0.5px;
}

@media (min-width: 1366px) and (max-width: 1920px) {
  .success-contact {
    font-size: clamp(13px, 0.5vw + 6px, 16px) !important;
  }
}

.success-contact a {
  color: #c9a84c;
  text-decoration: none;
  font-weight: 500;
}

.field-error {
  display: block;
  color: #c62828;
  font-size: 12px;
  margin-top: 4px;
  min-height: 16px;
  font-family: "Montserrat", sans-serif;
}

@media (min-width: 1366px) and (max-width: 1920px) {
  body.home-page .films-section .container {
    max-width: 1552px !important;
  }
  body.home-page .films-section .film-item {
    min-width: calc(25% - 15px) !important;
    max-width: calc(25% - 15px) !important;
  }
  body.home-page .films-section .film-thumbnail {
    height: auto !important;
    aspect-ratio: auto !important;
  }
  body.home-page .films-section .film-thumbnail img {
    width: 100% !important;
    height: auto !important;
    object-fit: contain !important;
    display: block !important;
  }
  body.home-page .statistics-section .container {
    max-width: 1552px !important;
  }
  body.home-page .statistics-grid {
    gap: 40px !important;
  }
  body.home-page .statistic-number,
  body.home-page .google-rating {
    font-size: 62px !important;
  }
  body.home-page .statistic-label {
    font-size: 15.4px !important;
  }
  body.home-page .hero-slider + .portfolio-section .section-header p {
    font-size: 0.9rem !important;
    line-height: 1.85 !important;
    color: #6b6b6b !important;
  }
  body.home-page .blog-section .container {
    max-width: 1500px !important;
  }
  body.home-page .blog-section .section-header p {
    font-size: 0.9rem !important;
    line-height: 1.85 !important;
  }
  body.home-page .blog-section .blog-content p {
    font-size: 0.9rem !important;
    line-height: 1.85 !important;
    color: #6b6b6b !important;
  }
  body.home-page .blog-grid {
    gap: 36px !important;
  }
  body.home-page .blog-image {
    height: auto !important;
  }
  body.home-page .blog-image img {
    height: auto !important;
    object-fit: contain !important;
  }
  body.home-page .booking-section h2 {
    font-size: 35px !important;
  }
  body.home-page .booking-section p {
    font-size: 15.4px !important;
  }
  body.home-page .booking-form {
    transform: scale(1.15) !important;
    transform-origin: center top !important;
    margin-bottom: 50px !important;
  }
  body.home-page .booking-section + .portfolio-section .section-header p {
    font-size: 0.9rem !important;
    line-height: 1.85 !important;
    color: #6b6b6b !important;
  }
  body.home-page .booking-section + .portfolio-section .section-header strong {
    font-size: inherit !important;
    line-height: inherit !important;
  }
  .footer {
    padding: 30px 0 12px !important;
  }
  .footer h3 {
    font-size: 14.3px !important;
  }
  .footer a,
  .footer p {
    font-size: 12px !important;
  }
  .footer-section > a {
    font-size: 13px !important;
    color: #2a2a2a !important;
  }
  .footer-section > a i {
    font-size: 12px !important;
  }
  .footer-social a,
  .footer-social a i {
    font-size: 19px !important;
  }
  .footer-bottom,
  .footer-bottom p,
  .footer-terms {
    font-size: 12px !important;
  }
  .footer-logo-circle img {
    height: 57px !important;
    width: 57px !important;
  }
}

/* ── Laptop band: 1366px to 1600px CSS viewport ──────────────────────────
   The block above starts at 1366px but every value in it is tuned for a real
   1920px desktop (1552px containers). Two very common machines land far below
   1920 yet still inside that block:

     15" 1080p Windows laptop @ Windows' default 125% scaling -> 1536px
     13" MacBook Air (2560x1664) @ macOS default              -> ~1470px

   At those widths a 1552px container fills the whole screen and the page
   loses its side margins. style.min.css already says these containers should
   be 1200px below 1600px, but extra.css loads later and was overriding it.
   This restores that intent for the laptop band only. Nothing at 1601px and
   above changes, and mobile (<=968px) and 4K (>=1921px) are untouched. */
@media (min-width: 1366px) and (max-width: 1600px) {
  body.home-page .films-section .container,
  body.home-page .statistics-section .container,
  body.home-page .blog-section .container {
    max-width: 1200px !important;
  }

  /* Narrowing the container alone is not enough: the card typography above is
     still at its 1920 size, so titles wrap onto extra lines and each card
     grows taller instead of shorter. Scale the text down by the same ~0.8
     the container moved (1500 -> 1200) so the section keeps its proportions
     and stops running off a ~780px-tall laptop viewport. */
  body.home-page .blog-section .blog-grid {
    gap: 28px !important;
  }
  body.home-page .blog-section .blog-content h3 {
    font-size: 17px !important;
  }
  body.home-page .blog-section .blog-content p {
    font-size: 0.8rem !important;
  }

  /* Fit a whole card in one screen. The viewport here is only ~780px tall
     (864 minus browser chrome) against 1080 on a real desktop, so the
     section's 1920-sized padding and 70px header gap push the card past the
     fold even after the type is scaled. Tighten the chrome around the cards
     rather than shrinking the cards further. */
  body.home-page .blog-section {
    padding: 40px 0 !important;
  }
  /* Was 24px, tight enough that the heading and the first row of cards read
     as touching. Nudged back up now that the card padding/type trims above
     already bought back most of the vertical room this band needed. */
  body.home-page .blog-section .section-header {
    margin-bottom: 44px !important;
  }
  body.home-page .blog-section .blog-content {
    padding: 18px !important;
  }
  /* The tallest card drives the row height, and here that is the one whose
     title wraps to four lines. Tighten the gaps inside the card instead of
     cropping the photo, so the image still shows in full. */
  body.home-page .blog-section .blog-meta {
    margin-bottom: 8px !important;
  }
  body.home-page .blog-section .blog-content h3 {
    line-height: 1.3 !important;
    margin-bottom: 10px !important;
  }
  body.home-page .blog-section .blog-content p {
    line-height: 1.7 !important;
    margin-bottom: 12px !important;
  }

  /* Films: style.min.css puts a 95px gap between the heading and the video
     grid for min-width:969px. That reads as balanced on a 1080px-tall screen
     but leaves a big empty band on a ~780px one, pushing the videos low. */
  body.home-page .films-section .section-header {
    margin-bottom: 40px !important;
  }

  /* Booking form: the block above scales the form up 15% for a 1920 desktop.
     transform does not change the layout box, so the extra height spills past
     the fold on a ~780px viewport and needs a 50px margin to compensate.
     Drop the scale here and tighten the surrounding spacing instead, so the
     whole form lands in one screen. */
  body.home-page .booking-form {
    transform: none !important;
    margin-bottom: 0 !important;
    padding: 24px !important;
  }
  body.home-page .booking-section {
    padding: 40px 0 !important;
  }
  body.home-page .booking-section h2 {
    font-size: 28px !important;
  }
  body.home-page .booking-section p {
    font-size: 13px !important;
  }
  body.home-page .booking-form .form-row {
    gap: 20px !important;
    margin-bottom: 16px !important;
  }
  body.home-page .booking-form .form-group label {
    margin-bottom: 6px !important;
  }
  /* The message box is rows="5", the single tallest element in the form.
     resize:vertical stays on, so anyone who needs more room can still drag
     it open. */
  body.home-page .booking-form textarea {
    height: 70px !important;
    min-height: 70px !important;
  }
}

/* ── INFO sidebar on laptop-class screens ────────────────────────────────
   Two machines land in this width range and both need the SAME compact
   scale, even though their heights differ:

     13" MacBook Air  -> ~1470 x ~870
     15" 1080p @125%  -> ~1536 x ~780

   An earlier attempt split these by max-height and gave the Mac roomier
   numbers; that pushed the email down under the floating WhatsApp button.
   One set of values for the whole band keeps them identical.

   Two gotchas worth remembering:
   - .info-sidebar-header is position:absolute, so it reserves no space. The
     description's margin-top is the only thing stopping CLOSE from sitting
     on the text.
   - The visible map is .branch-map-placeholder (an <img> script.js swaps in
     for the iframe), not .branch-map. Its img is height:auto on a square
     source, so setting width alone keeps it square. */
@media (min-width: 1366px) and (max-width: 1600px) {
  .info-sidebar {
    width: 300px !important;
    padding: 20px 26px !important;
  }
  .info-sidebar .info-sidebar-header {
    top: 16px !important;
    right: 26px !important;
  }
  .info-sidebar .info-description {
    font-size: 11.5px !important;
    line-height: 1.55 !important;
    margin-top: 30px !important;
    margin-bottom: 14px !important;
  }
  .info-sidebar .branch-section {
    margin-bottom: 18px !important;
  }
  .info-sidebar .branch-section h2 {
    font-size: 20px !important;
    letter-spacing: 2.5px !important;
    margin-bottom: 6px !important;
  }
  .info-sidebar .branch-city {
    font-size: 15px !important;
    margin-bottom: 4px !important;
  }
  .info-sidebar .branch-name {
    font-size: 15px !important;
    margin-bottom: 8px !important;
  }
  .info-sidebar .branch-address {
    font-size: 12px !important;
    line-height: 1.5 !important;
    margin-bottom: 6px !important;
  }
  .info-sidebar .branch-map-link {
    padding: 6px 14px !important;
    margin-bottom: 6px !important;
  }
  #infoSidebar .branch-map-placeholder,
  .info-sidebar .branch-map-placeholder {
    width: 160px !important;
    margin-top: 8px !important;
  }
  #infoSidebar .branch-map,
  .info-sidebar .branch-map {
    width: 160px !important;
    height: 160px !important;
    margin-top: 8px !important;
  }
  .info-sidebar .info-section {
    margin-top: 14px !important;
  }
  .info-sidebar .info-section h3 {
    font-size: 17px !important;
    letter-spacing: 2px !important;
    margin-top: 8px !important;
  }
  .info-sidebar .info-social {
    margin-top: 6px !important;
  }
  .info-sidebar .info-email {
    font-size: 12px !important;
    margin-top: 3px !important;
  }
}

/* ── INFO sidebar: MacBook only ──────────────────────────────────────────
   The block above is tuned for the shortest laptop viewport (15" 1080p at
   125% scaling, ~780px tall). A 13" MacBook has ~870px, so the same values
   read too small there. zoom:1.09 was the right proportion but zoom rescales
   already-rasterised text and the result looked soft, so the same 1.09 is
   baked into real px values here instead. Type stays crisp and the layout
   reflows properly.

   Only .info-sidebar-content is scaled, exactly like the zoom test: the
   panel width (300px), its padding, and the absolutely-positioned CLOSE
   button all stay put. min-height:821px is what separates the MacBook from
   the 15" Windows laptop, which stays on the values above. */
@media (min-width: 1366px) and (max-width: 1600px) and (min-height: 821px) {
  .info-sidebar .info-description {
    font-size: 12.5px !important;
    margin-top: 33px !important;
    margin-bottom: 15px !important;
  }
  .info-sidebar .branch-section {
    margin-bottom: 20px !important;
  }
  .info-sidebar .branch-section h2 {
    font-size: 22px !important;
    letter-spacing: 2.7px !important;
    margin-bottom: 7px !important;
  }
  .info-sidebar .branch-city {
    font-size: 16px !important;
    margin-bottom: 4px !important;
  }
  .info-sidebar .branch-name {
    font-size: 16px !important;
    margin-bottom: 9px !important;
  }
  .info-sidebar .branch-address {
    font-size: 13px !important;
    margin-bottom: 7px !important;
  }
  .info-sidebar .branch-map-link {
    padding: 7px 15px !important;
    margin-bottom: 7px !important;
  }
  #infoSidebar .branch-map-placeholder,
  .info-sidebar .branch-map-placeholder {
    width: 174px !important;
    margin-top: 9px !important;
  }
  #infoSidebar .branch-map,
  .info-sidebar .branch-map {
    width: 174px !important;
    height: 174px !important;
    margin-top: 9px !important;
  }
  .info-sidebar .info-section {
    margin-top: 15px !important;
  }
  .info-sidebar .info-section h3 {
    font-size: 18.5px !important;
    letter-spacing: 2.2px !important;
    margin-top: 9px !important;
  }
  .info-sidebar .info-social {
    margin-top: 7px !important;
  }
  .info-sidebar .info-email {
    font-size: 13px !important;
    margin-top: 3px !important;
  }
}

/* ── Privacy Policy / Terms pages on laptop-class screens ────────────────
   Both pages carry their spacing as inline style attributes in the markup
   (section padding 80/100, card padding 34, item gap 22) plus privacy's own
   inline <style> forcing padding-top:140px. Those add up to ~165px of pure
   whitespace, which is fine at 1080px tall but pushes the card past the fold
   on a ~780px laptop viewport.

   The header is position:fixed, so it sits ON TOP of this padding rather
   than above it. padding-top therefore has to cover the ~90px header before
   any visible gap starts: 135px leaves ~45px of real clearance, matched by
   45px at the bottom so the card sits centred between header and footer.
   Only spacing is touched here, no font sizes, so the pages look identical
   apart from being tighter. The doubled `html body...` selector is needed to
   outrank privacy-policy.html's own inline <style> rule, which is more
   specific than a plain .terms-section and loads after extra.css. */
@media (min-width: 1366px) and (max-width: 1600px) {
  html body .terms-section,
  html body.privacy-page .terms-section {
    padding-top: 135px !important;
    padding-bottom: 45px !important;
  }
  .terms-section .section-header {
    margin-bottom: 18px !important;
  }
  .terms-section .terms-content {
    padding: 24px !important;
  }
  .terms-section .terms-item {
    margin-bottom: 14px !important;
  }
}

/* ── Privacy / Terms: MacBook only ───────────────────────────────────────
   Same split as the INFO sidebar. The block above is cut for the shortest
   laptop viewport (15" 1080p at 125% scaling, ~780px tall). A 13" MacBook
   has ~870px, roughly 90px more, so it does not need the full squeeze and
   the page reads better with some of the original breathing room restored.
   min-height:821px is what separates the two; the 15" laptop stays on the
   tighter values above. Spacing only, no font changes. */
@media (min-width: 1366px) and (max-width: 1600px) and (min-height: 821px) {
  html body .terms-section,
  html body.privacy-page .terms-section {
    padding-top: 145px !important;
    padding-bottom: 60px !important;
  }
  .terms-section .section-header {
    margin-bottom: 24px !important;
  }
  .terms-section .terms-content {
    padding: 30px !important;
  }
  .terms-section .terms-item {
    margin-bottom: 18px !important;
  }
}

/* The last .terms-item's margin-bottom stacks on top of the card's own
   bottom padding, so the card ends up with ~48px of dead space under the
   final paragraph against 30px above the first one. That reads as the block
   sitting high inside its own card. Drop it on the last child only, which
   both balances the card and pushes the whole section down into the middle
   of the space between the fixed header and the footer. */
@media (min-width: 1366px) and (max-width: 1600px) {
  .terms-section .terms-item:last-child {
    margin-bottom: 0 !important;
  }
}

/* Make the policy section fill the viewport so the footer starts below the
   fold instead of peeking in under the card. The section is the only thing
   between the fixed header and the footer, so giving it min-height:100vh is
   enough; border-box keeps the padding inside that 100vh rather than adding
   to it. If the content is ever taller than one screen this does nothing,
   min-height only sets a floor. */
@media (min-width: 1366px) and (max-width: 1600px) {
  .terms-section {
    min-height: 100vh !important;
    box-sizing: border-box !important;
  }
}

/* ── Privacy / Terms: stop the gap under the footer on tall screens ──────
   These two pages are short. On a 4K monitor the header + card + footer do
   not reach the bottom of the viewport, so a band of background is left
   showing below the footer. Classic sticky-footer fix: make the page at
   least one viewport tall and let the content section absorb the slack, so
   the footer ends exactly at the bottom edge.

   body:has(.terms-section) is used because terms-conditions.html has a bare
   <body> with no class, unlike privacy-policy.html's body.privacy-page, and
   this keeps both pages working without editing their markup. Only these two
   pages match, so nothing else on the site becomes a flex container. */
@media (min-width: 1921px) {
  body:has(.terms-section) {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
  }
  body:has(.terms-section) .terms-section {
    flex: 1 0 auto;
  }
  body:has(.terms-section) .footer {
    flex: 0 0 auto;
    margin-top: auto;
  }
}

/* ══ HEADER FLUIDLY SCALED (was: LOCKED TO 4K AT EVERY WIDTH) ════════════
   Previously everything here was pinned to the >=2500px (4K) values with no
   inner scaling. That looked right on a wide 4K viewport but oversized on a
   1080p laptop: Windows display scaling (125% / 150%) makes such a laptop
   report only 1280-1536 CSS px, so the fixed 58px logo / 12px nav ate
   14-17% of the width instead of the ~11% a 4K screen shows.

   Now each value uses clamp(min, <vw>, max):
     - the MAX is the exact old 4K value, reached at ~>=1900px, so 4K and
       large monitors render identically to before;
     - the <vw> middle term holds a roughly constant on-screen proportion,
       so a 1280-1600px laptop shrinks smoothly to match the 4K look;
     - the MIN is a sensible floor so nothing gets too small.
   clamp() is continuous, so there are still no jarring breakpoint jumps,
   which was the original goal.

   Mobile (<=968px) is deliberately EXCLUDED. Its header is a different
   layout entirely: 65px tall, 41px logo, hamburger instead of a nav row. */
@media (min-width: 969px) {
  .header .container {
    padding: clamp(9px, 0.68vw, 13px) clamp(24px, 2.1vw, 40px) !important;
    max-width: 100% !important;
  }
  .header {
    height: auto !important;
  }
  .logo img {
    height: clamp(42px, 3.05vw, 58px) !important;
  }
  .nav-menu {
    gap: clamp(18px, 1.5vw, 28px) !important;
  }
  .nav-link {
    font-size: clamp(10px, 0.63vw, 12px) !important;
    letter-spacing: clamp(1.4px, 0.12vw, 2.2px) !important;
  }
  .nav-link.book-now {
    padding: clamp(8px, 0.57vw, 11px) clamp(18px, 1.46vw, 28px) !important;
  }
  .info-btn span {
    font-size: clamp(9px, 0.57vw, 11px) !important;
  }
  .info-btn-lines {
    width: clamp(34px, 2.6vw, 50px) !important;
  }
  .slide-content h1,
  .slide-content h2,
  .slide-content .slide-heading {
    font-size: 53px !important;
  }
  .slide-content p {
    font-size: 20px !important;
  }
}

/* ── Hero banners pinned to the 4K height ────────────────────────────────
   Same idea as the header block: one size at every width instead of
   shrinking at narrow breakpoints. The 4K values are simply the base ones,
   since >=2500px never overrode them:

     .page-hero          320px   (was 300px at 969-1200, 280px at 1201-1400)
     .blog-gallery-hero  260px

   Mobile is left out on purpose: there .page-hero is height:auto with a
   padding-bottom aspect ratio and .blog-gallery-hero is 110px. */
@media (min-width: 969px) {
  .page-hero {
    height: 320px !important;
  }
  .blog-gallery-hero {
    height: 260px !important;
  }
}

/* 15-inch 1080p laptop viewport: keep the complete home blog section within
   the space below the desktop header and carry its background to the bottom
   edge, so the following white section does not appear as a strip.
   The min-height is taller than the content needs, so centre the content
   vertically instead of letting the leftover space pile up under the cards
   (which made the band look top-heavy). Flex column + justify-content:center
   splits the slack evenly above and below; the container keeps its own
   horizontal auto-margins, so left/right centring is unchanged. */
@media (min-width: 1366px) and (max-width: 1920px) and (min-height: 700px) and (max-height: 1100px) {
  body.home-page .blog-section {
    min-height: calc(100svh - 80px);
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* ── Home blog section: responsive infinite slider ──────────────────────
   Built like the films carousel: a flex track inside an overflow:hidden
   viewport, moved with translateX and transition: transform 0.5s ease.

   An earlier version used CSS scroll-snap and kept drifting off centre,
   because the resting position was a scroll offset that snapping, momentum
   and a JS tween all had a say in. A transform is absolute, so a card cannot
   land anywhere except its exact index.

   The reveal is editorial rather than a scale-and-fade: the photo settles
   out of a slow zoom while a warm veil lifts off it, one light sweep
   crosses, and the text rises in a stagger. Nothing scales the whole card,
   which is what made an earlier version feel like it popped. */
.blog-slider-dots {
  display: none;
}

/* Home blog carousel: three cards on laptop/desktop, matching the films
   carousel's flat track movement. The existing card styling is untouched. */
body.home-page .blog-section .blog-grid {
  display: block !important;
  grid-template-columns: none !important;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
  cursor: grab;
}
body.home-page .blog-section .blog-grid.is-dragging {
  cursor: grabbing;
}
body.home-page .blog-section .blog-grid a,
body.home-page .blog-section .blog-grid img {
  -webkit-user-drag: none;
}
body.home-page .blog-section .blog-grid a {
  cursor: pointer;
}
body.home-page .blog-section .blog-slider-track {
  display: flex;
  align-items: stretch;
  gap: 30px;
  will-change: transform;
  transition: transform 0.5s ease;
}
body.home-page .blog-section .blog-slider-track .blog-item {
  flex: 0 0 calc((100% - 60px) / 3);
  min-width: calc((100% - 60px) / 3);
  display: flex;
  flex-direction: column;
}
body.home-page .blog-section .blog-slider-track .blog-content {
  flex: 1;
  display: flex;
  flex-direction: column;
}
body.home-page .blog-section .blog-slider-track .blog-content h3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  line-height: 1.4;
  min-height: 0;
  max-height: calc(2 * 1.4em);
}
body.home-page .blog-section .blog-slider-track .blog-content p {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  line-height: 1.8;
  min-height: calc(3 * 1.8em);
  max-height: calc(3 * 1.8em);
}
body.home-page .blog-section .blog-slider-track .read-more {
  margin-top: auto;
}
body.home-page .blog-section .blog-slider-track .blog-image {
  height: auto !important;
  aspect-ratio: 3 / 2;
}
body.home-page .blog-section .blog-slider-track .blog-image img {
  width: 100%;
  height: 100% !important;
  object-fit: cover !important;
}

@media (max-width: 968px) {
  body.home-page .blog-section .blog-grid {
    /* viewport only: the track inside it does the moving. overflow hidden on
       BOTH axes, so the mouse wheel can never be captured here, and the JS
       sets the height to the card on screen so nothing shows underneath. */
    display: block !important;
    grid-template-columns: none !important;
    overflow: hidden;
    padding: 6px 0 18px;
    touch-action: pan-y;
    /* no height transition: every card is the same height (see below), so the
       height is set once and never animates. Animating it alongside the slide
       is what made the change look like a flash. */
  }
  body.home-page .blog-section .blog-slider-track {
    display: flex;
    align-items: stretch;
    /* films carousel runs gap:0 on mobile, so one card fills the viewport
       exactly and no sliver of the next one shows during the slide */
    gap: 0;
    align-items: stretch;
    will-change: transform;
    /* same flat slide the films carousel uses */
    transition: transform 0.5s ease;
  }

  /* Flat slide, exactly like the films carousel: no rotation, no perspective,
     and no per-card fade. The card itself never transforms; only the track
     moves. Fading idle cards is what made the change read as a flash. */
  body.home-page .blog-section .blog-slider-track .blog-item {
    flex: 0 0 100% !important;
    min-width: 100% !important;
    max-width: 100% !important;
    background: #fff;
    border-radius: 0;
    overflow: hidden;
    box-shadow: 0 3px 12px rgba(0, 0, 0, 0.05);
  }

  /* equal heights: 3 lines of title, 3 of excerpt, on every card */
  /* clamped so a long headline cannot stretch one card far past the others,
     but no min-height: reserving three lines on a two-line title is what put
     the empty band between the excerpt and READ MORE */
  /* line-clamp caps a long title at 3 lines, but a 2-line title still renders
     shorter. min-height reserves all 3 lines either way, so every card ends up
     exactly the same height and the slider never has to resize. */
  body.home-page .blog-section .blog-content h3 {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.4;
    min-height: 0;
    max-height: none;
  }
  body.home-page .blog-section .blog-content p {
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.8;
    min-height: calc(3 * 1.8em);
  }

  /* Photo sits plain, like the films carousel thumbnails: no zoom-out, no
     veil, no light sweep, and no staggered text. The slide itself is the
     whole animation.

     Fixed ratio is what keeps every card the same height, the same way
     .film-thumbnail uses aspect-ratio on mobile. The !important here undoes
     the earlier mobile block (~line 138) that sets height:auto and
     object-fit:contain, which would otherwise let each photo size itself. */
  body.home-page .blog-section .blog-image {
    position: relative;
    overflow: hidden;
    aspect-ratio: 3 / 2; /* source images are 1024x683 */
    height: auto !important;
  }
  body.home-page .blog-section .blog-image img {
    width: 100%;
    height: 100% !important;
    object-fit: cover !important;
  }

  /* dots: the active one is a track that fills over the autoplay interval,
     so the wait is visible instead of feeling like a random jump */
  .blog-slider-dots {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 9px;
    margin-top: 10px;
  }

  /* Gap between the slider and the testimonials block. Four separate values
     stacked up to ~135px on a phone: the track's bottom padding, the dots
     margin, .blog-section's 45px bottom and .testimonials-section's 40px
     top. Roughly halved here; the section paddings are the bulk of it, so
     only their facing edges are touched and the tops/bottoms elsewhere stay
     as they were. */
  body.home-page .blog-section {
    padding-bottom: 20px !important;
  }
  body.home-page .testimonials-section {
    padding-top: 18px !important;
  }
  .blog-slider-dots button {
    position: relative;
    overflow: hidden;
    width: 6px;
    height: 6px;
    padding: 0;
    border: 0;
    border-radius: 99px;
    background: #ded7ca;
    cursor: pointer;
    transition:
      width 0.5s cubic-bezier(0.22, 0.9, 0.3, 1),
      background-color 0.5s ease;
  }
  .blog-slider-dots button.is-active {
    width: 26px;
    background: rgba(171, 144, 94, 0.28);
  }
  .blog-slider-dots button.is-active::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 99px;
    background: var(--secondary-color);
    transform: scaleX(0);
    transform-origin: left center;
    animation: twsDotFill 2000ms linear forwards;
  }
  .blog-slider-dots.is-paused button.is-active::after {
    animation-play-state: paused;
  }
  @keyframes twsDotFill {
    to {
      transform: scaleX(1);
    }
  }
}

/* ═══════════════════════════════════════════════════════════
   Photography gallery — category filter + masonry grid
   ═══════════════════════════════════════════════════════════ */

.gallery-filter {
  display: flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 auto 38px;
  padding: 0 16px;
}

/* ── Floating category bar ──────────────────────────────────────────────
   A SEPARATE element from the row above, cloned from it in photography.html.
   This is the whole point of the design: the in-flow row never moves, never
   changes position, and never hands over to anything. The floating bar is
   an overlay that fades in over the photographs and fades out again, so the
   two can never fight for the same space or flicker into each other.

   It is display:none until JS clones it, so a visitor without JS simply
   keeps the ordinary row.                                                */
.gallery-filter-float {
  position: fixed;
  /* Sits directly under the site header. The exact offset is measured from
     the real header in JS, because the header changes height responsively. */
  top: var(--float-top, 92px);
  left: 0;
  right: 0;
  /* Below the header (which sits higher), so the bar slides out from behind
     it rather than across it. */
  z-index: 880;
  margin: 0;
  padding: 14px 16px;
  /* Starts a full bar-height up, tucked behind the header. The travel has to
     be larger than the bar is tall or the movement is over before the eye
     catches it — an earlier 14px shift was technically animating but read as
     the bar simply appearing. */
  opacity: 0;
  transform: translate3d(0, -100%, 0);
  pointer-events: none;
  visibility: hidden;
  /* Solid, and the same white as the header. Any transparency here shows the
     photographs through and breaks the illusion that the two are one surface;
     even 0.97 left a faint tonal step along the join. */
  background-color: #fff;
  /* Shadow along the BOTTOM edge only. A symmetrical box-shadow also casts
     upward onto the header and draws a line exactly where the two are meant
     to merge; clipping the spread to one side is what removes the seam. */
  box-shadow: 0 8px 16px -10px rgba(0, 0, 0, 0.18);
  /* ── Leaving ──
     Slower than the arrival and eased out, so the bar is watched away rather
     than snatched. visibility is stepped at the very end via a delay, which
     keeps it out of the tab order once hidden without cutting the fade
     short. */
  transition:
    opacity 0.5s cubic-bezier(0.4, 0, 0.6, 1),
    transform 0.5s cubic-bezier(0.4, 0, 0.6, 1),
    visibility 0s linear 0.5s;
}

/* The bar overshoots upward under the header so the two whites overlap with
   no hairline between them. Drawn as a pseudo-element rather than padding on
   the bar itself, because the bar's own box is what the pills are centred in
   and growing it would push them down. */
.gallery-filter-float::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 100%;
  height: 18px;
  background-color: #fff;
}

/* ── Arriving ──
   Quicker and eased in: the visitor scrolled up asking for this bar, so it
   should meet them promptly. visibility flips immediately here, hence the
   0s delay overriding the exit rule above. */
.gallery-filter-float.is-shown {
  opacity: 1;
  transform: translate3d(0, 0, 0);
  pointer-events: auto;
  visibility: visible;
  /* The bar settles into place on a decelerating curve, and the fade is
     deliberately quicker than the slide: by the time the surface is solid the
     movement is still finishing, which is what makes it read as descending
     rather than materialising. */
  transition:
    opacity 0.3s ease-out,
    transform 0.52s cubic-bezier(0.16, 1, 0.3, 1),
    visibility 0s linear 0s;
}

/* ── Merging into the real row ──
   Near the top of the gallery the in-flow row is about to arrive on screen
   in its own right, so the floating copy dissolves in place rather than
   sliding away: it is being replaced by the row it was standing in for, and
   any movement here would read as two bars passing each other. The pills
   hold still and only the surface lifts, which is what makes the swap
   invisible. */
.gallery-filter-float.is-merging {
  opacity: 0;
  /* Held at rest: the copy dissolves exactly where it stands. Letting it
     slide up here (the base rule's -100%) would send it climbing past the
     real row on its way down, which is the one thing the merge exists to
     avoid. */
  transform: translate3d(0, 0, 0);
  pointer-events: none;
  transition:
    opacity 0.34s ease,
    transform 0s linear,
    visibility 0s linear 0.34s;
}

/* While the bar is joined to the header the header must not cast its own
   shadow: that shadow falls straight onto the bar and draws the very line the
   two are meant to hide. The bar carries the shadow for both. !important is
   required to beat the header's own !important rule in style.min.css. */
body.photography-page.filter-bar-joined .header {
  box-shadow: none !important;
  /* Snap the shadow off the instant the bar joins. The header carries its own
     `transition: box-shadow 0.4s` for the scroll-shadow effect, and without
     this override the shadow would FADE to none over that 0.4s — during the
     fade it leaks below the header as a faint hairline right at the join,
     appearing then vanishing. Most visible on fractional-DPR laptop screens
     (e.g. 1080p at 150% scaling). The background transition is left intact. */
  transition: background-color 0.4s, box-shadow 0s !important;
}

@media (max-width: 968px) {
  .gallery-filter-float {
    padding: 11px 12px;
  }
}

@media (max-width: 580px) {
  .gallery-filter-float {
    padding: 9px 6px;
  }
}

/* The bar is an overlay, so it must not also reserve height: its inner row
   drops the bottom margin the in-flow one carries. */
.gallery-filter-float .gallery-filter {
  margin: 0 auto;
  padding: 0;
}

/* The real row has arrived and is now occupying the same strip of screen.
   A dissolve cannot be relied on to finish here: on a fast flick the visitor
   covers the whole merge band in a couple of frames, and whatever is left of
   the fading copy would be sitting on top of the genuine row. So this cuts it
   dead instead — correctness over polish, in the one case where the polish
   cannot keep up. Ordinary scrolling never reaches this rule, because the
   merge has already finished by then. */
.gallery-filter-float.is-cut {
  opacity: 0;
  visibility: hidden;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  /* No descent and no dissolve — the bar is simply present or absent. */
  .gallery-filter-float,
  .gallery-filter-float.is-shown,
  .gallery-filter-float.is-merging {
    transform: none;
    transition: none;
  }
}

/* The grid dims while the page glides to the top of a new category, so the
   swap reads as a cross-fade rather than the old photographs being cut away
   mid-travel. Opacity only — fading the height would move the target. */
#photoGrid {
  transition: opacity 0.32s ease;
}

#photoGrid.is-swapping {
  /* Dimmed, not hidden: the visitor should still see the page moving under
     them, otherwise a long travel looks like a blank screen. */
  opacity: 0.42;
}

@media (prefers-reduced-motion: reduce) {
  #photoGrid {
    transition: none;
  }

  #photoGrid.is-swapping {
    opacity: 1;
  }
}

.gallery-filter-btn {
  position: relative;
  /* Above the panel, which sits at 0 in the same stacking context */
  z-index: 1;
  /* Lighter and warmer than the old neutral grey: the unselected pills
     recede so the gold one clearly leads, instead of five outlines
     competing at the same weight. */
  border: 1px solid rgba(160, 148, 128, 0.34);
  border-radius: 999px;
  padding: 11px 26px;
  font-family: "Montserrat", sans-serif;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: #6b6660;
  background-color: #ffffff;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  transition:
    color 0.35s ease,
    border-color 0.35s ease,
    box-shadow 0.35s ease,
    background-color 0.35s ease,
    transform 0.35s ease;
  -webkit-tap-highlight-color: transparent;
}

.gallery-filter-btn > * {
  position: relative;
  z-index: 1;
}

.gallery-filter-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  /* A warm mid taupe, not the site gold. "Get a Quote" in the header
     directly above already owns --secondary-color, and two gold pills
     stacked that close read as the same control. This sits between the
     two: clearly its own colour, but light enough that the row still feels
     airy rather than weighed down by a near-black chip. */
  background: #7c736a;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.42s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 0;
}

.gallery-filter-btn:hover {
  color: #2f2d2a;
  border-color: var(--secondary-color);
  background-color: #fffcf6;
  box-shadow: 0 6px 16px -6px rgba(150, 130, 96, 0.32);
  transform: translateY(-2px);
}

.gallery-filter-btn:focus-visible {
  outline: 2px solid var(--secondary-color);
  outline-offset: 3px;
}

/* The selected ceremony carries the gold fill, a deeper shadow and a slight
   lift, so it reads as the current view rather than one option among five. */
.gallery-filter-btn.is-active {
  color: #ffffff;
  font-weight: 600;
  border-color: #7c736a;
  /* Three layers: a tight seat, a soft neutral glow that separates the pill
     from the panel, and an inner highlight along the top edge that gives
     the fill a light source instead of leaving it flat. Kept gentle so the
     pill reads as raised, not as a heavy block. */
  box-shadow:
    0 2px 6px rgba(112, 103, 93, 0.26),
    0 12px 26px -10px rgba(112, 103, 93, 0.45),
    inset 0 1px 0 rgba(255, 255, 255, 0.26);
  transform: translateY(-2px);
  /* A thin gold ring just outside the edge — the only gold left on the
     pill, which ties it to the palette without repeating the quote button.
     An outline rather than a pseudo-element because the pill sets
     overflow:hidden for its fill sweep, which would clip a child; outline
     is painted outside the box and ignores that clip entirely. */
  outline: 1px solid rgba(201, 185, 154, 0.6);
  outline-offset: 3px;
}

/* The active label carries a whisper of shadow so white type stays crisp
   on the gold, which is otherwise light enough to wash it out. Slightly
   wider tracking makes the selected item read as the heading of the set. */
.gallery-filter-btn.is-active .filter-label {
  /* The lighter fill needs this more, not less: it is what keeps the white
     type crisp instead of letting it blend into the taupe. */
  text-shadow: 0 1px 2px rgba(72, 65, 58, 0.45);
  letter-spacing: 1.9px;
}

.gallery-filter-btn.is-active::before {
  transform: scaleX(1);
  transform-origin: left center;
}

/* The selected pill is already lifted; hovering it should not shove it
   further or it looks unstable next to the others. Placed after .is-active
   so it wins at equal specificity. */
.gallery-filter-btn.is-active:hover {
  transform: translateY(-2px);
  border-color: #7c736a;
  background-color: transparent;
}

/* Focus must still win over the resting ring on .is-active above */
.gallery-filter-btn.is-active:focus-visible {
  outline: 2px solid var(--secondary-color);
  outline-offset: 3px;
}

/* !important because style.min.css sets the section padding with !important
   in its mobile block; without it the gap collapses on a phone, which is
   exactly where the two sections are most likely to run together. */
body.photography-page .testimonials-section {
  padding-top: 84px !important;
  border-top: 1px solid rgba(74, 74, 74, 0.1);
}

/* On laptop screens the reviews should occupy the complete viewport beneath
   the fixed desktop header.  Center the existing review block in that space
   so the preceding gallery and the following section cannot appear as strips
   above or below it. */
@media (min-width: 969px) and (max-width: 1920px) and (min-height: 650px) and (max-height: 1100px) {
  body.photography-page .testimonials-section {
    min-height: calc(100vh - 92px);
    min-height: calc(100svh - 92px);
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    display: flex;
    align-items: center;
  }

  body.photography-page .testimonials-section > .container {
    width: 100%;
  }
}

@media (max-width: 580px) {
  body.photography-page .testimonials-section {
    padding-top: 46px !important;
  }
}

/* ── Masonry columns ── */

/* The legacy .photography-grid rules in style.min.css use CSS multi-column
   (column-count) with !important. That would slice our flex columns apart,
   so the grid is neutralised to a plain block here and the layout is done
   entirely by .photo-columns below. */
.photography-grid {
  display: block !important;
  column-count: unset !important;
  columns: unset !important;
  column-gap: 0 !important;
  line-height: normal;
}

.photo-columns {
  display: flex;
  gap: 5px;
  width: 100%;
  align-items: flex-start;
}

.photo-col {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 5px;
}

.photo-col .photography-item {
  position: relative;
  width: 100%;
  display: block;
  overflow: hidden;
  background: #f2efea;
  border-radius: 3px;
  cursor: pointer;
  /* style.min.css puts margin-bottom:5px on .photography-item, which stacked
     on top of the flex gap and made vertical spacing twice the horizontal.
     The gap alone controls spacing here. */
  margin: 0;
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity 0.6s ease,
    transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* `.is-revealed` is the photography gallery's scroll-reveal class; `.visible`
   is the pre-wedding page's equivalent (it tags every tile visible up front,
   with no per-tile observer). Both pages share these `.photo-col
   .photography-item` styles, so the base opacity:0 above would otherwise leave
   the pre-wedding tiles permanently hidden. Reveal on either class. */
.photo-col .photography-item.is-revealed,
.photo-col .photography-item.visible {
  opacity: 1;
  transform: translateY(0);
}

.photo-col .photography-item img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  object-position: center center;
  opacity: 0;
  /* Held at a slight scale at rest so the drift on hover never exposes an
     edge of the frame. */
  transform: scale(1.03);
  /* transform stays slower than opacity so the small hover drift
     (scale 1.03->1.05, a 1.6% shift) reads as a glide rather than a snap.
     0.9s is the tuned speed applied across every category. */
  transition:
    opacity 0.55s ease,
    transform 0.9s cubic-bezier(0.25, 0.1, 0.25, 1);
  background: #f2efea;
}

.photo-col .photography-item img.loaded {
  opacity: 1;
}

/* Hover: on every category now, the image scales up a touch further and
   drifts to the right inside its frame. The tile itself never moves and
   casts no shadow — the whole gesture is the photograph shifting behind a
   fixed opening. */
.photo-col .photography-item:hover img.loaded {
  transform: scale(1.05) translateX(1.6%);
}

/* Nothing in the gallery casts a shadow, and nothing dims. style.min.css
   carries both from its own layouts — a shadow on the tile and
   `filter: brightness(0.92)` on hover, which is the darkening the gallery is
   deliberately doing without. The movement is the whole effect here, so the
   photograph must stay at full strength while it drifts. */
.photo-col .photography-item,
.photo-col .photography-item:hover,
.photo-col .photography-item img,
.photo-col .photography-item img.loaded {
  box-shadow: none;
}

.photo-col .photography-item:hover img,
.photo-col .photography-item:hover img.loaded {
  filter: none;
}

/* style.min.css also puts a dark wash on hover via ::after
   (background: rgba(0,0,0,0.08)) — the actual source of the "dark overlay"
   the drift-only design was meant to remove. Neutralised here rather than
   edited at the source, which is the generated/minified stylesheet. */
.photo-col .photography-item::after {
  background: transparent !important;
}

.photo-col .photography-item:hover::after {
  background: transparent !important;
}

/* ── Load more ── */

.gallery-loadmore-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 42px;
}

/* No trailing gap when the button is hidden (single-ceremony views) */
.gallery-loadmore-wrap:empty,
.gallery-loadmore-wrap:has(> .gallery-loadmore-btn[hidden]) {
  margin-top: 0;
}

.gallery-loadmore-btn {
  position: relative;
  background: transparent;
  border: 1px solid rgba(74, 74, 74, 0.22);
  border-radius: 999px;
  padding: 14px 42px;
  font-family: "Montserrat", sans-serif;
  font-size: 0.73rem;
  font-weight: 500;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: #4a4a4a;
  cursor: pointer;
  overflow: hidden;
  isolation: isolate;
  transition:
    color 0.35s ease,
    border-color 0.35s ease;
  -webkit-tap-highlight-color: transparent;
}

/* The label sits above the sliding fill, otherwise the text turns white
   against an unfilled background and disappears. */
.gallery-loadmore-btn > span {
  position: relative;
  z-index: 1;
}

.gallery-loadmore-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--secondary-color);
  transform: scaleY(0);
  transform-origin: bottom center;
  transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  z-index: 0;
}

.gallery-loadmore-btn:hover {
  color: #ffffff;
  border-color: var(--secondary-color);
}

.gallery-loadmore-btn:hover::before {
  transform: scaleY(1);
  transform-origin: top center;
}

.gallery-loadmore-btn[hidden] {
  display: none;
}

/* ── Tablet ── */
@media (max-width: 968px) {
  .photo-columns {
    gap: 5px;
  }

  .photo-col {
    gap: 5px;
  }

  .gallery-filter {
    gap: 6px;
    margin-bottom: 28px;
  }

  .gallery-filter-btn {
    padding: 9px 19px;
    font-size: 0.66rem;
    letter-spacing: 1.2px;
  }
}

/* ── Mobile: one photo per row, so each frame gets the full screen width ── */
@media (max-width: 580px) {
  /* Single column, so the horizontal gap never applies; the vertical rhythm
     opens up because each photo now reads as its own frame rather than one
     cell of a grid. */
  .photo-columns {
    gap: 0;
  }

  .photo-col {
    gap: 10px;
  }

  /* All five pills stay on one line. The label size tracks the viewport so
     the row fills the width on a large phone and still fits a 320px one;
     the clamp floor is the smallest size the longest label ("Engagement")
     can take and still fit. nowrap keeps flex from breaking the row.
     The type is deliberately a notch smaller than it could be, so the
     spare width goes into the gaps and side padding instead of the
     letters — the row reads as five separate chips, not one dense strip. */
  .gallery-filter {
    flex-wrap: nowrap;
    justify-content: center;
    gap: clamp(5px, 1.6vw, 9px);
    padding: 0 4px;
    margin-bottom: 26px;
  }

  .gallery-filter-btn {
    padding: 9px clamp(7px, 2.6vw, 15px);
    font-size: clamp(0.47rem, 2.2vw, 0.64rem);
    letter-spacing: 0.4px;
    white-space: nowrap;
    text-align: center;
    flex: 0 1 auto;
    min-width: 0;
  }

  /* Touch devices have no hover: hold the rest scale, skip the drift */
  .photo-col .photography-item:hover img.loaded {
    transform: scale(1.03);
  }

  .gallery-loadmore-btn {
    padding: 13px 34px;
    font-size: 0.68rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .photo-col .photography-item,
  .photo-col .photography-item img,
  .gallery-filter-btn::before,
  .gallery-loadmore-btn::before {
    transition: none !important;
  }

  .photo-col .photography-item {
    opacity: 1;
    transform: none;
  }

  /* Keep the rest scale so the tile still fills its frame; just no drift */
  .photo-col .photography-item:hover img.loaded {
    transform: scale(1.03);
  }
}

/* Homepage "Explore Our Photography" cards: cancel the blog carousel's
   zoom-on-hover for the image (style.min.css sets scale(1.1) on
   `.blog-item:hover .blog-image img`). The card keeps its own hover treatment;
   only the photograph is held still. Scoped to the homepage section and made
   more specific than the source rule so it wins without !important. */
body.home-page .blog-section .blog-item:hover .blog-image img {
  transform: none;
}

/* Whole-card click target. Each category card carries an overlay anchor
   (`.card-cover-link`) stretched over the entire card, so a click anywhere on
   it, not just the title, image or "View Gallery", opens that gallery. The
   card is the positioning context; the overlay sits above the card body but
   below the "View Gallery" link so that link keeps its own hover/focus. */
body.home-page .blog-section .is-category-card {
  position: relative;
}
body.home-page .blog-section .is-category-card .card-cover-link {
  position: absolute;
  inset: 0;
  z-index: 2;
  /* purely a hit area — no visual of its own */
  font-size: 0;
  cursor: pointer;
}
/* Keep the real "View Gallery" link interactive and on top of the overlay so
   it still receives hover/focus styling; it points to the same gallery. */
body.home-page .blog-section .is-category-card .read-more {
  position: relative;
  z-index: 3;
}

/* ── 4K only: enlarge the category-card titles ──────────────────────────────
   The category-card titles ("The Mehndi Evening", "Your Wedding Day"…) are the
   same 20.97px as the uppercase blog-story titles ("THE VILLAGE LAKE RESORT…"),
   but Title Case at the same px reads noticeably smaller than ALL CAPS, so on a
   4K screen they looked tiny. Bump them to sit level with the uppercase titles.
   Scoped to >=2500px (this site's 4K breakpoint) so laptop/desktop are untouched. */
@media (min-width: 2500px) {
  body.home-page .blog-section .blog-content h3,
  body.home-page .blog-section .blog-content h3 a {
    font-size: 24px !important;
  }
}

/* ── Rounded corners on the home-page card boxes ──
   Each of these boxes already sets overflow:hidden, so a border-radius on the
   box clips the photograph (and its hover zoom) to match. A gentle 14px keeps
   the editorial feel — enough to soften the hard corners without turning the
   cards into pill shapes. The booking form is a larger panel, so it carries a
   slightly bigger radius to stay in proportion. */
.film-thumbnail,        /* Films / video carousel */
.portfolio-image,       /* Blog grid (Village Lake, Hotel Divine, …) */
.blog-image,            /* Category cards (VIEW GALLERY) */
.instagram-item {       /* Instagram grid */
  border-radius: 0;
}
.booking-form {         /* Contact form panel */
  border-radius: 0;
}

/* ── Mobile: square gallery thumbnails on Photography & Pre-Wedding pages ──
   The .photography-item thumbnails keep their 3px softening on desktop, but on
   phones the client wants perfectly square corners to match the rest of the
   mobile layout. */
@media (max-width: 968px) {
  .photo-col .photography-item {
    border-radius: 0;
  }
}

/* ── "Get a Quote" anchor lands below the fixed header ──
   The Get a Quote links (href="/#book") scroll the booking section to the very
   top of the viewport, where the fixed white header (~68px) then covered the
   "LET'S MAKE YOUR WEDDING…" title, so only the "Get in Touch" subtitle showed.
   scroll-margin-top offsets the anchor stop by the header height plus a little
   breathing room, so the section arrives just under the header with its full
   title and form in view — the same framing as scrolling to it normally. */
#book {
  scroll-margin-top: 80px;
}
