/* ==========================================================================
   norlux-ui.css — Norlux Paints design system layer
   --------------------------------------------------------------------------
   Loaded AFTER bootstrap.min.css and style.css, so it is the last word.

   WHY THIS FILE EXISTS
   The compiled css/bootstrap.min.css (Bootstrap 5.0.0) has hardcoded colour
   utilities — .bg-light is baked in as #cb0707, a RED, where the scss source
   says #F2F3F5. Because .bg-light sits on the navbar of every page and on the
   feature/contact sections, the whole site renders drenched in red. The colour
   utilities are compiled literals with !important, so overriding --bs-* custom
   properties does nothing; the utilities have to be redefined outright. That is
   what the "Bootstrap utility repairs" block below does.

   PALETTE — read off img/logo.png (red #F00000, navy #104080, white) and then
   disciplined: navy carries the design, red is reserved for calls to action and
   accents. Before this file, red was ~40% of the page; after, it is ~5%.

   Nothing here needs a build step: plain CSS custom properties.
   ========================================================================== */

/* ==========================================================================
   1 · TOKENS
   ========================================================================== */
:root {
    /* Brand */
    --nx-navy:        #0B2A5B;
    --nx-navy-600:    #123A75;
    --nx-navy-800:    #08203F;
    --nx-navy-900:    #061831;
    --nx-red:         #E01B1B;
    --nx-red-600:     #C41616;
    --nx-red-700:     #A81212;

    /* Surfaces + ink */
    --nx-white:       #FFFFFF;
    --nx-mist:        #F5F7FA;
    --nx-mist-200:    #EDF1F7;
    --nx-line:        #E3E8EF;
    --nx-ink:         #1A1D21;
    --nx-body:        #4B5563;
    --nx-muted:       #6B7280;

    /* Tint of navy, for scrims and shadows that must stay in-family */
    --nx-navy-a08:    rgba(11, 42, 91, 0.08);
    --nx-navy-a12:    rgba(11, 42, 91, 0.12);
    --nx-navy-a18:    rgba(11, 42, 91, 0.18);

    /* Shape */
    --nx-radius:      14px;
    --nx-radius-sm:   10px;
    --nx-radius-lg:   20px;
    --nx-radius-pill: 999px;

    /* Depth — tinted navy, never neutral black, so cards sit in the palette */
    --nx-shadow-1: 0 1px 2px var(--nx-navy-a08), 0 2px 8px var(--nx-navy-a08);
    --nx-shadow-2: 0 8px 24px var(--nx-navy-a12);
    --nx-shadow-3: 0 20px 56px var(--nx-navy-a18);

    /* Motion — one ease for the whole site, so everything feels related */
    --nx-ease:      cubic-bezier(0.22, 0.61, 0.36, 1);
    --nx-ease-out:  cubic-bezier(0.16, 1, 0.3, 1);
    --nx-fast:      0.18s;
    --nx-base:      0.32s;
    --nx-slow:      0.6s;

    /* Vertical rhythm — one value drives every section, which is most of what
       "looks more professional" actually means. */
    --nx-section-y: clamp(3.25rem, 6.5vw, 6rem);
    --nx-gutter:    clamp(1rem, 2.5vw, 2rem);
}

/* ==========================================================================
   2 · BASE REPAIRS
   The old index.html inline block had a global `img { width: 100%; height: auto }`
   which stretched every image on the page — including the logo — to its
   container. Images are made fluid per component instead.
   ========================================================================== */
body {
    font-family: 'Roboto', system-ui, -apple-system, "Segoe UI", sans-serif;
    color: var(--nx-body);
    background: var(--nx-white);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;           /* stops the 1px horizontal scroll on mobile */
}

img,
svg,
video {
    max-width: 100%;
    height: auto;
}

/* The logo is a fixed-height brand mark, not fluid content. */
.navbar-brand img {
    height: clamp(56px, 7vw, 78px) !important;
    width: auto !important;
    max-width: none;
    transition: height var(--nx-base) var(--nx-ease);
}

a { text-decoration: none; }

::selection {
    background: var(--nx-navy);
    color: #fff;
}

:focus-visible {
    outline: 3px solid var(--nx-red);
    outline-offset: 2px;
}

/* ==========================================================================
   3 · TYPE SCALE
   Fluid, so headings stop overflowing on small screens without a stack of
   per-breakpoint font-size overrides.
   ========================================================================== */
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: "Playfair Display", Georgia, serif;
    color: var(--nx-ink);
    font-weight: 700;
    line-height: 1.15;
    letter-spacing: -0.01em;
}

h1, .h1           { font-size: clamp(2rem,   4.4vw, 3.5rem); }
h2, .h2           { font-size: clamp(1.65rem, 3.4vw, 2.6rem); }
h3, .h3           { font-size: clamp(1.35rem, 2.4vw, 1.9rem); }
h4, .h4           { font-size: clamp(1.15rem, 1.8vw, 1.4rem); }
h5, .h5           { font-size: 1.1rem; }

.display-1, .display-2, .display-3,
.display-4, .display-5, .display-6 {
    font-family: "Playfair Display", Georgia, serif;
    line-height: 1.1;
    letter-spacing: -0.015em;
}
.display-4 { font-size: clamp(1.9rem, 4vw, 3.1rem); }
.display-5 { font-size: clamp(1.7rem, 3.4vw, 2.6rem); }
.display-6 { font-size: clamp(1.45rem, 2.6vw, 2.05rem); }

p { line-height: 1.7; }

.lead,
p.fs-5 { font-size: clamp(1rem, 1.25vw, 1.125rem); }

/* The Lobster brand wordmark — was #e63946, an off-brand red. */
.stylish-font {
    font-family: 'Lobster', cursive;
    font-size: clamp(1.35rem, 2.6vw, 2rem);
    color: var(--nx-navy);
    line-height: 1.2;
}

/* Eyebrow labels above section headings. The markup already writes these as
   `text-uppercase text-secondary`, so red stays as a deliberate accent here. */
.text-uppercase.text-secondary {
    letter-spacing: 0.16em;
    font-weight: 700;
    font-size: 0.82rem !important;
}

/* ==========================================================================
   4 · BOOTSTRAP UTILITY REPAIRS
   These have to carry !important because the compiled utilities do.
   ========================================================================== */
.bg-light        { background-color: var(--nx-mist) !important; }
.bg-secondary    { background-color: var(--nx-red) !important; }
.bg-primary      { background-color: var(--nx-navy) !important; }
.bg-dark         { background-color: var(--nx-navy-900) !important; }
.bg-white        { background-color: var(--nx-white) !important; }

.text-primary    { color: var(--nx-navy) !important; }
.text-secondary  { color: var(--nx-red) !important; }
.text-dark       { color: var(--nx-ink) !important; }
.text-light      { color: var(--nx-mist) !important; }
.text-muted      { color: var(--nx-muted) !important; }
.text-body       { color: var(--nx-body) !important; }

.border-primary   { border-color: var(--nx-navy) !important; }
.border-secondary { border-color: var(--nx-red) !important; }

/* On a light surface, "light" text would be invisible. The old red --bs-light
   accidentally hid this; now it has to be handled honestly. */
.bg-light .text-light,
.bg-white .text-light { color: var(--nx-body) !important; }

/* Rounded corners: the scss set $border-radius to 0, which is what makes the
   current build look dated. */
.rounded    { border-radius: var(--nx-radius) !important; }
.rounded-sm { border-radius: var(--nx-radius-sm) !important; }
.rounded-lg { border-radius: var(--nx-radius-lg) !important; }

/* ==========================================================================
   5 · BUTTONS
   ========================================================================== */
.btn {
    --nx-btn-bg: var(--nx-navy);
    --nx-btn-fg: #fff;
    font-family: 'Roboto', sans-serif;
    font-weight: 600;
    letter-spacing: 0.01em;
    border-radius: var(--nx-radius-pill);
    padding: 0.75rem 1.6rem;
    position: relative;
    overflow: hidden;
    isolation: isolate;
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease),
                color var(--nx-fast) linear,
                background-color var(--nx-base) var(--nx-ease);
}

/* Wipe fill on hover, instead of a flat colour swap. */
.btn::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--nx-btn-hover, var(--nx-navy-800));
    transform: translateY(101%);
    transition: transform var(--nx-base) var(--nx-ease-out);
}
.btn:hover::after { transform: translateY(0); }

.btn:hover {
    transform: translateY(-2px);
    box-shadow: var(--nx-shadow-2);
}
.btn:active { transform: translateY(0); }

.btn-primary,
.btn.btn-primary {
    background-color: var(--nx-navy);
    border-color: var(--nx-navy);
    color: #fff;
    --nx-btn-hover: var(--nx-navy-800);
}
.btn-primary:hover,
.btn.btn-primary:hover {
    background-color: var(--nx-navy);
    border-color: var(--nx-navy-800);
    color: #fff;
}

/* Red is the single call-to-action colour. */
.btn-secondary,
.btn.btn-secondary {
    background-color: var(--nx-red);
    border-color: var(--nx-red);
    color: #fff;
    --nx-btn-hover: var(--nx-red-700);
}
.btn-secondary:hover,
.btn.btn-secondary:hover {
    background-color: var(--nx-red);
    border-color: var(--nx-red-700);
    color: #fff;
}

.btn-outline-primary {
    border: 2px solid var(--nx-navy);
    color: var(--nx-navy);
    background: transparent;
    --nx-btn-hover: var(--nx-navy);
}
.btn-outline-primary:hover { color: #fff; border-color: var(--nx-navy); background: transparent; }

.btn-outline-light {
    border: 2px solid rgba(255, 255, 255, 0.75);
    color: #fff;
    background: transparent;
    --nx-btn-hover: #fff;
}
.btn-outline-light:hover { color: var(--nx-navy); background: transparent; }

/* Square icon buttons must stay circular and must not grow a wipe. */
.btn-square, .btn-sm-square, .btn-md-square, .btn-lg-square, .btn-xl-square {
    border-radius: 50%;
    padding: 0;
    overflow: visible;
}
.btn-square::after, .btn-sm-square::after, .btn-md-square::after,
.btn-lg-square::after, .btn-xl-square::after { display: none; }

/* Arrow nudge on any button whose label ends in an icon. */
.btn i { transition: transform var(--nx-base) var(--nx-ease); }
.btn:hover i.fa-arrow-right,
.btn:hover i.fa-angle-right,
.btn:hover i.bi-arrow-right { transform: translateX(4px); }

/* The old "Join Us" counter button: was #fa0808 red with a GREY hover. */
.counter-btn {
    background-color: var(--nx-red);
    color: #fff;
    border: none;
    padding: 0.85rem 2rem;
    font-family: 'Roboto', sans-serif;
    font-weight: 700;
    font-size: 1rem;
    border-radius: var(--nx-radius-pill);
    cursor: pointer;
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease),
                background-color var(--nx-base) var(--nx-ease);
}
.counter-btn:hover {
    background-color: var(--nx-red-700);
    transform: translateY(-3px);
    box-shadow: var(--nx-shadow-2);
}

/* ==========================================================================
   6 · TOPBAR + NAVBAR
   The navbar carried `navbar-dark bg-light`, i.e. white text on the accidental
   red. It becomes a white bar with navy links and a red underline on hover.
   ========================================================================== */
.topbar {
    background: var(--nx-navy) !important;
    padding: 0.5rem 1rem;
    font-size: 0.9rem;
}
.topbar,
.topbar a,
.topbar i,
.topbar small,
.topbar span { color: rgba(255, 255, 255, 0.92) !important; }
.topbar a:hover { color: #fff !important; }

.navbar.bg-light,
.navbar.navbar-dark.bg-light {
    background-color: rgba(255, 255, 255, 0.96) !important;
    backdrop-filter: saturate(180%) blur(10px);
    border-bottom: 1px solid var(--nx-line);
    box-shadow: 0 1px 0 rgba(11, 42, 91, 0.04);
    padding-top: 0.65rem !important;
    padding-bottom: 0.65rem !important;
    transition: padding var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease);
}

/* Compact once the page is scrolled (js adds .nx-stuck). */
.navbar.nx-stuck {
    padding-top: 0.3rem !important;
    padding-bottom: 0.3rem !important;
    box-shadow: var(--nx-shadow-2);
}
.navbar.nx-stuck .navbar-brand img { height: clamp(46px, 5vw, 58px) !important; }

.navbar .navbar-nav .nav-link,
.navbar .navbar-nav .nav-item.nav-link {
    color: var(--nx-ink) !important;
    font-family: 'Roboto', sans-serif;
    font-weight: 500;
    font-size: 1rem;
    padding: 0.5rem 0.15rem !important;
    margin: 0 0.6rem;
    position: relative;
}

/* Kill the old ::before/::after box effect from style.css and use a rule. */
.navbar .navbar-nav .nav-item::before,
.navbar .navbar-nav .nav-item::after { display: none !important; }

.navbar .navbar-nav .nav-link::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -2px;
    height: 2px;
    background: var(--nx-red);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--nx-base) var(--nx-ease-out);
}
.navbar .navbar-nav .nav-link:hover::after,
.navbar .navbar-nav .nav-link.active::after { transform: scaleX(1); }

.navbar .navbar-nav .nav-link:hover,
.navbar .navbar-nav .nav-link.active {
    color: var(--nx-navy) !important;
}

.navbar-toggler {
    border: 1px solid var(--nx-line);
    border-radius: var(--nx-radius-sm);
    padding: 0.45rem 0.6rem;
}
.navbar-toggler:focus { box-shadow: 0 0 0 3px var(--nx-navy-a12); }
/* navbar-dark ships a white burger icon, invisible on the new white bar. */
.navbar-dark .navbar-toggler-icon {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%230B2A5B' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* ==========================================================================
   7 · SECTION RHYTHM + HEADINGS
   ========================================================================== */
.container-fluid.py-5,
section.py-5,
div.py-5 {
    padding-top: var(--nx-section-y) !important;
    padding-bottom: var(--nx-section-y) !important;
}
.container.py-5 {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
}

/* Accent rule under centred section headings. */
.text-center.mx-auto.pb-5 { padding-bottom: clamp(2rem, 4vw, 3.25rem) !important; }

.text-center.mx-auto > .text-uppercase.text-secondary { margin-bottom: 0.5rem !important; }

.text-center.mx-auto > h2::after,
.text-center.mx-auto > .display-4::after,
.text-center.mx-auto > .display-5::after {
    content: "";
    display: block;
    width: 68px;
    height: 3px;
    margin: 1.1rem auto 0;
    border-radius: 2px;
    background: linear-gradient(90deg, var(--nx-red), var(--nx-navy));
    transform: scaleX(0);
    transform-origin: center;
    transition: transform var(--nx-slow) var(--nx-ease-out) 0.15s;
}
.text-center.mx-auto.nx-in > h2::after,
.text-center.mx-auto.nx-in > .display-4::after,
.text-center.mx-auto.nx-in > .display-5::after { transform: scaleX(1); }

/* ==========================================================================
   8 · CARD COMPONENTS
   One treatment shared by every *-item block, which is what makes the pages
   read as one system: service (20), project (34), feature (6), team (4),
   counter (12), mancap (4).
   ========================================================================== */
.service-item,
.project-item,
.feature-item,
.team-item,
.blog-item,
.counter-item,
.mancap-img-card,
.depot-item {
    background: var(--nx-white);
    border: 1px solid var(--nx-line);
    border-radius: var(--nx-radius);
    overflow: hidden;
    height: 100%;
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease),
                border-color var(--nx-base) var(--nx-ease);
}

.service-item:hover,
.project-item:hover,
.feature-item:hover,
.team-item:hover,
.blog-item:hover,
.mancap-img-card:hover,
.depot-item:hover {
    transform: translateY(-6px);
    box-shadow: var(--nx-shadow-3);
    border-color: transparent;
}

/* Image wrappers: crop, then zoom the image inside on hover. */
.service-img,
.project-img,
.feature-img,
.team-img,
.blog-img {
    position: relative;
    overflow: hidden;
    background: var(--nx-mist);
}

.service-img img,
.project-img img,
.feature-img img,
.team-img img,
.blog-img img {
    width: 100%;
    display: block;
    transition: transform var(--nx-slow) var(--nx-ease);
    will-change: transform;
}

.service-item:hover .service-img img,
.project-item:hover .project-img img,
.feature-item:hover .feature-img img,
.team-item:hover .team-img img,
.blog-item:hover .blog-img img { transform: scale(1.07); }

/* A red hairline that draws across the top of a card on hover. */
.service-item,
.project-item,
.feature-item { position: relative; }

.service-item::before,
.project-item::before,
.feature-item::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: var(--nx-red);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--nx-base) var(--nx-ease-out);
    z-index: 2;
}
.service-item:hover::before,
.project-item:hover::before,
.feature-item:hover::before { transform: scaleX(1); }

.service-content,
.project-content,
.about-item-content { padding: clamp(1.1rem, 2vw, 1.6rem); }

.service-content h3,
.service-content h4,
.project-content h3,
.project-content h4 { transition: color var(--nx-base) var(--nx-ease); }

.service-item:hover .service-content h3,
.service-item:hover .service-content h4,
.project-item:hover .project-content h3,
.project-item:hover .project-content h4 { color: var(--nx-navy); }

/* Counters */
.counter-item { text-align: center; padding: clamp(1.25rem, 2.5vw, 2rem); }
.counter-item-inner { border-radius: var(--nx-radius-sm); }
.counter-item .h1,
.counter-item h1,
.counter-item [data-toggle="counter-up"] {
    color: var(--nx-navy);
    font-family: "Playfair Display", serif;
    font-weight: 700;
}
.counter-item:hover { transform: translateY(-4px); }

/* Team icon strip */
.team-icon a,
.team-icon .btn { transition: transform var(--nx-base) var(--nx-ease), background-color var(--nx-base) var(--nx-ease); }
.team-icon a:hover { transform: translateY(-3px); }

/* ==========================================================================
   9 · VIDEO + GALLERY
   61 mp4s carry this site, so the play affordance matters.
   ========================================================================== */
.video-thumbnail {
    position: relative;
    overflow: hidden;
    border-radius: var(--nx-radius);
    background: var(--nx-navy-900);
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease), box-shadow var(--nx-base) var(--nx-ease);
}
.video-thumbnail:hover {
    transform: translateY(-5px);
    box-shadow: var(--nx-shadow-3);
}
.video-thumbnail video {
    display: block;
    width: 100%;
    border-radius: inherit;
    object-fit: cover;
}

.play-button {
    cursor: pointer;
    width: 66px;
    height: 66px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(224, 27, 27, 0.94);
    box-shadow: 0 10px 30px rgba(224, 27, 27, 0.35);
    transition: transform var(--nx-base) var(--nx-ease-out), background-color var(--nx-base) var(--nx-ease);
}
.play-button i { color: #fff; font-size: 1.35rem; margin-left: 3px; }
.video-thumbnail:hover .play-button { transform: translate(-50%, -50%) scale(1.12); }
.play-button:hover { background: var(--nx-red-700); }

/* Soft pulse ring, so an unplayed video reads as interactive. */
.play-button::before {
    content: "";
    position: absolute;
    inset: -10px;
    border-radius: 50%;
    border: 2px solid rgba(224, 27, 27, 0.5);
    animation: nx-pulse 2.4s var(--nx-ease) infinite;
}
@keyframes nx-pulse {
    0%   { transform: scale(0.9); opacity: 0.8; }
    70%  { transform: scale(1.25); opacity: 0; }
    100% { transform: scale(1.25); opacity: 0; }
}

/* MANCAP award cards */
.mancap-img-card { border: none; }
.mancap-img-card:hover { transform: translateY(-8px) scale(1.02); }

/* ==========================================================================
   10 · FORMS
   The old inline CSS styled bare `form`, `label`, `input`, `select` and
   `input[type=submit]` globally — so the join-us modal's rules leaked onto the
   contact form site-wide (including `form { display: flex }`). Everything is
   scoped to its component here.
   ========================================================================== */
.nx-form,
#myModal form {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
}

.form-control,
.form-select,
#myModal input,
#myModal select,
.nx-form input,
.nx-form select,
.nx-form textarea {
    border: 1.5px solid var(--nx-line);
    border-radius: var(--nx-radius-sm);
    padding: 0.8rem 1rem;
    font-size: 1rem;
    font-family: 'Roboto', sans-serif;
    color: var(--nx-ink);
    background: var(--nx-white);
    transition: border-color var(--nx-fast) var(--nx-ease), box-shadow var(--nx-fast) var(--nx-ease);
}

.form-control:focus,
.form-select:focus,
#myModal input:focus,
#myModal select:focus,
.nx-form input:focus,
.nx-form textarea:focus {
    border-color: var(--nx-navy);
    box-shadow: 0 0 0 4px var(--nx-navy-a12);
    outline: none;
}

.form-label,
#myModal label,
.nx-form label {
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--nx-ink);
    margin-bottom: 0.35rem;
}

/* Submit was #4CAF50 green — off-brand. */
#myModal input[type="submit"],
.nx-form input[type="submit"],
.nx-form button[type="submit"] {
    background-color: var(--nx-red);
    color: #fff;
    border: none;
    border-radius: var(--nx-radius-pill);
    padding: 0.85rem 1.5rem;
    font-weight: 700;
    cursor: pointer;
    margin-top: 0.75rem;
    transition: background-color var(--nx-base) var(--nx-ease), transform var(--nx-base) var(--nx-ease), box-shadow var(--nx-base) var(--nx-ease);
}
#myModal input[type="submit"]:hover,
.nx-form input[type="submit"]:hover,
.nx-form button[type="submit"]:hover {
    background-color: var(--nx-red-700);
    transform: translateY(-2px);
    box-shadow: var(--nx-shadow-2);
}

.input-icon { position: relative; }
.input-icon i {
    position: absolute;
    left: 0.95rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--nx-muted);
    pointer-events: none;
    transition: color var(--nx-fast) var(--nx-ease);
    z-index: 2;
}
.input-icon input,
.input-icon select { padding-left: 2.6rem !important; width: 100%; }
.input-icon:focus-within i { color: var(--nx-navy); }

/* ==========================================================================
   11 · MODAL (join-us)
   ========================================================================== */
#myModal.modal {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 9999;
    overflow: auto;
    background: rgba(6, 24, 49, 0.55);
    backdrop-filter: blur(4px);
    animation: nx-fade var(--nx-base) var(--nx-ease);
}
#myModal .modal-content {
    background: var(--nx-white);
    border-radius: var(--nx-radius-lg);
    box-shadow: var(--nx-shadow-3);
    padding: clamp(1.5rem, 3vw, 2.25rem);
    max-width: 520px;
    width: calc(100% - 2rem);
    margin: clamp(2rem, 8vh, 6rem) auto;
    animation: nx-slide-in var(--nx-slow) var(--nx-ease-out);
}
#myModal .close {
    color: var(--nx-muted);
    float: right;
    font-size: 1.9rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    transition: color var(--nx-fast) var(--nx-ease), transform var(--nx-fast) var(--nx-ease);
}
#myModal .close:hover { color: var(--nx-red); transform: rotate(90deg); }

@keyframes nx-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes nx-slide-in {
    from { transform: translateY(-24px); opacity: 0; }
    to   { transform: translateY(0);     opacity: 1; }
}

/* ==========================================================================
   12 · FLOATING ACTIONS
   ========================================================================== */
.whatsapp-button {
    position: fixed;
    bottom: 22px;
    right: 22px;
    width: 58px;
    height: 58px;
    display: grid;
    place-items: center;
    background-color: #25D366;   /* WhatsApp's own brand green, left alone */
    border: none;
    border-radius: 50%;
    box-shadow: 0 8px 24px rgba(37, 211, 102, 0.4);
    z-index: 1000;
    cursor: pointer;
    transition: transform var(--nx-base) var(--nx-ease-out), box-shadow var(--nx-base) var(--nx-ease);
}
.whatsapp-button:hover {
    transform: translateY(-4px) scale(1.06);
    box-shadow: 0 14px 34px rgba(37, 211, 102, 0.5);
    background-color: #1DA851;
}
.whatsapp-button img { width: 30px; height: 30px; }

.back-to-top {
    right: 22px;
    bottom: 92px;              /* clears the WhatsApp button */
    width: 46px;
    height: 46px;
    display: grid !important;
    place-items: center;
    border-radius: 50%;
    background: var(--nx-navy);
    color: #fff;
    box-shadow: var(--nx-shadow-2);
    transition: transform var(--nx-base) var(--nx-ease), background-color var(--nx-base) var(--nx-ease);
}
.back-to-top:hover { background: var(--nx-navy-800); transform: translateY(-3px); }

/* ==========================================================================
   13 · FOOTER
   ========================================================================== */
.footer,
.container-fluid.footer {
    background: var(--nx-navy-900) !important;
    color: rgba(255, 255, 255, 0.72);
}
.footer h1, .footer h2, .footer h3, .footer h4, .footer h5,
.footer .h4, .footer .h5 { color: #fff; }
.footer a { color: rgba(255, 255, 255, 0.72); transition: color var(--nx-fast) var(--nx-ease), padding-left var(--nx-fast) var(--nx-ease); }
.footer a:hover { color: #fff; }
.footer-item a:hover { padding-left: 5px; }
.footer .btn-square,
.footer .btn-sm-square,
.footer .btn-md-square {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.18);
}
.footer .btn-square:hover,
.footer .btn-sm-square:hover,
.footer .btn-md-square:hover { background: var(--nx-red); border-color: var(--nx-red); }
.copyright { background: var(--nx-navy-900); border-top: 1px solid rgba(255, 255, 255, 0.1); }
.copyright, .copyright a { color: rgba(255, 255, 255, 0.6); }

/* ==========================================================================
   14 · HERO
   The old markup used a homegrown slider whose wrapper class was `.carousel` —
   the same name Bootstrap uses for its own component — with slides hard-cut by
   inline `display:none/block` from JS, a fixed 20px caption and a translucent
   grey box behind the text. It is now `.nx-hero`/`.nx-slide`: crossfaded, with
   a slow Ken Burns push on the active slide, a navy scrim for legibility and
   fluid type.
   ========================================================================== */
.nx-hero {
    position: relative;
    overflow: hidden;
    background: var(--nx-navy-900);
    min-height: clamp(460px, 74vh, 820px);
    isolation: isolate;
}

.nx-slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity 1.1s var(--nx-ease), visibility 0s linear 1.1s;
}
.nx-slide.is-active {
    opacity: 1;
    visibility: visible;
    z-index: 1;
    transition: opacity 1.1s var(--nx-ease), visibility 0s linear 0s;
}

.nx-slide img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
}

/* Slow push on whichever slide is showing. */
.nx-slide.is-active img { animation: nx-kenburns 12s var(--nx-ease) both; }
@keyframes nx-kenburns {
    from { transform: scale(1.02); }
    to   { transform: scale(1.12); }
}

/* Navy scrim — a gradient, so the photo stays visible at the top while the
   copy sits on solid contrast at the bottom.

   It has to live on .nx-slide, NOT on .nx-hero: the active slide is
   positioned with a z-index, which makes it its own stacking context, so a
   scrim on the hero would paint above the entire slide — including the copy
   nested inside it — and wash the headline out. Inside the slide, the three
   layers order correctly: image 0, scrim 1, copy 2. */
.nx-slide::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background:
        linear-gradient(to bottom, rgba(6, 24, 49, 0.34) 0%, rgba(6, 24, 49, 0.12) 38%, rgba(6, 24, 49, 0.78) 100%),
        linear-gradient(to right, rgba(6, 24, 49, 0.45), rgba(6, 24, 49, 0) 62%);
}

.nx-slide img { position: relative; z-index: 0; }

.nx-hero-copy {
    position: absolute;
    z-index: 2;
    left: 0;
    right: 0;
    bottom: 0;
    padding: clamp(1.5rem, 5vw, 4.5rem) var(--nx-gutter);
    max-width: 1180px;
    margin: 0 auto;
    text-align: left;
}

.nx-hero-copy h1 {
    color: #fff !important;
    font-size: clamp(1.75rem, 4.6vw, 3.6rem);
    margin: 0 0 0.75rem;
    max-width: 21ch;
    text-shadow: 0 2px 24px rgba(6, 24, 49, 0.5);
}

.nx-hero-copy p {
    color: rgba(255, 255, 255, 0.92);
    font-size: clamp(0.95rem, 1.5vw, 1.1rem);
    max-width: 62ch;
    margin: 0 0 0.65rem;
    text-shadow: 0 1px 12px rgba(6, 24, 49, 0.55);
}
.nx-hero-copy p:last-of-type { margin-bottom: 1.4rem; }

/* Staggered entrance for the copy of the active slide. */
.nx-slide.is-active .nx-hero-copy > * {
    animation: nx-rise 0.9s var(--nx-ease-out) both;
}
.nx-slide.is-active .nx-hero-copy > *:nth-child(1) { animation-delay: 0.15s; }
.nx-slide.is-active .nx-hero-copy > *:nth-child(2) { animation-delay: 0.28s; }
.nx-slide.is-active .nx-hero-copy > *:nth-child(3) { animation-delay: 0.4s; }
.nx-slide.is-active .nx-hero-copy > *:nth-child(4) { animation-delay: 0.52s; }
@keyframes nx-rise {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: none; }
}

/* Hero CTA — the markup's `<button class="button">`, which was a raw red box. */
.nx-hero-copy .button,
.nx-hero-copy .nx-cta {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0;
    padding: 0.85rem 1.9rem;
    background: var(--nx-red);
    color: #fff;
    border: none;
    border-radius: var(--nx-radius-pill);
    font-family: 'Roboto', sans-serif;
    font-weight: 700;
    font-size: 1rem;
    cursor: pointer;
    box-shadow: 0 10px 30px rgba(224, 27, 27, 0.35);
    transition: transform var(--nx-base) var(--nx-ease),
                background-color var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease);
}
.nx-hero-copy .button:hover,
.nx-hero-copy .nx-cta:hover {
    background: var(--nx-red-700);
    transform: translateY(-3px);
    box-shadow: 0 16px 40px rgba(224, 27, 27, 0.45);
}

/* Slide dots */
.nx-hero-dots {
    position: absolute;
    z-index: 4;
    bottom: 1.1rem;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 0.5rem;
}
.nx-hero-dots button {
    width: 9px;
    height: 9px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.45);
    cursor: pointer;
    transition: width var(--nx-base) var(--nx-ease), background-color var(--nx-base) var(--nx-ease);
}
.nx-hero-dots button.is-active {
    width: 26px;
    border-radius: var(--nx-radius-pill);
    background: var(--nx-red);
}

/* Desktop / mobile art direction, kept from the original markup. */
@media (max-width: 768px) {
    .nx-slide img.desktop, img.desktop { display: none; }
    .nx-slide img.mobile,  img.mobile  { display: block; }
}
@media (min-width: 769px) {
    .nx-slide img.desktop, img.desktop { display: block; }
    .nx-slide img.mobile,  img.mobile  { display: none; }
}

/* ==========================================================================
   15 · ANIMATION LAYER
   Two systems, deliberately:
   (a) The 119 existing `.wow` elements keep working — animate.css is only
       retuned below, because its stock fadeInUp travels 100% of the element
       height, which reads as cheap. 28px is enough.
   (b) `data-nx-reveal` is the new IntersectionObserver layer, used for
       anything WOW does not already cover. It degrades to visible if JS fails.
   ========================================================================== */

/* (a) Retune animate.css */
.animated {
    animation-duration: 0.9s !important;
    animation-timing-function: var(--nx-ease-out) !important;
    animation-fill-mode: both;
}
@keyframes fadeInUp {
    from { opacity: 0; transform: translate3d(0, 28px, 0); }
    to   { opacity: 1; transform: none; }
}
@keyframes fadeInDown {
    from { opacity: 0; transform: translate3d(0, -24px, 0); }
    to   { opacity: 1; transform: none; }
}
@keyframes fadeInLeft {
    from { opacity: 0; transform: translate3d(-30px, 0, 0); }
    to   { opacity: 1; transform: none; }
}
@keyframes fadeInRight {
    from { opacity: 0; transform: translate3d(30px, 0, 0); }
    to   { opacity: 1; transform: none; }
}

/* (b) Reveal layer */
[data-nx-reveal] {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.75s var(--nx-ease-out), transform 0.75s var(--nx-ease-out);
    transition-delay: calc(var(--nx-d, 0) * 90ms);
}
[data-nx-reveal="left"]  { transform: translateX(-28px); }
[data-nx-reveal="right"] { transform: translateX(28px); }
[data-nx-reveal="scale"] { transform: scale(0.94); }
[data-nx-reveal="fade"]  { transform: none; }

[data-nx-reveal].nx-in {
    opacity: 1;
    transform: none;
}

/* If JS never runs, nothing stays hidden. */
.no-js [data-nx-reveal] { opacity: 1; transform: none; }

/* Count-up figures shouldn't jump layout as digits change. */
[data-toggle="counter-up"] { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   16 · PAGE HEADER / BREADCRUMB
   ========================================================================== */
.breadcrumb-item,
.breadcrumb-item a { color: rgba(255, 255, 255, 0.8); }
.breadcrumb-item a:hover { color: #fff; }
.breadcrumb-item.active { color: #fff; }
.breadcrumb-item + .breadcrumb-item::before { color: rgba(255, 255, 255, 0.5); }

/* ==========================================================================
   17 · MISC PAGE PIECES
   ========================================================================== */
/* about.html image blocks — the old rule set these to 50%/50% then 25%/25% on
   mobile, which made them tiny. */
.about-item-image-content { width: 100%; height: auto; }
.about-item-image-content img {
    width: 100%;
    height: auto;
    object-fit: cover;
    border-radius: var(--nx-radius);
}

/* Depot cards sat on the accidental red via bg-light p-4 rounded. */
.bg-light.p-4.rounded {
    background: var(--nx-white) !important;
    border: 1px solid var(--nx-line);
    border-radius: var(--nx-radius) !important;
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease), box-shadow var(--nx-base) var(--nx-ease);
}
.bg-light.p-4.rounded:hover {
    transform: translateY(-4px);
    box-shadow: var(--nx-shadow-2);
}

/* Owl carousel controls, in brand. */
.owl-carousel .owl-nav button.owl-prev,
.owl-carousel .owl-nav button.owl-next {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--nx-white) !important;
    color: var(--nx-navy) !important;
    border: 1px solid var(--nx-line) !important;
    box-shadow: var(--nx-shadow-1);
    transition: background-color var(--nx-base) var(--nx-ease), color var(--nx-base) var(--nx-ease), transform var(--nx-base) var(--nx-ease);
}
.owl-carousel .owl-nav button:hover {
    background: var(--nx-red) !important;
    color: #fff !important;
    transform: translateY(-2px);
}
.owl-dots .owl-dot span { background: var(--nx-line) !important; transition: background-color var(--nx-base) var(--nx-ease), width var(--nx-base) var(--nx-ease); }
.owl-dots .owl-dot.active span { background: var(--nx-red) !important; }

/* Spinner */
#spinner { background: var(--nx-white); }
#spinner .spinner-border { color: var(--nx-navy) !important; }

/* ==========================================================================
   18 · RESPONSIVE
   The original had six media queries in total for the whole site.
   ========================================================================== */

/* Tablet and below */
@media (max-width: 991.98px) {
    .navbar.bg-light,
    .navbar.navbar-dark.bg-light {
        padding: 0.5rem 1rem !important;
    }

    /* Mobile menu panel: a real surface, not links floating on the page. */
    .navbar-collapse {
        background: var(--nx-white);
        border: 1px solid var(--nx-line);
        border-radius: var(--nx-radius);
        box-shadow: var(--nx-shadow-2);
        margin-top: 0.75rem;
        padding: 0.5rem;
        max-height: 78vh;
        overflow-y: auto;
    }

    .navbar .navbar-nav .nav-link,
    .navbar .navbar-nav .nav-item.nav-link {
        padding: 0.85rem 0.9rem !important;   /* ≥44px tap target */
        margin: 0;
        border-radius: var(--nx-radius-sm);
        border-bottom: 1px solid var(--nx-line);
    }
    .navbar .navbar-nav .nav-item:last-child .nav-link { border-bottom: none; }
    .navbar .navbar-nav .nav-link:hover { background: var(--nx-mist); }
    /* An underline that slides is meaningless in a stacked menu. */
    .navbar .navbar-nav .nav-link::after { display: none; }
    .navbar .navbar-nav .nav-link.active {
        background: var(--nx-mist);
        color: var(--nx-navy) !important;
        border-left: 3px solid var(--nx-red);
    }

    .nx-hero { min-height: clamp(420px, 62vh, 560px); }
    .nx-hero-copy { text-align: center; padding-bottom: 3.25rem; }
    .nx-hero-copy h1 { max-width: none; }
    .nx-hero-copy p { max-width: none; margin-left: auto; margin-right: auto; }

    .text-center.mx-auto.pb-5 { padding-bottom: 2rem !important; }
}

/* Phones */
@media (max-width: 575.98px) {
    :root { --nx-section-y: 2.75rem; }

    .nx-hero { min-height: 66vh; }
    .nx-hero-copy p:not(:first-of-type) { display: none; }  /* one line of copy is enough on a phone */

    /* Anything the pages set as a big display size needs a floor on mobile. */
    .display-1, .display-2, .display-3 { font-size: clamp(1.8rem, 8vw, 2.4rem) !important; }

    .btn, .counter-btn { padding: 0.75rem 1.35rem; min-height: 44px; }

    .service-content, .project-content { padding: 1.1rem; }

    /* Vertical 9:16 gallery clips: one per row reads far better than four. */
    .video-thumbnail video { border-radius: var(--nx-radius); }

    .whatsapp-button { width: 52px; height: 52px; bottom: 16px; right: 16px; }
    .whatsapp-button img { width: 26px; height: 26px; }
    .back-to-top { right: 16px; bottom: 80px; width: 42px; height: 42px; }

    /* Long words / URLs in cards must not push the layout sideways. */
    .service-content, .project-content, .footer, .about-item-content { overflow-wrap: anywhere; }
}

/* Wide screens: stop the content from stretching thin. */
@media (min-width: 1400px) {
    .container { max-width: 1240px; }
}

/* ==========================================================================
   19 · MOTION + PRINT PREFERENCES
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
    [data-nx-reveal] { opacity: 1 !important; transform: none !important; }
    .nx-slide.is-active img { animation: none; }
    .play-button::before { animation: none; }
}

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

@media print {
    .navbar, .topbar, .whatsapp-button, .back-to-top, #spinner, .nx-hero-dots { display: none !important; }
    .nx-hero { min-height: auto; }
    [data-nx-reveal] { opacity: 1 !important; transform: none !important; }
}

/* ==========================================================================
   20 · GALLERY  (ported out of gallery.html's inline <style>)
   The original used a flex wrap with `max-width: 400px` per item and a global
   `video { width:100%; height:100% }`. Rebuilt as a real grid so the 9:16
   clips and 16:9 clips can each sit in a sane column, and so one clip per row
   on a phone instead of four cramped ones.
   ========================================================================== */
.gallery-section { margin-bottom: clamp(2.5rem, 5vw, 4rem); }

.gallery-section h2 {
    text-align: center;
    margin-bottom: clamp(1.25rem, 3vw, 2rem);
    color: var(--nx-ink);          /* was #b30909 — yet another red */
}

.gallery-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: clamp(0.85rem, 1.8vw, 1.4rem);
    justify-content: center;
    align-items: start;
}

.gallery-grid video,
.gallery-grid img {
    width: 100%;
    height: auto;
    max-width: none;
    display: block;
    border-radius: var(--nx-radius);
    background: var(--nx-navy-900);
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease);
}
.gallery-grid video:hover,
.gallery-grid img:hover {
    transform: translateY(-4px);
    box-shadow: var(--nx-shadow-3);
}

/* Portrait clips (the vt and hori sets) get a narrower cell so they are not
   letterboxed into a wide box. */
.gallery-grid.is-portrait { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }

@media (max-width: 575.98px) {
    .gallery-grid { grid-template-columns: 1fr; }
    .gallery-grid.is-portrait { grid-template-columns: repeat(2, 1fr); }
}

/* ==========================================================================
   21 · SMALL HELPERS used by the page markup
   ========================================================================== */
.nx-tagline {
    font-size: 1.05rem;
    color: var(--nx-navy);
    font-weight: 500;
    letter-spacing: 0.01em;
}

/* Eyebrow + emphasis on the dark awards band. The three award cards each had
   a different accent (gold, green, red) with small bold text on near-black
   navy — under 4.5:1 in every case. Unified to a light tint that passes. */
.nx-eyebrow-light {
    font-family: 'Roboto', sans-serif;
    font-size: 0.85rem;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.82);
}

/* The toggler markup is <span class="fa fa-bars">, not Bootstrap's
   .navbar-toggler-icon, so it inherits colour from .navbar-dark — which is
   near-white and would vanish on the new white bar. */
.navbar .navbar-toggler,
.navbar-dark .navbar-toggler,
.navbar .navbar-toggler .fa-bars {
    color: var(--nx-navy) !important;
    font-size: 1.35rem;
    line-height: 1;
}

/* Brand lockup has to share the bar with the toggler on a narrow phone. */
@media (max-width: 575.98px) {
    .navbar-brand img { height: 48px !important; }
    .navbar-brand .ms-3 { margin-left: 0.6rem !important; }
    .stylish-font { font-size: 1.2rem; }
    .nx-tagline { font-size: 0.82rem; }
}

/* ==========================================================================
   22 · --bs-* VARIABLE OVERRIDES
   css/style.css refers to var(--bs-primary) / var(--bs-secondary) /
   var(--bs-light) / var(--bs-dark) in dozens of rules (the topbar background,
   the toggler, nav hovers, footer accents). Those DO follow the custom
   properties, so retargeting them here fixes every one of those rules at
   once. Bootstrap's own compiled bg- and text- utilities do not read these —
   they are baked literals, handled in section 4.
   ========================================================================== */
:root {
    --bs-primary:   #0B2A5B;
    --bs-secondary: #E01B1B;
    --bs-light:     #F5F7FA;
    --bs-dark:      #1A1D21;
    --bs-white:     #FFFFFF;
    --bs-body-color: #4B5563;
}

/* The menu button: style.css gives it var(--bs-secondary) as a background and
   var(--bs-dark) as its colour — dark ink on red, which is unreadable. Made a
   deliberate red brand button with white bars, matching the Join Us CTA. */
.navbar .navbar-toggler,
.navbar-dark .navbar-toggler {
    background: var(--nx-red) !important;
    border: none !important;
    border-radius: var(--nx-radius-sm);
    padding: 0.55rem 0.85rem !important;
    box-shadow: 0 6px 18px rgba(224, 27, 27, 0.3);
}
.navbar .navbar-toggler,
.navbar-dark .navbar-toggler,
.navbar .navbar-toggler .fa-bars,
.navbar .navbar-toggler span {
    color: #fff !important;
}
.navbar .navbar-toggler:focus { box-shadow: 0 0 0 4px rgba(224, 27, 27, 0.3); }

/* Phone hero: the art in these slides is busy through the middle, where the
   bottom-weighted scrim is at its lightest, so the headline lands on low
   contrast. Deepen it when the copy is centred over the image. */
@media (max-width: 767.98px) {
    .nx-slide::after {
        background: linear-gradient(to bottom,
            rgba(6, 24, 49, 0.5) 0%,
            rgba(6, 24, 49, 0.42) 28%,
            rgba(6, 24, 49, 0.88) 100%);
    }
}

/* Phone hero, part two: art direction, not just contrast.
   The mobile slide images (3p.jpeg and friends) already contain the Norlux
   wordmark and a paint-splash through the middle, so overlaying our headline
   on top stacks type on type and reads as clutter however dark the scrim is.
   Below 768px the copy drops onto its own navy panel at the foot of the hero:
   the photograph stays clean above it, and legibility no longer depends on
   what happens to be in the picture. */
@media (max-width: 767.98px) {
    .nx-slide::after {
        background: linear-gradient(to bottom,
            rgba(6, 24, 49, 0.28) 0%,
            rgba(6, 24, 49, 0.10) 30%,
            rgba(6, 24, 49, 0.55) 100%);
    }

    .nx-hero-copy {
        top: auto;
        bottom: 0;
        padding: 3.5rem 1.15rem 3rem;
        background: linear-gradient(to top,
            rgba(6, 24, 49, 0.97) 0%,
            rgba(6, 24, 49, 0.93) 55%,
            rgba(6, 24, 49, 0.55) 82%,
            rgba(6, 24, 49, 0) 100%);
    }

    .nx-hero-copy h1 {
        font-size: clamp(1.5rem, 6.2vw, 2rem);
        margin-bottom: 0.6rem;
        text-shadow: none;          /* the panel does the work now */
    }
    .nx-hero-copy p {
        font-size: 0.95rem;
        line-height: 1.6;
        text-shadow: none;
        margin-bottom: 1rem;
    }
    .nx-hero-copy .button,
    .nx-hero-copy .nx-cta { padding: 0.8rem 1.6rem; }

    .nx-hero-dots { bottom: 0.55rem; }
}

/* ==========================================================================
   23 · CARD INTERNALS
   The service/project cards hold copy of differing length, so their buttons
   landed at different heights across a row and the grid read as ragged. Make
   each card a column and push the action to the foot.
   ========================================================================== */
.service-item,
.project-item,
.feature-item,
.team-item,
.blog-item {
    display: flex;
    flex-direction: column;
}
.service-content,
.project-content {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
}
/* Whichever element ends the card body becomes the anchored action. */
.service-content > .btn:last-child,
.service-content > a.btn,
.project-content > .btn:last-child,
.service-content > p:last-child + .btn { margin-top: auto; }

/* Give the buttons a consistent width so a row of cards lines up. */
.service-content .btn,
.project-content .btn { align-self: center; }

/* A flex child will not shrink below its content's intrinsic width, which is
   what pushed a column 8px past the viewport on two pages. min-width:0 is the
   standard release valve. */
.row > [class*="col-"] { min-width: 0; max-width: 100%; }

/* ==========================================================================
   24 · FIXES FOUND IN TESTING
   ========================================================================== */

/* REGRESSION: section 5 gives .btn `position: relative` so the hover wipe has
   something to anchor to. .back-to-top is also a .btn, and this file loads
   after style.css — where its `position: fixed` lives — so equal specificity
   plus later source order silently demoted it to relative and the floating
   button dropped into the footer flow. Restated here, after .btn. */
.back-to-top {
    position: fixed !important;
    z-index: 99;
}

/* Horizontal reveal offsets are fine on a desktop grid but on a narrow screen
   a ±28px translate on a full-width column pushes the document 28px wider than
   the viewport (measured at 504px). Sideways entrances are the wrong gesture
   on a phone anyway — everything rises instead. */
@media (max-width: 767.98px) {
    [data-nx-reveal="left"],
    [data-nx-reveal="right"] { transform: translateY(22px); }
}

/* ==========================================================================
   25 · CONTACT MAPS
   The two Google Maps iframes carry inline `height: 1000px; aspect-ratio: 9/16`
   — a portrait map two-thirds of a laptop screen tall, twice over. Behind each
   one, style.css paints two 70%x70% offset slabs (::before navy, ::after red)
   as a picture-frame effect, which at that size becomes a wall of flat colour
   rather than a frame. Beating an inline style needs !important.
   ========================================================================== */
.contact .contact-map {
    padding: 0;
    border-radius: var(--nx-radius);
    overflow: hidden;
    border: 1px solid var(--nx-line);
    box-shadow: var(--nx-shadow-2);
    transition: box-shadow var(--nx-base) var(--nx-ease), transform var(--nx-base) var(--nx-ease);
}
.contact .contact-map:hover {
    transform: translateY(-4px);
    box-shadow: var(--nx-shadow-3);
}
.contact .contact-map::before,
.contact .contact-map::after { display: none; }

.contact .contact-map iframe {
    height: clamp(260px, 32vw, 420px) !important;
    aspect-ratio: auto !important;
    width: 100% !important;
    display: block;
    border: 0;
}

/* The three address cards use `border border-secondary`, i.e. a full red
   outline each. Kept as a red top rule instead — the accent survives, the page
   stops looking boxed-in. */
.contact .border.border-secondary {
    border-color: var(--nx-line) !important;
    border-top: 3px solid var(--nx-red) !important;
    border-radius: var(--nx-radius);
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease), box-shadow var(--nx-base) var(--nx-ease);
}
.contact .border.border-secondary:hover {
    transform: translateY(-4px);
    box-shadow: var(--nx-shadow-2);
}

/* ==========================================================================
   26 · SIGNATURE LOOK
   The first pass was too quiet — correct colours, but a visitor who never saw
   the old site would not register a redesign. These are the loud, above-the-
   fold moves: a dark glass header on every page, a red scroll-progress rail, a
   capability marquee, a hero trust strip, and cards that actually respond.
   ========================================================================== */

/* --- 26.1 Dark glass header ------------------------------------------------
   The single most visible change: it sits on every page, above the fold,
   permanently. It was a white bar; now near-black navy with blur, white links
   and a red rule. The logo carries a white ring, so it reads well on dark. */
.container-fluid.topbar {
    background: var(--nx-navy-900) !important;
    border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

.navbar.bg-light,
.navbar.navbar-dark.bg-light {
    background: rgba(6, 24, 49, 0.86) !important;
    backdrop-filter: saturate(160%) blur(14px);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
    border-bottom: 1px solid rgba(255, 255, 255, 0.09);
    box-shadow: none;
}
.navbar.nx-stuck {
    background: rgba(6, 24, 49, 0.97) !important;
    box-shadow: 0 10px 34px rgba(0, 0, 0, 0.35);
}

.navbar .navbar-nav .nav-link,
.navbar .navbar-nav .nav-item.nav-link {
    color: rgba(255, 255, 255, 0.86) !important;
    font-weight: 500;
}
.navbar .navbar-nav .nav-link:hover,
.navbar .navbar-nav .nav-link.active {
    color: #fff !important;
}
.navbar .navbar-nav .nav-link::after {
    background: var(--nx-red);
    height: 3px;
    bottom: -4px;
}

/* Brand lockup on dark */
.navbar .stylish-font { color: #fff; }
.navbar .nx-tagline   { color: rgba(255, 255, 255, 0.62); }

/* The search button was navy on navy — invisible against the new bar. */
.navbar .btn.btn-primary {
    background: rgba(255, 255, 255, 0.1) !important;
    border: 1px solid rgba(255, 255, 255, 0.22) !important;
    color: #fff !important;
}
.navbar .btn.btn-primary:hover {
    background: var(--nx-red) !important;
    border-color: var(--nx-red) !important;
}

/* Mobile panel has to follow the bar onto dark, or it opens as a white slab. */
@media (max-width: 991.98px) {
    .navbar-collapse {
        background: rgba(6, 24, 49, 0.98);
        border: 1px solid rgba(255, 255, 255, 0.12);
        box-shadow: 0 20px 50px rgba(0, 0, 0, 0.45);
    }
    .navbar .navbar-nav .nav-link,
    .navbar .navbar-nav .nav-item.nav-link {
        border-bottom: 1px solid rgba(255, 255, 255, 0.09);
    }
    .navbar .navbar-nav .nav-link:hover { background: rgba(255, 255, 255, 0.07); }
    .navbar .navbar-nav .nav-link.active {
        background: rgba(224, 27, 27, 0.16);
        border-left: 3px solid var(--nx-red);
        color: #fff !important;
    }
}

/* --- 26.2 Scroll progress rail --------------------------------------------
   A 3px red bar tracking read position. Small, but it is live movement in the
   chrome of the page — people register it immediately. */
.nx-progress {
    position: fixed;
    top: 0;
    left: 0;
    height: 3px;
    width: 100%;
    transform: scaleX(0);
    transform-origin: left;
    background: linear-gradient(90deg, var(--nx-red), #ff6a3d);
    z-index: 2000;
    pointer-events: none;
    will-change: transform;
}

/* --- 26.3 Capability marquee ----------------------------------------------
   A continuously scrolling strip of what the company actually does, directly
   under the header. Unmistakably new, and it says what Norlux sells. */
.nx-marquee {
    background: var(--nx-navy);
    overflow: hidden;
    position: relative;
    padding: 0.85rem 0;
    border-bottom: 3px solid var(--nx-red);
}
.nx-marquee__track {
    display: flex;
    width: max-content;
    animation: nx-marquee 34s linear infinite;
}
.nx-marquee:hover .nx-marquee__track { animation-play-state: paused; }
.nx-marquee__group {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}
.nx-marquee span {
    font-family: 'Roboto', sans-serif;
    font-weight: 700;
    font-size: clamp(0.75rem, 1.3vw, 0.92rem);
    text-transform: uppercase;
    letter-spacing: 0.18em;
    padding: 0 1.5rem;
    white-space: nowrap;
    color: rgba(255, 255, 255, 0.92);
}
.nx-marquee b {
    color: var(--nx-red);
    font-size: 0.7rem;
    flex: 0 0 auto;
    line-height: 1;
}
@keyframes nx-marquee {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
    .nx-marquee__track { animation: none; }
}

/* --- 26.4 Hero trust strip ------------------------------------------------
   A card straddling the hero's bottom edge. It gives the fold a focal point
   the old full-bleed photo never had, and puts the credentials where they are
   actually read. */
.nx-trust {
    position: relative;
    z-index: 20;
    margin: -3.5rem auto 0;
    max-width: 1140px;
    width: calc(100% - 2rem);
    background: #fff;
    border-radius: var(--nx-radius-lg);
    box-shadow: var(--nx-shadow-3);
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    overflow: hidden;
    border: 1px solid var(--nx-line);
}
.nx-trust__item {
    padding: clamp(1rem, 2.2vw, 1.6rem) 0.9rem;
    text-align: center;
    border-right: 1px solid var(--nx-line);
    transition: background-color var(--nx-base) var(--nx-ease);
}
.nx-trust__item:last-child { border-right: none; }
.nx-trust__item:hover { background: var(--nx-mist); }
.nx-trust__k {
    display: block;
    font-family: "Playfair Display", serif;
    font-weight: 700;
    font-size: clamp(1.25rem, 2.5vw, 1.95rem);
    color: var(--nx-navy);
    line-height: 1.1;
}
.nx-trust__v {
    display: block;
    margin-top: 0.3rem;
    font-size: clamp(0.66rem, 1.05vw, 0.76rem);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-weight: 700;
    color: var(--nx-muted);
}
.nx-trust__k i { color: var(--nx-red); margin-right: 0.3rem; font-size: 0.85em; }

@media (max-width: 767.98px) {
    .nx-trust {
        grid-template-columns: repeat(2, 1fr);
        margin-top: -1.75rem;
    }
    .nx-trust__item:nth-child(2n) { border-right: none; }
    .nx-trust__item:nth-child(-n+2) { border-bottom: 1px solid var(--nx-line); }
}

/* --- 26.5 Cards that actually respond -------------------------------------
   The service cards were flat white boxes with a colour change on hover. The
   whole card now fills navy from the bottom, the copy inverts and the icon
   kicks. */
.service-item {
    position: relative;
    isolation: isolate;
}
.service-item::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--nx-navy);
    transform: translateY(100%);
    transition: transform var(--nx-base) var(--nx-ease-out);
}
.service-item:hover::after { transform: translateY(0); }

.service-item:hover .service-content h3,
.service-item:hover .service-content h4,
.service-item:hover .service-content h5,
.service-item:hover .service-content a:not(.btn) { color: #fff !important; }
.service-item:hover .service-content p { color: rgba(255, 255, 255, 0.82) !important; }
.service-item:hover .service-img i,
.service-item:hover .service-content i { color: #fff !important; }

.service-item .fa-2x,
.service-item .fa-3x,
.service-item .service-img {
    transition: transform var(--nx-base) var(--nx-ease), color var(--nx-base) var(--nx-ease);
}
.service-item:hover .fa-2x,
.service-item:hover .fa-3x { transform: scale(1.12) rotate(-6deg); }

/* Keep the button legible once the card behind it turns navy. */
.service-item:hover .btn.btn-secondary {
    background: #fff !important;
    color: var(--nx-navy) !important;
    border-color: #fff !important;
}

/* --- 26.6 Counters on navy ------------------------------------------------ */
.container-fluid.counter {
    background: var(--nx-navy-900) !important;
    position: relative;
    overflow: hidden;
}
.container-fluid.counter::before {
    content: "";
    position: absolute;
    width: 520px;
    height: 520px;
    right: -160px;
    top: -220px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(224, 27, 27, 0.28), transparent 68%);
    pointer-events: none;
}
/* BUG FIX — white on white.
   style.css:614 paints .counter-item-inner with var(--bs-white), so the four
   figures sit on a WHITE card. An earlier version of this block forced every
   descendant of .counter-item to #fff for the dark band, which made the card's
   own text invisible: "Completed Projects / 20567+" disappeared entirely.

   The band stays navy; the cards stay white and keep dark text. White cards on
   a navy field read better than translucent ones anyway. */
.container-fluid.counter .counter-item {
    background: transparent;
    border: none;
}

.container-fluid.counter .counter-item .counter-item-inner {
    background: #fff;
    border-radius: var(--nx-radius);
    /* was a hard 20px offset in rgba(255,94,21,1) — an orange nothing else on
       the site uses. */
    box-shadow: 0 18px 44px rgba(0, 0, 0, 0.32);
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease);
}
.container-fluid.counter .counter-item:hover .counter-item-inner {
    transform: translateY(-6px);
    box-shadow: 0 26px 60px rgba(0, 0, 0, 0.42);
}

/* The offset slab behind each card, kept as the red accent. */
.container-fluid.counter .counter-item .counter-item-style {
    background: var(--nx-red);
    border-radius: var(--nx-radius);
}

/* Explicit, readable colours on the white card — no blanket override. */
.container-fluid.counter .counter-item-inner h4 { color: var(--nx-ink) !important; }
.container-fluid.counter .counter-item-inner i,
.container-fluid.counter .counter-item-inner .text-secondary,
.container-fluid.counter .counter-item-inner [data-toggle="counter-up"],
.container-fluid.counter .counter-item-inner .h1 { color: var(--nx-red) !important; }

/* --- 26.7 Section heading kicker ------------------------------------------
   An oversized red rule to the left of centred section headings, so sections
   announce themselves instead of blending together. */
.text-center.mx-auto > .text-uppercase.text-secondary::before,
.text-center.mx-auto > .text-uppercase.text-secondary::after {
    content: "";
    display: inline-block;
    width: 26px;
    height: 2px;
    background: var(--nx-red);
    vertical-align: middle;
    margin: 0 0.6rem;
}

/* style.css:154 sets the active/hover link to var(--bs-secondary) — now red —
   with .sticky-top .navbar .navbar-nav .nav-link.active !important, four
   classes deep. Red on the new near-black bar is about 3:1, so it has to be
   beaten at equal specificity rather than left to a shorter selector. */
.sticky-top .navbar .navbar-nav .nav-link:hover,
.sticky-top .navbar .navbar-nav .nav-link.active,
.navbar .navbar-nav .nav-link:hover,
.navbar .navbar-nav .nav-link.active {
    color: #fff !important;
}

/* The fourth trust cell ("Awka + depots statewide") is the longest label and
   was clipping in a half-width cell on a phone: tightened tracking and allowed
   it to wrap onto two lines. */
@media (max-width: 575.98px) {
    .nx-trust__v {
        letter-spacing: 0.07em;
        font-size: 0.62rem;
        line-height: 1.35;
        overflow-wrap: anywhere;
    }
    .nx-trust__item { padding-left: 0.5rem; padding-right: 0.5rem; }
}

/* ==========================================================================
   27 · RED TEXT CONTRAST
   Measured in-browser: the brand red #E01B1B as TEXT on white comes out at
   2.90:1 — under the 4.5:1 AA minimum for body text and under even the 3:1
   large-text allowance. .text-secondary is used 84 times across the site,
   mostly as small uppercase eyebrow labels on white, so this was a real
   legibility problem rather than a technicality.

   Text gets the darker red; buttons, fills and rules keep the logo red, where
   contrast is carried by white type on a red field. */
.text-secondary,
.container-fluid.counter .counter-item-inner i,
.container-fluid.counter .counter-item-inner .text-secondary,
.container-fluid.counter .counter-item-inner [data-toggle="counter-up"],
.container-fluid.counter .counter-item-inner .h1 {
    color: var(--nx-red-700) !important;
}

/* On dark grounds the darker red would go the wrong way — keep those light. */
.bg-primary .text-secondary,
.bg-dark .text-secondary,
.footer .text-secondary,
.container-fluid.counter .text-secondary:not(.counter-item-inner *) {
    color: #fff !important;
}

/* ==========================================================================
   28 · TEXT ON DARK PANELS — two real contrast failures, measured
   ========================================================================== */

/* 28.1 The counter figures.
   .counter-counting carries a navy strip at the foot of each white card
   (style.css, via var(--bs-primary)), so the numbers were never on white:
   the markup's .text-secondary red sat on navy at 1.84:1. They are white. */
.counter-counting,
.counter-counting *,
.counter-item-inner .counter-counting [data-toggle="counter-up"],
.counter-item-inner .counter-counting .h1,
.container-fluid.counter .counter-item-inner .counter-counting * {
    color: #fff !important;
}

/* 28.2 Video card captions — a regression from section 9.
   .video-thumbnail was given a near-black background so a portrait clip
   letterboxes cleanly. On .project-item cards the caption sits inside that
   same box, so the heading kept its dark ink on near-black: 1.05:1, which is
   invisible. The card stays dark (it suits video); the text goes light. */
.project-item.video-thumbnail .project-content h1,
.project-item.video-thumbnail .project-content h2,
.project-item.video-thumbnail .project-content h3,
.project-item.video-thumbnail .project-content h4,
.project-item.video-thumbnail .project-content h5,
.project-item.video-thumbnail .project-content h6,
.video-thumbnail .project-content h4,
.video-thumbnail .project-content h5 {
    color: #fff !important;
}
.project-item.video-thumbnail .project-content p,
.video-thumbnail .project-content p,
.video-thumbnail .project-content small {
    color: rgba(255, 255, 255, 0.82) !important;
}
.video-thumbnail .project-content a:not(.btn) { color: #fff !important; }

/* The card already lifts on hover; keep the title from turning navy-on-navy
   via the section 8 hover rule. */
.project-item.video-thumbnail:hover .project-content h3,
.project-item.video-thumbnail:hover .project-content h4 { color: #fff !important; }

/* ==========================================================================
   29 · .text-white ON LIGHT SURFACES
   Measured: project.html had six paragraphs at 1.00:1 — literally white on
   white. The markup writes <p class="text-white"> inside .service-item, which
   was readable only because the card used to be dark before this redesign
   turned card surfaces white. Rather than edit the class out of the markup on
   every page, .text-white is neutralised wherever it lands on a light ground.
   ========================================================================== */
.service-item .text-white,
.feature-item .text-white,
.team-item .text-white,
.blog-item .text-white,
.project-item:not(.video-thumbnail) .text-white,
.bg-white .text-white,
.bg-light .text-white,
.counter-item-inner .text-white {
    color: var(--nx-body) !important;
}

/* Headings keep more weight than body copy. */
.service-item h1.text-white, .service-item h2.text-white,
.service-item h3.text-white, .service-item h4.text-white,
.service-item h5.text-white,
.feature-item h4.text-white, .feature-item h5.text-white,
.bg-light h4.text-white, .bg-light h5.text-white {
    color: var(--nx-ink) !important;
}

/* Re-assert the hover inversion from section 26.5: this block is later in the
   file, so without restating it the rule above would win on hover too and the
   copy would go dark just as the card turns navy. */
.service-item:hover .service-content .text-white,
.service-item:hover .service-content p,
.service-item:hover .service-content p.text-white { color: rgba(255, 255, 255, 0.85) !important; }
.service-item:hover .service-content h3.text-white,
.service-item:hover .service-content h4.text-white,
.service-item:hover .service-content h5.text-white,
.service-item:hover .service-content a { color: #fff !important; }

/* Without JS the hero would be blank: .nx-slide is hidden until JS adds
   .is-active. The no-js class is on <html> and removed by norlux-ui.js, so
   this shows the first slide for anyone whose JS never runs. */
.no-js .nx-hero .nx-slide:first-of-type {
    opacity: 1 !important;
    visibility: visible !important;
    z-index: 1;
}
.no-js .nx-hero-dots { display: none; }

/* ==========================================================================
   30 · GALLERY PAGE
   The page held four grids all sized for landscape cells, so the 15 portrait
   clips (720x1280) towered over the 13 landscape ones (1280x720) and nothing
   lined up. Each orientation now gets its own cell size and a locked aspect
   ratio, so every row is even regardless of what the source file is.

   With preload="none" the browser paints the poster, and object-fit applies to
   a poster exactly as it does to a frame — so these crop cleanly.
   ========================================================================== */
.gallery-section {
    margin-bottom: clamp(2.5rem, 5vw, 4.5rem);
    padding-top: clamp(1.5rem, 3vw, 2.5rem);
}

.gallery-eyebrow {
    text-transform: uppercase;
    letter-spacing: 0.16em;
    font-weight: 700;
    font-size: 0.82rem;
    color: var(--nx-red-700);
    text-align: center;
    margin: 0 0 0.35rem;
}
.gallery-section h2 {
    text-align: center;
    margin: 0 0 0.75rem;
    color: var(--nx-ink);
}
.gallery-lead {
    text-align: center;
    max-width: 60ch;
    margin: 0 auto clamp(1.5rem, 3vw, 2.25rem);
    color: var(--nx-body);
}
.gallery-subhead {
    text-align: center;
    font-size: clamp(1.1rem, 2vw, 1.45rem);
    color: var(--nx-navy);
    margin: 0 0 clamp(1.25rem, 2.5vw, 1.75rem);
    position: relative;
}
.gallery-subhead::after {
    content: "";
    display: block;
    width: 54px;
    height: 3px;
    border-radius: 2px;
    margin: 0.7rem auto 0;
    background: linear-gradient(90deg, var(--nx-red), var(--nx-navy));
}

/* Portrait clips — narrower cells, 9:16 locked. */
.gallery-grid.is-portrait {
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.gallery-grid.is-portrait video {
    aspect-ratio: 9 / 16;
    object-fit: cover;
    height: auto;
}

/* Landscape clips — wider cells, 16:9 locked. */
.gallery-grid.is-landscape {
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
.gallery-grid.is-landscape video {
    aspect-ratio: 16 / 9;
    object-fit: cover;
    height: auto;
}

/* Photographs — one consistent tile shape so the grid reads as a grid. */
.gallery-grid img {
    aspect-ratio: 4 / 3;
    object-fit: cover;
    height: auto;
}

/* The native controls bar is the only affordance on a poster, so keep the
   frame obviously interactive. */
.gallery-grid video {
    background: var(--nx-navy-900);
    cursor: pointer;
}

@media (max-width: 575.98px) {
    /* Two portrait clips still read well side by side on a phone; landscape
       and photos go full width. */
    .gallery-grid.is-portrait { grid-template-columns: repeat(2, 1fr); }
    .gallery-grid.is-landscape,
    .gallery-grid:not(.is-portrait) { grid-template-columns: 1fr; }
}

/* ==========================================================================
   31 · PROJECT VIDEO CARDS — aspect lock
   With preload="none" a <video> has no intrinsic size until metadata loads,
   so the browser sizes it from the poster. One rotated clip therefore rendered
   its card 562px tall against its neighbours' 248px and broke the row. The
   cards are a fixed 16:9 regardless of what the poster turns out to be.
   ========================================================================== */
.project-item.video-thumbnail video,
.project-item.video-thumbnail .img-fluid {
    aspect-ratio: 16 / 9;
    object-fit: cover;
    width: 100%;
    height: auto;
    display: block;
}

/* ==========================================================================
   32 · LIGHTBOX
   Tapping a gallery tile opens it large over a dark backdrop. The pages used
   to link lib/lightbox, but that library was never in the project — both its
   CSS and JS were 404s on every page load — so this is written here instead.
   It handles video as well as photographs, which lightbox2 does not.

   The tiles only become clickable once JS has wired them up, and JS is also
   what removes `controls` from the grid videos; if it never runs, the tiles
   keep their native controls and still play inline. Enhancement, not
   dependency.
   ========================================================================== */

/* --- tile affordances ----------------------------------------------------- */
.nx-zoomable {
    cursor: zoom-in;
    position: relative;
}

.gallery-grid .nx-tile {
    position: relative;
    display: block;
    border-radius: var(--nx-radius);
    overflow: hidden;
    background: var(--nx-navy-900);
    box-shadow: var(--nx-shadow-1);
    transition: transform var(--nx-base) var(--nx-ease),
                box-shadow var(--nx-base) var(--nx-ease);
}
.gallery-grid .nx-tile:hover {
    transform: translateY(-4px);
    box-shadow: var(--nx-shadow-3);
}
.gallery-grid .nx-tile:focus-visible {
    outline: 3px solid var(--nx-red);
    outline-offset: 3px;
}

/* Play badge on video tiles — with controls removed, this is the only cue
   that the tile is a clip. */
.nx-tile__play {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 54px;
    height: 54px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: rgba(224, 27, 27, 0.92);
    box-shadow: 0 10px 28px rgba(0, 0, 0, 0.45);
    pointer-events: none;
    transition: transform var(--nx-base) var(--nx-ease-out),
                background-color var(--nx-base) var(--nx-ease);
    z-index: 2;
}
.nx-tile__play::before {
    content: "";
    width: 0;
    height: 0;
    margin-left: 4px;
    border-left: 16px solid #fff;
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
}
.nx-tile:hover .nx-tile__play {
    transform: translate(-50%, -50%) scale(1.12);
    background: var(--nx-red);
}

/* A small expand cue on photographs. */
.nx-tile__zoom {
    position: absolute;
    right: 10px;
    bottom: 10px;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: rgba(6, 24, 49, 0.72);
    backdrop-filter: blur(4px);
    display: grid;
    place-items: center;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity var(--nx-base) var(--nx-ease), transform var(--nx-base) var(--nx-ease);
    pointer-events: none;
    z-index: 2;
}
.nx-tile__zoom::before {
    content: "";
    width: 13px;
    height: 13px;
    border: 2px solid #fff;
    border-radius: 50%;
    box-shadow: 7px 7px 0 -5px #fff;
}
.nx-tile:hover .nx-tile__zoom { opacity: 1; transform: translateY(0); }

/* --- the overlay ---------------------------------------------------------- */
.nx-lb {
    position: fixed;
    inset: 0;
    z-index: 4000;
    display: grid;
    place-items: center;
    background: rgba(4, 14, 30, 0.93);
    backdrop-filter: blur(10px);
    padding: clamp(0.75rem, 3vw, 2.5rem);
    animation: nx-lb-in 0.22s var(--nx-ease-out);
}
@keyframes nx-lb-in { from { opacity: 0; } to { opacity: 1; } }

.nx-lb__stage {
    position: relative;
    max-width: 100%;
    max-height: 100%;
    display: grid;
    place-items: center;
}

.nx-lb__stage img,
.nx-lb__stage video {
    display: block;
    max-width: min(94vw, 1600px);
    max-height: 86vh;
    width: auto;
    height: auto;
    border-radius: var(--nx-radius);
    box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
    background: #000;
    animation: nx-lb-pop 0.28s var(--nx-ease-out);
}
@keyframes nx-lb-pop {
    from { opacity: 0; transform: scale(0.96); }
    to   { opacity: 1; transform: none; }
}

/* Controls */
.nx-lb__close,
.nx-lb__nav {
    position: fixed;
    border: none;
    cursor: pointer;
    color: #fff;
    background: rgba(255, 255, 255, 0.12);
    border: 1px solid rgba(255, 255, 255, 0.22);
    backdrop-filter: blur(6px);
    display: grid;
    place-items: center;
    border-radius: 50%;
    transition: background-color var(--nx-fast) var(--nx-ease), transform var(--nx-fast) var(--nx-ease);
    z-index: 2;
    padding: 0;
    line-height: 1;
}
.nx-lb__close:hover,
.nx-lb__nav:hover { background: var(--nx-red); border-color: var(--nx-red); transform: scale(1.06); }
.nx-lb__close:focus-visible,
.nx-lb__nav:focus-visible { outline: 3px solid var(--nx-red); outline-offset: 3px; }

.nx-lb__close {
    top: clamp(0.75rem, 2vw, 1.5rem);
    right: clamp(0.75rem, 2vw, 1.5rem);
    width: 46px;
    height: 46px;
    font-size: 1.6rem;
}
.nx-lb__nav {
    top: 50%;
    transform: translateY(-50%);
    width: 52px;
    height: 52px;
    font-size: 2rem;
}
.nx-lb__nav:hover { transform: translateY(-50%) scale(1.06); }
.nx-lb__prev { left: clamp(0.5rem, 2vw, 1.75rem); }
.nx-lb__next { right: clamp(0.5rem, 2vw, 1.75rem); }

.nx-lb__count {
    position: fixed;
    bottom: clamp(0.75rem, 2vw, 1.5rem);
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    background: rgba(6, 24, 49, 0.6);
    border: 1px solid rgba(255, 255, 255, 0.14);
    padding: 0.4rem 0.9rem;
    border-radius: var(--nx-radius-pill);
    z-index: 2;
}

/* Stop the page behind from scrolling while the overlay is up. */
body.nx-lb-open { overflow: hidden; }

@media (max-width: 575.98px) {
    .nx-lb__nav { width: 44px; height: 44px; font-size: 1.6rem; }
    .nx-lb__stage img,
    .nx-lb__stage video { max-height: 78vh; max-width: 96vw; }
    .nx-tile__play { width: 44px; height: 44px; }
    .nx-tile__play::before { border-left-width: 13px; border-top-width: 8px; border-bottom-width: 8px; }
}

@media (prefers-reduced-motion: reduce) {
    .nx-lb, .nx-lb__stage img, .nx-lb__stage video { animation: none; }
}

/* Once a tile is wrapped, the wrapper owns the frame and the hover lift —
   otherwise the media inside lifts too and the two transforms compound. */
.gallery-grid .nx-tile video,
.gallery-grid .nx-tile img {
    border-radius: 0;
    box-shadow: none;
}
.gallery-grid .nx-tile video:hover,
.gallery-grid .nx-tile img:hover {
    transform: none;
    box-shadow: none;
}
