/* home.css - Landing Page Styles */

/* --- Hero Section --- */
.hero {
    position: relative;
    background: #ffffff;
    min-height: 80vh;
    display: flex;
    align-items: center;
    padding: 120px 20px 80px;
    overflow: hidden;
    color: var(--npt-black);
}

/* Two-column layout: copy on the left, square image on the right */
.hero-inner {
    position: relative;
    z-index: 2;
    width: 100%;
    /* Was a flat 1200px, which capped the whole hero: on a 2560px monitor the
       collage could never exceed 48% of 1200. The rail now opens up on large
       displays so the photographs have somewhere to grow into. */
    max-width: min(1560px, 92vw);
    margin: 0 auto;
    display: flex;
    align-items: center;
    gap: clamp(40px, 4vw, 80px);
}

.hero-content {
    flex: 1;
    text-align: left;
    /* Makes the column itself the reference for the cqi type scale on
       .hero-title, so the headline sizes against the space it actually has
       rather than against the viewport.

       width:100% is load-bearing, not decoration. container-type also applies
       inline-size containment, which stops the contents from contributing to
       the element's own intrinsic width. In the row layout `flex: 1` still
       grows it, but once .hero-inner stacks to a column below 900px there is
       nothing left to size it and it collapsed to 0 — which pinned the
       headline to the bottom of its clamp on every phone. flex-basis from
       `flex: 1` still wins on the main axis, so this only affects the stacked
       case. */
    width: 100%;
    /* Now that the rail widens on large displays, the copy would otherwise
       stretch to a line length nobody can track across. */
    max-width: 660px;
    container-type: inline-size;
}

/* The install collage. Fluid rather than a fixed 560px so it grows with the
   display: ~576px at 1440, ~768px at 1920, and up to 820px beyond that. The
   48% ceiling keeps it from crowding the copy on mid-size screens, where that
   share of the rail is the tighter of the two limits. */
.hero-image {
    flex: 0 0 auto;
    width: clamp(320px, 40vw, 820px);
    max-width: 48%;
}

.hero-image img {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 24px;
    box-shadow: 0 24px 60px rgba(0,0,0,0.45), 0 0 0 1px rgba(255,255,255,0.06);
    /* Branded placeholder so the square looks intentional before the photo is imported */
    background: linear-gradient(135deg, #0B8C38, #03A63C);
}

.hero-title {
    font-family: var(--font-heading, 'Montserrat', sans-serif);
    /* The headline must always break as exactly two lines:
           Fiber internet built for
           the towns we live in.
       Measured in Montserrat 800, the longer half renders at 11.9x the font
       size, so it fits a column of width W whenever the font size is at most
       W / 11.9, i.e. 8.4cqi. 8cqi leaves headroom for the fallback face while
       the webfont loads. The 2.85rem cap is the largest size that still fits
       the 580px column of the two-up desktop layout. */
    font-size: clamp(1.35rem, 8cqi, 2.85rem);
    font-weight: 800;
    line-height: 1.1;
    margin-bottom: 1.5rem;
    letter-spacing: -1px;
}

.hero-line {
    display: block;
    white-space: nowrap;
}

.highlight-text {
    color: var(--cfn-green);
    background: -webkit-linear-gradient(45deg, var(--cfn-green), #73bf4b, #bdd63a);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* The font URL only requests Open Sans 400, 500 and 600. Asking for 800 here
   left the browser to synthesise it, which thickens the strokes unevenly and is
   what made the price look off next to the rest of the line. 600 is a real
   loaded weight and still separates cleanly from 400 body copy. */
.hero-subtitle strong {
    color: var(--npt-black);
    font-weight: 600;
}

.hero-subtitle {
    font-family: var(--font-body, 'Open Sans', sans-serif);
    font-size: 1.25rem;
    color: #475569;
    margin-bottom: 2.5rem;
    line-height: 1.6;
    /* Was 600. Semibold is an emphasis weight; a whole sentence set in it reads
       dense and slightly cramped, especially at 20px. Regular lets the bolded
       price actually stand out. */
    font-weight: 400;
}

/* --- Availability Search Section (its own premium panel) --- */
.availability-section {
    background-color: #ffffff;
    padding: 70px 24px 90px;
}

.availability-inner {
    max-width: 1800px;
    margin: 0 auto;
    position: relative;
    /* Not overflow:hidden. It was here to keep the dot texture inside the
       rounded corners, but it also cropped the address suggestions the moment
       the list grew taller than the card. The texture clips itself instead
       (see ::before below), so nothing needs clipping at this level. */
    border-radius: 32px;
    padding: 64px 40px;
    text-align: center;
    color: #ffffff;
    /* Same gradient as the "We Call Home" hero lettering */
    background: linear-gradient(45deg, var(--cfn-green), #a8e063);
    box-shadow: 0 30px 60px rgba(3, 166, 60, 0.28);
}

/* Subtle dot texture inside the panel */
.availability-inner::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background-image: radial-gradient(rgba(255, 255, 255, 0.10) 1px, transparent 1px);
    background-size: 26px 26px;
    pointer-events: none;
}

.availability-section h2 {
    position: relative;
    z-index: 1;
    font-size: clamp(2.4rem, 4.5vw, 3.5rem);
    color: #ffffff;
    font-family: var(--font-heading);
    font-weight: 800;
    line-height: 1.15;
    margin-bottom: 14px;
}

.availability-section p {
    position: relative;
    z-index: 1;
    font-size: clamp(1.2rem, 1.6vw, 1.4rem);
    color: rgba(255, 255, 255, 0.9);
    margin-bottom: 34px;
}

/* The panel's own search. Sized and centred inside the green card, with the
   same pill treatment the bar uses so the two read as one component. */
.availability-inner .availability-search-container {
    position: relative;
    /* Above the fixed availability bar (9000). This selector is more specific
       than the base rule in address-bar.css, so the value has to be repeated
       here or the panel's suggestion list drops back under the bar. */
    z-index: 9500;
    max-width: 640px;
    margin: 0 auto;
}

.availability-inner .search-input-wrapper {
    padding: 6px 6px 6px 4px;
    box-shadow: 0 14px 30px rgba(15, 23, 42, 0.2);
}

.availability-inner .search-input-wrapper:focus-within {
    transform: none;
    box-shadow: 0 16px 36px rgba(15, 23, 42, 0.28), 0 0 0 3px rgba(255, 255, 255, 0.55);
}


/* --- Residential Promo Section --- */
.res-promo-section {
    padding: 80px 20px;
    background-color: #ffffff;
    border-bottom: 1px solid #f1f5f9;
}

.res-promo-content {
    display: flex;
    align-items: center;
    gap: 60px;
    max-width: 1240px;
    margin: 0 auto;
}

.res-promo-text {
    flex: 1;
    text-align: left;
}

.section-tag {
    display: inline-block;
    background-color: #dcfce7;
    color: var(--cfn-dark-green);
    font-weight: 700;
    font-size: 0.85rem;
    padding: 6px 12px;
    border-radius: 4px;
    text-transform: uppercase;
    margin-bottom: 15px;
    letter-spacing: 0.5px;
}

.res-promo-text h2 {
    font-size: 2.5rem;
    color: var(--npt-black);
    margin-bottom: 20px;
    line-height: 1.1;
    font-family: var(--font-heading);
    font-weight: 800;
}

.res-promo-text p {
    font-size: 1.1rem;
    color: #64748b;
    margin-bottom: 30px;
    line-height: 1.6;
}

.res-promo-image {
    flex: 1.5;
    display: flex;
    justify-content: center;
    align-items: center;
}

.res-promo-image img {
    width: 100%;
    max-width: 760px;
    height: auto;
    object-fit: contain;
    filter: drop-shadow(0 20px 30px rgba(0,0,0,0.1));
}

.btn-primary-action {
    display: inline-flex;
    align-items: center;
    background-color: var(--cfn-green);
    color: white;
    padding: 14px 32px;
    border-radius: 50px;
    font-weight: 700;
    text-transform: uppercase;
    font-size: 1rem;
    text-decoration: none;
    transition: all 0.3s ease;
    box-shadow: 0 4px 6px rgba(46, 204, 113, 0.2);
}

.btn-primary-action:hover {
    background-color: var(--cfn-dark-green);
    transform: translateY(-2px);
    box-shadow: 0 10px 20px rgba(46, 204, 113, 0.3);
}

.btn-primary-action .cfn-icon {
    margin-left: 10px;
    transition: transform 0.2s;
}

.btn-primary-action:hover .cfn-icon {
    transform: translateX(4px);
}



/* --- Other Services Section --- */
.other-services-section {
    padding: 96px 20px 130px;
    /* Soft green "sunset" glow rising from the bottom-center into the footer */
    background:
        radial-gradient(130% 65% at 50% 128%, rgba(3, 166, 60, 0.32) 0%, rgba(139, 217, 173, 0.18) 40%, transparent 72%),
        linear-gradient(180deg, #ffffff 0%, #f5faf6 100%);
    overflow: hidden;
    position: relative;
}

.explore-header {
    max-width: 680px;
    margin: 0 auto 56px;
    text-align: center;
}

.section-title-small {
    text-align: center;
    color: var(--npt-black);
    font-family: var(--font-heading);
    font-size: clamp(2rem, 4vw, 3rem);
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.1;
    margin: 0 0 14px;
    text-transform: none;
}

.explore-header p {
    color: #475569;
    font-size: 1.08rem;
    line-height: 1.65;
    margin: 0;
}

.services-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 22px;
    max-width: 1140px;
    margin: 0 auto;
}

.service-card {
    background: var(--service-card-bg, #2f7f67);
    border: 1px solid rgba(255, 255, 255, 0.2);
    border-radius: 16px;
    text-decoration: none;
    color: #ffffff;
    display: flex;
    flex-direction: column;
    min-height: 370px;
    position: relative;
    overflow: hidden;
    box-shadow: 0 14px 32px rgba(15, 23, 42, 0.11);
    transition: transform 0.3s var(--transition-smooth),
                box-shadow 0.3s var(--transition-smooth);
    isolation: isolate;
}

.service-card-residential {
    --service-card-bg: #4f8f82;
}

.service-card-business {
    --service-card-bg: #55799b;
}

.service-card-builders {
    --service-card-bg: #8a7a55;
}

.service-card-support {
    --service-card-bg: #687aa7;
}

.service-card-about {
    --service-card-bg: #3f765f;
}

.service-card-news {
    --service-card-bg: #536fa8;
}

/* One confident hover state: a gentle lift */
.service-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 18px 38px rgba(15, 23, 42, 0.16);
}

.service-media {
    position: absolute;
    right: 0;
    bottom: -28px;
    left: 0;
    height: 190px;
    overflow: hidden;
    pointer-events: none;
    z-index: 1;
}

.service-media img {
    width: auto;
    height: 100%;
    max-width: none;
    object-fit: contain;
    display: block;
    margin-left: auto;
    transition: transform 0.6s var(--transition-smooth);
    filter: drop-shadow(0 18px 28px rgba(15, 23, 42, 0.2));
}

.service-card:hover .service-media img {
    transform: translateY(-4px) scale(1.03);
}

.service-media-router {
    height: 170px;
    bottom: -16px;
    right: -20px;
}

.service-media-router img {
    height: 150px;
}

.service-media-ont {
    height: 198px;
    bottom: -22px;
    right: -46px;
}

.service-media-hat {
    height: 210px;
    bottom: -34px;
    right: 18px;
}

.service-media-headset {
    height: 230px;
    bottom: -36px;
    right: 14px;
}

.service-media-headset img {
    filter: drop-shadow(0 18px 26px rgba(15, 23, 42, 0.28));
}

.service-media-building {
    height: 220px;
    right: 0;
    bottom: 0px;
    left: 0;
    border-radius: 0;
    box-shadow: none;
}

.service-media-building img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 42%;
    filter: none;
}

.service-media-mailbox {
    height: 244px;
    right: -20px;
    bottom: -78px;
}

.service-content {
    display: flex;
    flex-direction: column;
    min-height: 196px;
    padding: 26px 24px 22px;
    position: relative;
    z-index: 2;
}

.service-content h3 {
    font-size: 1.33rem;
    font-weight: 700;
    margin-bottom: 10px;
    color: #ffffff;
    font-family: var(--font-heading);
    line-height: 1.25;
}

.service-content p {
    color: rgba(255, 255, 255, 0.84);
    font-size: 0.95rem;
    line-height: 1.6;
    max-width: 275px;
    margin-bottom: 18px;
}

.service-link {
    font-weight: 700;
    color: #ffffff;
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    transition: gap 0.25s ease;
    margin-top: auto;
}

.service-card:hover .service-link {
    gap: 12px;
}

@media (max-width: 1040px) {
    .services-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Responsive */
@media (max-width: 900px) {
    .hero {
        padding: 56px 18px 64px;
        min-height: auto;
    }

    .hero-inner {
        flex-direction: column;
        text-align: center;
        gap: 32px;
    }

    .hero-content {
        text-align: center;
    }

    /* The collage no longer leads on a phone. `order: -1` made sense when this
       was one hero photo, but the orbit is a tall square: at 440px it pushed the
       headline, the price and the CTA entirely below the fold. The message goes
       first and the photographs support it. */
    .hero-image {
        width: 100%;
        /* Fluid rather than a flat 340px, which made the collage jump smaller
           at the exact point the layout stacked — narrower at 900px than it had
           been at 980px. The 440px ceiling keeps it from swallowing a tablet
           screen, and 300px keeps the CTA above the fold on a phone. */
        max-width: clamp(300px, 45vw, 440px);
        margin: 0 auto;
    }

    /* Keep the stacked card aligned with the extended reel while retaining a
       small visual handoff between them. */
    .availability-section {
        margin-top: -18px;
    }

    .availability-inner {
        max-width: 760px;
    }

    .res-promo-content {
        flex-direction: column;
        text-align: center;
        gap: 40px;
    }

    .res-promo-text {
        text-align: center;
    }

    .res-promo-image {
        order: -1; /* Image on top for mobile? Or keep bottom? Default is bottom if not set. */
    }
}

@media (max-width: 640px) {
    .hero {
        min-height: auto;
        padding: 34px 16px 48px;
    }

    .hero-inner {
        gap: 28px;
    }

    .hero-title {
        letter-spacing: -0.03em;
    }

    .hero-subtitle {
        font-size: 1.05rem;
        margin-bottom: 1.8rem;
    }

    .hero-image {
        max-width: 300px;
    }

    .availability-section {
        /* Positive here because the compact paddings total only 90px. */
        margin-top: 26px;
        padding: 42px 14px 58px;
    }

    .availability-inner {
        border-radius: 20px;
        padding: 34px 16px;
    }

    .availability-section h2 {
        font-size: clamp(1.75rem, 9vw, 2.35rem);
    }

    .availability-section p {
        font-size: 1rem;
        margin-bottom: 22px;
    }

    .availability-inner .search-input-wrapper {
        border-radius: 18px;
        padding: 6px 8px;
    }

    .availability-inner .cfn-address-input {
        font-size: 1rem;
        min-width: 0;
    }

    .res-promo-section,
    .other-services-section {
        padding-left: 16px;
        padding-right: 16px;
    }

    .res-promo-section {
        padding-top: 54px;
        padding-bottom: 58px;
    }

    .res-promo-content {
        gap: 28px;
    }

    .res-promo-text h2 {
        font-size: clamp(1.85rem, 9vw, 2.35rem);
    }

    .services-grid {
        grid-template-columns: 1fr;
        gap: 16px;
    }

    .service-card {
        min-height: 300px;
        border-radius: 12px;
    }

    .service-content {
        min-height: 168px;
        padding: 24px 22px 20px;
    }

    .service-content p {
        max-width: 310px;
    }

    .service-media-router {
        height: 150px;
        bottom: -8px;
    }

    .service-media-router img {
        height: 132px;
    }

    .service-media-ont {
        height: 178px;
        right: -38px;
    }

    .service-media-hat {
        height: 190px;
        bottom: -30px;
        right: 8px;
    }

    .service-media-headset {
        height: 202px;
        bottom: -34px;
        right: 8px;
    }

    .service-media-building {
        height: 164px;
        bottom: 0;
    }

    .service-media-mailbox {
        height: 190px;
        bottom: -56px;
    }

    .other-services-section {
        padding-top: 62px;
        padding-bottom: 78px;
    }

    .explore-header {
        margin-bottom: 32px;
    }
}

/* --- Service Areas Section (Locally Served Communities) --- */
.service-areas-section {
    padding: 90px 20px;
    background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
    border-top: 1px solid #e2e8f0;
    scroll-margin-top: 110px;
}

.service-areas-inner {
    max-width: 1100px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 300px 1fr;
    gap: 60px;
    align-items: center;
}

.service-areas-map {
    text-align: center;
}

.service-areas-map img {
    width: 100%;
    max-width: 260px;
    height: auto;
    display: block;
    margin: 0 auto;
    filter: drop-shadow(0 12px 22px rgba(3, 166, 60, 0.16));
}

.service-areas-content h2 {
    font-family: var(--font-heading);
    font-size: clamp(1.8rem, 4vw, 2.4rem);
    font-weight: 800;
    color: var(--npt-black);
    line-height: 1.15;
    margin: 0 0 14px;
}

.service-areas-content h2 span {
    color: var(--cfn-green);
}

.service-areas-content > p {
    color: #475569;
    font-size: 1.05rem;
    line-height: 1.65;
    margin: 0 0 28px;
    max-width: 560px;
}

.service-areas-links {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr));
    column-gap: 40px;
    list-style: none;
    margin: 0;
    padding: 0;
    max-width: 480px;
}

.service-areas-links a {
    display: block;
    padding: 12px 2px;
    border-bottom: 1px solid #e2e8f0;
    text-decoration: none;
    color: #334155;
    font-family: var(--font-heading);
    font-weight: 600;
    font-size: 0.95rem;
    transition: color 0.2s ease, border-color 0.2s ease;
}

.service-areas-links a:hover {
    color: var(--cfn-green);
    border-color: var(--cfn-green);
}

@media (max-width: 850px) {
    .service-areas-section {
        padding: 62px 16px;
    }

    .service-areas-inner {
        grid-template-columns: 1fr;
        gap: 24px;
    }

    .service-areas-map img {
        max-width: 190px;
    }

    .service-areas-content {
        text-align: center;
    }

    .service-areas-content > p {
        margin-left: auto;
        margin-right: auto;
    }

    .service-areas-links {
        margin-left: auto;
        margin-right: auto;
        grid-template-columns: 1fr 1fr;
        gap: 0 18px;
    }
}

@media (max-width: 420px) {
    .btn-primary-action {
        width: 100%;
        justify-content: center;
        padding-left: 18px;
        padding-right: 18px;
        font-size: 0.92rem;
    }

    .service-areas-links {
        grid-template-columns: 1fr;
    }
}

/* --- Hero photo reel overlapping the availability panel ------------------
   Desktop only. Below 901px the hero stacks and the reel sits under the copy,
   where pulling the panel up would drop photos onto its heading. */
@media (min-width: 901px) {
    /* overflow-x:clip, not hidden: hidden on one axis forces the other to auto,
       which would cut off the reel's rise up behind the header. */
    .hero {
        overflow-x: clip;
        overflow-y: visible;
    }

    /* Run the reel up behind the header so the photos disappear under it rather
       than starting at a hard edge below it.

       The negative margin fixes the reel's top behind the header. Its height is
       defined in the base rule below, where an equal negative bottom margin
       keeps the surrounding layout stable while only the lower edge grows. */
    .photo-reel {
        --reel-rise: 340px;
        margin-top: calc(-1 * var(--reel-rise));
    }

    .hero-image {
        position: relative;
        z-index: 3;
    }

    .availability-section {
        /* Pull the card itself into the hero's empty lower field. Resizing the
           reel could not close this gap because the whitespace belongs to the
           surrounding sections, not to the reel. */
        margin-top: -82px;
        /* No white band above the green panel. The section covers the reel from
           its own top edge, so any padding here shows as blank white between
           the photos and the card — the photos have to run straight into the
           green, not stop above it. The card's own 64px padding keeps the
           heading off the edge. */
        padding-top: 0;
        /* Above .hero-image (z-index 3) so this section's opaque white ground
           covers where the reel stops. The photos run underneath and the hard
           cutoff is never seen — which is what the mask used to hide. */
        position: relative;
        z-index: 4;
    }
}

/* --- Hero photo reel -----------------------------------------------------
   Two columns of install photos drifting in opposite directions. Replaces the
   orbiting tile ring, which had to shrink every photo small enough to fit
   inside a circle — at 1440 the tiles were ~190px, so the work in them was
   barely legible. Here each photo gets roughly half the width of the block.

   The seam: each column repeats its own list once and travels exactly -50%, so
   the second copy arrives precisely where the first started and the loop has no
   visible jump. Transform-only, so it runs on the compositor. */
.photo-reel {
    position: relative;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: clamp(12px, 1.4vw, 20px);
    /* The reel runs past its layout box and the availability section (z-index 4,
       opaque white) is drawn over the tail, so the hard cutoff happens out of
       sight beneath it. The negative margin cancels the extra height, so the
       section does not move and the visible amount of photo is unchanged —
       the tail only buys depth to hide the cut. Without it the section covered
       the edge by 2px (80px hero padding minus its 82px pull). */
    --reel-tail: 160px;
    height: calc(clamp(560px, 56vw, 800px) + var(--reel-tail));
    margin-bottom: calc(-1 * var(--reel-tail));
    overflow: hidden;
}

/* Slideshow position dots. Only meaningful for the mobile swipe strip, so the
   base state is hidden and the mobile block turns them on. */
.photo-reel-dots {
    display: none;
}

.photo-reel-col {
    display: flex;
    flex-direction: column;
    gap: clamp(12px, 1.4vw, 20px);
    will-change: transform;
}


.photo-reel-col--up {
    animation: photo-reel-up 42s linear infinite;
}

/* Offset so the two columns never line up rung-for-rung, which would read as a
   grid sliding rather than two independent reels. */
.photo-reel-col--down {
    animation: photo-reel-down 48s linear infinite;
}

.hero-image .photo-reel-col img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    border-radius: 16px;
    box-shadow: 0 12px 30px rgba(15, 23, 42, 0.16);
    background: linear-gradient(135deg, #0B8C38, #03A63C);
}

@keyframes photo-reel-up {
    from { transform: translateY(0); }
    to   { transform: translateY(-50%); }
}

@keyframes photo-reel-down {
    from { transform: translateY(-50%); }
    to   { transform: translateY(0); }
}

/* --- Mobile: swipeable slideshow ----------------------------------------
   Two drifting columns need vertical room a phone does not have — the reel was
   eating most of the first screen. Below 901px the same photos become one
   horizontal scroll-snap strip: native momentum swipe, no JS, and roughly half
   the height.

   Deliberately placed after the base rules. Media queries add no specificity,
   so the earlier max-width block near the top of this file would lose to
   .photo-reel above. */
@media (max-width: 900px) {
    .hero-image {
        /* The max-width caps above suited a static collage. A swipe strip wants
           the full width so each slide fills the screen edge to edge. */
        max-width: none;
        width: 100%;
    }

    .photo-reel {
        display: flex;
        flex-direction: row;
        height: clamp(240px, 58vw, 360px);
        /* Slides are inset by 12px so the rounded corners read as separate
           cards. Basis 100%-24px with a 24px gap puts the next slide exactly
           one container width away, so one photo fills the view and none of
           the next one peeks in — and reel.clientWidth stays the scroll step
           the autoplay uses. */
        gap: 24px;
        padding-inline: 12px;
        scroll-padding-inline: 12px;
        /* No tail on mobile: nothing overlaps the reel here to hide it. */
        margin-bottom: 0;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior-x: contain;
        scrollbar-width: none;
    }

    .photo-reel::-webkit-scrollbar {
        display: none;
    }

    /* The columns stop generating boxes, so their images become the strip's own
       flex items in source order. */
    .photo-reel-col {
        display: contents;
    }

    .photo-reel-col--up,
    .photo-reel-col--down {
        animation: none;
        transform: none;
    }

    .hero-image .photo-reel-col img {
        flex: 0 0 calc(100% - 24px);
        width: calc(100% - 24px);
        height: 100%;
        /* The 4:5 portrait ratio would make a full-width slide taller than the
           screen. The container sets the height and object-fit crops to it. */
        aspect-ratio: auto;
        border-radius: 16px;
        scroll-snap-align: start;
    }

    .photo-reel-dots {
        display: flex;
        justify-content: center;
        align-items: center;
        gap: 8px;
        margin-top: 14px;
    }

    .photo-reel-dot {
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: rgba(15, 23, 42, 0.22);
        transition: background 0.2s ease, transform 0.2s ease;
    }

    .photo-reel-dot.is-active {
        background: var(--cfn-green, #03A63C);
        transform: scale(1.4);
    }

    /* Each column lists its photos twice so the vertical loop has an invisible
       seam. A swipe strip has no seam, and the repeat would just read as a
       mistake, so the second copy is dropped. Matched by class rather than
       nth-child: the columns no longer hold the same number of photos, and a
       positional rule silently hides the wrong ones the moment a photo is
       added to either column. */
    .photo-reel-col img.reel-loop-copy {
        display: none;
    }
}

/* Motion off: the reel becomes a still pair of columns, cropped the same way.
   Nothing is lost but the drift. */
@media (prefers-reduced-motion: reduce) {
    .photo-reel-col--up,
    .photo-reel-col--down {
        animation: none;
    }

    .photo-reel-col--down {
        transform: translateY(-50%);
    }
}
