/* ═══════════════════════════════════════════════════════════════════════════
   Site-wide refinement layer — loaded last on every page.
   Same palette (navy / blue / cyan) and fonts (Orbitron, Exo 2) as styles.css;
   this file only refines spacing, type, depth, motion and accessibility, and
   fixes a few layout bugs shared between pages.
   1. Tokens and base   2. Navigation   3. Heroes   4. Section headings
   5. Cards   6. Home: services, projects, contact   7. Breadcrumbs
   8. Inner pages   9. Motion and accessibility   10. Motion layer (GSAP)
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Tokens and base ────────────────────────────────────────────────────── */
:root {
    --nj-radius: 16px;
    --nj-line: rgba(93, 173, 226, 0.22);
    /* Lift shadow for hovered cards: tinted to the navy page, not a blue glow */
    --nj-shadow: 0 24px 48px -26px rgba(6, 9, 26, 0.9);
    --nj-gradient: linear-gradient(90deg, var(--accent-blue), var(--accent-cyan));
    --nj-ease: cubic-bezier(0.22, 1, 0.36, 1);
}

html {
    scroll-padding-top: 90px;
    -webkit-text-size-adjust: 100%;
}

body {
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

::selection {
    background: rgba(74, 144, 226, 0.45);
    color: var(--white);
}

h1,
h2,
h3,
h4 {
    text-wrap: balance;
}

p,
li {
    text-wrap: pretty;
}

/* height: auto keeps the aspect ratio now that images carry width and height
   attributes (which reserve their space before they load) */
img {
    max-width: 100%;
    height: auto;
}

/* A visible focus ring for keyboard users, everywhere */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible,
summary:focus-visible {
    outline: 2px solid var(--accent-cyan);
    outline-offset: 3px;
    border-radius: 6px;
}

/* ── 2. Navigation ─────────────────────────────────────────────────────────── */
.navbar {
    background: rgba(26, 31, 58, 0.78);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
    backdrop-filter: saturate(160%) blur(14px);
}

.navbar.scrolled {
    background: rgba(26, 31, 58, 0.93);
    box-shadow: 0 10px 34px -14px rgba(0, 0, 0, 0.7);
}

.nav-link {
    font-size: 0.95rem;
    letter-spacing: 0.01em;
}

.nav-link::after {
    border-radius: 2px;
}

.contact-btn {
    box-shadow: 0 8px 18px -12px rgba(6, 9, 26, 0.9);
    transition: transform var(--transition-fast), box-shadow var(--transition-medium), color var(--transition-fast);
}

/* ── 3. Heroes ─────────────────────────────────────────────────────────────── */

/* Home */
.hero {
    isolation: isolate;
    min-height: 100svh;
}

.hero::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(45% 55% at 24% 42%, rgba(74, 144, 226, 0.20), transparent 70%),
        radial-gradient(34% 44% at 76% 46%, rgba(93, 173, 226, 0.16), transparent 70%);
}

.hero::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 180px;
    z-index: 0;
    pointer-events: none;
    background: linear-gradient(180deg, transparent, var(--primary-navy));
}

.hero .scroll-indicator {
    z-index: 2;
}

/* The faint grid behind the home and About heroes holds still */
.grid-overlay {
    animation: none;
}

.hero .grid-overlay {
    -webkit-mask-image: radial-gradient(ellipse 80% 70% at 50% 42%, #000 30%, transparent 80%);
    mask-image: radial-gradient(ellipse 80% 70% at 50% 42%, #000 30%, transparent 80%);
}

.hero .hero-title {
    font-size: clamp(2.2rem, 5.2vw, 4rem);
    letter-spacing: -0.01em;
}

/* Each line of the headline stays on one line */
.hero .title-line {
    text-wrap: nowrap;
}

@media (max-width: 480px) {
    .hero .title-line {
        text-wrap: balance;
    }
}

.hero .hero-subtitle {
    font-size: clamp(1.02rem, 1.5vw, 1.22rem);
    max-width: 560px;
}


/* Glow behind the floating logo */
.floating-logo::before {
    content: '';
    position: absolute;
    inset: 12%;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(74, 144, 226, 0.35), transparent 68%);
    filter: blur(18px);
    z-index: 0;
}

/* Primary buttons sit on a quiet navy shadow instead of a blue glow */
.cta-primary {
    box-shadow: 0 10px 22px -14px rgba(6, 9, 26, 0.9);
}

.cta-primary:hover {
    box-shadow: 0 14px 26px -14px rgba(6, 9, 26, 0.95);
}

/* Buttons give a little under a press. "scale" is its own property, so it
   adds to a button's hover lift or magnetic pull (transforms) instead of
   replacing them. */
.cta-primary:active,
.cta-secondary:active,
.contact-btn:active,
.submit-btn:active,
.cta-btn:active,
.btn-primary:active,
.btn-secondary:active,
.internship-btn:active,
.talent-pool-btn:active,
.subscribe-btn:active,
.category-btn:active {
    scale: 0.97;
}

.cta-secondary:hover {
    border-color: var(--accent-cyan);
    color: var(--white);
}

/* Inner-page heroes (team, careers, knowledge centre) */
.team-hero,
.careers-hero,
.knowledge-hero {
    background:
        radial-gradient(60% 90% at 50% 0%, rgba(74, 144, 226, 0.22), transparent 70%),
        linear-gradient(180deg, var(--primary-navy) 0%, var(--secondary-navy) 100%);
}

/* These heroes reuse the home page's .hero-content class, which is a
   two-column grid; that split the title and the text into broken columns. */
.careers-hero .hero-content,
.knowledge-hero .hero-content {
    display: block;
    text-align: center;
}

.careers-hero .hero-title,
.knowledge-hero .hero-title,
.team-hero-title {
    font-size: clamp(2.1rem, 5.6vw, 3.6rem);
    line-height: 1.12;
    letter-spacing: -0.01em;
}

.careers-hero .hero-subtitle,
.knowledge-hero .hero-subtitle,
.team-hero-subtitle {
    font-size: clamp(1.02rem, 1.5vw, 1.2rem);
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
}

.knowledge-hero .search-bar {
    margin-left: auto;
    margin-right: auto;
}

/* ── 4. Section headings ───────────────────────────────────────────────────── */
.section-title {
    font-size: clamp(1.8rem, 4.4vw, 3rem);
    line-height: 1.15;
}

/* Accent bar under each section title */
.section-title::after {
    content: '';
    display: block;
    width: 64px;
    height: 4px;
    margin: 1rem 0 0;
    border-radius: 2px;
    background: var(--nj-gradient);
}

.section-header .section-title::after {
    margin-left: auto;
    margin-right: auto;
}

.section-subtitle {
    font-size: clamp(1rem, 1.5vw, 1.2rem);
    max-width: 640px;
    margin-left: auto;
    margin-right: auto;
}

/* ── 5. Cards (one behaviour for every card on the site) ───────────────────── */
.service-card,
.project-card,
.team-card,
.benefit-card,
.culture-card,
.opening-card,
.internship-card,
.article-card,
.details-card,
.testimonial-card {
    border-radius: var(--nj-radius);
    transition:
        opacity 0.6s ease,
        transform 0.5s var(--nj-ease),
        border-color var(--transition-medium),
        box-shadow var(--transition-medium),
        background var(--transition-medium);
}

.service-card:hover,
.project-card:hover,
.team-card:hover,
.benefit-card:hover,
.culture-card:hover,
.opening-card:hover,
.article-card:hover {
    transform: translateY(-8px);
    border-color: rgba(93, 173, 226, 0.6);
    box-shadow: var(--nj-shadow);
}

.tag {
    border-radius: 999px;
    font-size: 0.8rem;
    letter-spacing: 0.02em;
}

/* ── 6. Home: services, projects, contact ──────────────────────────────────── */
.services-grid,
.projects-grid {
    gap: 1.5rem;
}

/* A card left alone on the last row is centred. These grids size their own
   columns (auto-fit), so the rules ask the grid how wide it is, not the
   window: each range below is where that grid has three columns or two.
   Column counts are unchanged. */
.services-grid,
.projects-grid {
    container-type: inline-size;
}

/* Services, seven cards. Three columns from 888px of grid width: 3 + 3 + 1. */
@container (min-width: 888px) {
    .services-grid > :last-child:nth-child(3n + 1) {
        grid-column: 2;
    }
}

/* Two columns below that: 2 + 2 + 2 + 1. This one asks the window as well,
   because from 768px down styles.css puts the grid in a single column whatever
   its width. */
@media (min-width: 769px) {
    @container (max-width: 887.98px) {
        .services-grid > :last-child:nth-child(odd) {
            grid-column: 1 / -1;
            justify-self: center;
            width: calc((100% - 1.5rem) / 2);
        }
    }
}

/* Projects, nine cards. Two columns between 724px and 1098px of grid width. */
@container (min-width: 724px) and (max-width: 1097.98px) {
    .projects-grid > :last-child:nth-child(odd) {
        grid-column: 1 / -1;
        justify-self: center;
        width: calc((100% - 1.5rem) / 2);
    }
}

.service-card>* {
    position: relative;
}

.service-icon {
    border-radius: 14px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
    transition: transform 0.5s var(--nj-ease);
}

.service-card:hover .service-icon {
    transform: translateY(-3px) rotate(-5deg) scale(1.05);
}

.service-title,
.project-title {
    font-size: clamp(1.15rem, 1.8vw, 1.4rem);
    line-height: 1.3;
}

/* Project cards */
.project-image {
    height: 230px;
}

/* 0.4s ease is what these have always shown. script.js used to write that into
   the page after every stylesheet, over a slower value that stood here. */
.project-placeholder {
    transition: transform 0.4s ease;
}

.project-placeholder img {
    object-fit: cover;
    opacity: 0.92;
}

.project-placeholder svg {
    max-width: 46%;
    max-height: 60%;
    opacity: 0.85;
}

.project-card:hover .project-placeholder {
    transform: scale(1.06);
}

/* The home page cards zoom a little further. This used to be set from script. */
.projects-grid .project-card:hover .project-placeholder {
    transform: scale(1.1);
}

.project-overlay {
    background: linear-gradient(180deg, rgba(26, 31, 58, 0.55), rgba(26, 31, 58, 0.92));
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
}

.project-link {
    box-shadow: 0 10px 26px -10px rgba(74, 144, 226, 0.9);
    transform: translateY(8px);
    transition: transform 0.45s var(--nj-ease);
}

.project-card:hover .project-link {
    transform: translateY(0);
}

/* The card's title is its link. Focusing it shows the same overlay as hover,
   and on touch screens the link covers the whole card. */
.project-title a {
    color: inherit;
    text-decoration: none;
}

.project-card:focus-within .project-overlay {
    opacity: 1;
}

.project-card:focus-within .project-link {
    transform: translateY(0);
}

@media (hover: none) {
    .project-card {
        position: relative;
    }

    .project-title a::after {
        content: '';
        position: absolute;
        inset: 0;
        z-index: 11;
    }
}

/* Contact */
.contact-form {
    border-radius: var(--nj-radius);
    background: linear-gradient(160deg, rgba(37, 43, 71, 0.95), rgba(26, 31, 58, 0.95));
    box-shadow: 0 30px 60px -36px rgba(6, 9, 26, 0.9);
}

.form-group input,
.form-group select,
.form-group textarea {
    border-width: 1px;
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}

.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
    border-color: var(--accent-cyan);
    box-shadow: 0 0 0 4px rgba(74, 144, 226, 0.2);
}

.method-icon {
    border-radius: 12px;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25);
}

.contact-method {
    align-items: center;
}

/* Service cards: the case studies behind a service */
.service-work {
    margin-top: 1rem;
    font-size: 0.88rem;
    line-height: 1.6;
    color: var(--text-gray);
}

.service-work a {
    color: var(--light-blue);
    text-decoration: underline;
    text-decoration-color: rgba(133, 193, 233, 0.4);
    text-underline-offset: 3px;
    transition: color var(--transition-fast), text-decoration-color var(--transition-fast);
}

.service-work a:hover,
.service-work a:focus-visible {
    color: var(--white);
    text-decoration-color: currentColor;
}

/* Concepts: systems designed and not yet built. Their status is said in
   words wherever they appear: a line on cards, a pill under a page title. */
.project-card-status {
    margin-bottom: 0.5rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--light-blue);
}

.project-status {
    display: inline-block;
    margin: 1rem 0 0.9rem;
    padding: 0.35rem 0.9rem;
    border: 1px dashed rgba(133, 193, 233, 0.6);
    border-radius: 999px;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--light-blue);
}

.concepts {
    background: var(--primary-navy);
}

.concepts-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem;
}

@media (max-width: 768px) {
    .concepts-grid {
        grid-template-columns: 1fr;
    }
}

.concept-card {
    padding: 1.5rem 1.6rem;
    border: 1px solid var(--border-gray);
    border-radius: var(--nj-radius);
    background: rgba(45, 52, 84, 0.3);
    transition: border-color var(--transition-medium), box-shadow var(--transition-medium), background var(--transition-medium);
}

.concept-card:hover {
    border-color: rgba(93, 173, 226, 0.6);
    box-shadow: var(--nj-shadow);
}

.concept-category {
    margin-bottom: 0.45rem;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--light-blue);
}

.concept-title {
    font-family: var(--font-heading);
    font-size: 1.12rem;
    line-height: 1.4;
    margin-bottom: 0.6rem;
}

.concept-title a {
    color: var(--white);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.concept-card:hover .concept-title a {
    color: var(--light-blue);
}

/* The whole card follows the title's link */
.concept-card {
    position: relative;
}

.concept-title a::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    border-radius: var(--nj-radius);
}

.concept-title a:focus-visible {
    outline: none;
}

.concept-title a:focus-visible::after {
    outline: 2px solid var(--accent-cyan);
    outline-offset: 2px;
}

.concept-summary {
    color: var(--text-gray);
    line-height: 1.7;
    margin-bottom: 1rem;
}

.concepts-cta {
    margin-top: 2rem;
    text-align: center;
    color: var(--text-gray);
}

.concepts-cta a {
    color: var(--light-blue);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.concepts-cta a:hover,
.concepts-cta a:focus-visible {
    color: var(--white);
}

/* A concept's "How It Would Work": each step named by its verb */
.design-card {
    margin-top: var(--spacing-md);
    padding: var(--spacing-md);
    background: var(--secondary-navy);
    border: 1px solid var(--border-gray);
    border-radius: var(--nj-radius);
}

.design-card h2 {
    font-family: var(--font-heading);
    font-size: 1.3rem;
    color: var(--white);
    margin-bottom: var(--spacing-sm);
}

.design-steps {
    list-style: none;
    display: grid;
    gap: 0.9rem;
}

.design-steps li {
    padding-left: 1rem;
    border-left: 2px solid rgba(93, 173, 226, 0.45);
    color: var(--text-gray);
    line-height: 1.7;
}

.design-step-title {
    display: block;
    font-family: var(--font-heading);
    font-size: 0.95rem;
    color: var(--light-blue);
}

/* Frequently asked questions (Home and Careers). Native <details>, so every
   answer is in the page for readers and crawlers, and opens without script. */
.faq {
    background: linear-gradient(180deg, var(--primary-navy) 0%, var(--secondary-navy) 100%);
}

.faq-list {
    max-width: 860px;
    margin: 0 auto;
    display: grid;
    gap: 0.85rem;
}

.faq-item {
    border: 1px solid var(--border-gray);
    border-radius: var(--nj-radius);
    background: rgba(45, 52, 84, 0.3);
    transition: border-color var(--transition-medium), background var(--transition-medium);
}

.faq-item[open] {
    border-color: rgba(93, 173, 226, 0.45);
    background: rgba(45, 52, 84, 0.45);
}

.faq-item summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.15rem 1.4rem;
    cursor: pointer;
    list-style: none;
}

.faq-item summary::-webkit-details-marker {
    display: none;
}

.faq-item summary:focus-visible {
    outline: 2px solid var(--accent-cyan);
    outline-offset: 2px;
    border-radius: var(--nj-radius);
}

/* A plus that turns into a minus */
.faq-item summary::after {
    content: '';
    flex: none;
    width: 14px;
    height: 14px;
    background:
        linear-gradient(var(--accent-cyan), var(--accent-cyan)) center / 14px 2px no-repeat,
        linear-gradient(var(--accent-cyan), var(--accent-cyan)) center / 2px 14px no-repeat;
    transition: transform var(--transition-medium);
}

.faq-item[open] summary::after {
    transform: rotate(180deg);
    background: linear-gradient(var(--accent-cyan), var(--accent-cyan)) center / 14px 2px no-repeat;
}

.faq-question {
    font-family: var(--font-heading);
    font-size: 1.02rem;
    line-height: 1.45;
    color: var(--white);
}

.faq-answer {
    padding: 0 1.4rem 1.25rem;
}

.faq-answer p {
    color: var(--text-gray);
    line-height: 1.75;
    max-width: 68ch;
}

.faq-answer a {
    color: var(--light-blue);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.faq-answer a:hover,
.faq-answer a:focus-visible {
    color: var(--white);
}

/* ── 7. Breadcrumbs (were unstyled browser-blue on several pages) ──────────── */
.breadcrumb {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
    font-size: 0.9rem;
    color: var(--text-gray);
}

.breadcrumb a {
    color: var(--text-gray);
    text-decoration: none;
    transition: color var(--transition-fast);
}

.breadcrumb a:hover {
    color: var(--accent-cyan);
}

.breadcrumb .separator {
    opacity: 0.5;
}

.breadcrumb .current {
    color: var(--accent-cyan);
    font-weight: 600;
}

/* On team, careers and the knowledge centre the breadcrumb sat in a full-height
   section above the hero, leaving an empty band between the two. */
.breadcrumb-section:has(+ .team-hero),
.breadcrumb-section:has(+ .careers-hero),
.breadcrumb-section:has(+ .knowledge-hero) {
    padding: calc(80px + 1.1rem) 0 1.1rem;
    background: var(--primary-navy);
}

.breadcrumb-section+.team-hero,
.breadcrumb-section+.careers-hero,
.breadcrumb-section+.knowledge-hero {
    margin-top: 0;
}

/* ── 8. Inner pages ────────────────────────────────────────────────────────── */

/* Team: the portrait stays beside the biography instead of floating mid-column */
@media (min-width: 969px) {
    .ceo-content {
        align-items: start;
    }

    .ceo-image {
        position: sticky;
        top: 110px;
    }
}

.ceo-image-wrapper {
    box-shadow: 0 30px 60px -30px rgba(0, 0, 0, 0.85);
    border-radius: 22px;
}

/* Footer headings keep still on hover: no underline drawing in beneath them */
.footer-section h2::after {
    content: none;
}

/* The map pin before a place (footer, Team cards), a line icon in place of
   the pin and flag emoji. Flags drew as letter pairs ("BD") on Windows. */
.nj-pin {
    width: 1em;
    height: 1em;
    margin-right: 0.35em;
    vertical-align: -0.125em;
}

/* A project or article that has no picture yet: the brand mark, faint, on a
   quiet navy panel, in place of a stock drawing on a bright gradient */
.nj-blank,
.image-placeholder.nj-blank {
    position: relative;
    background: linear-gradient(160deg, var(--border-gray), var(--secondary-navy));
}

.image-placeholder.nj-blank {
    border: 1px solid var(--nj-line);
}

.nj-blank::before {
    content: '';
    position: absolute;
    inset: 0;
    background: url('/assets/img/logo-trig.svg') center / 72px auto no-repeat;
    opacity: 0.2;
}

/* Knowledge centre and related-project thumbnails */
.article-card-image .image-placeholder,
.featured-image .image-placeholder {
    transition: transform 0.9s var(--nj-ease);
}

.article-card:hover .article-card-image .image-placeholder {
    transform: scale(1.05);
}

/* ── 9. Motion and accessibility ───────────────────────────────────────────── */

/* Skip link: off screen until it receives keyboard focus */
.skip-link {
    position: fixed;
    top: 0.75rem;
    left: 0.75rem;
    z-index: 1200;
    padding: 0.6rem 1.2rem;
    border-radius: 25px;
    border: 1px solid var(--accent-cyan);
    background: var(--secondary-navy);
    color: var(--white);
    font-weight: 600;
    text-decoration: none;
    transform: translateY(-250%);
    transition: transform var(--transition-fast);
}

.skip-link:focus {
    transform: translateY(0);
}

/* Touch areas. These controls look the same; an invisible pseudo-element
   extends what can be tapped to about 44px, or as far as the neighbouring
   control allows. */
@media (pointer: coarse), (max-width: 1024px) {

    .hamburger,
    .social-link,
    .breadcrumb a,
    .article-link,
    .opening-link {
        position: relative;
    }

    .hamburger::before,
    .social-link::before,
    .breadcrumb a::before,
    .article-link::before,
    .opening-link::before,
    .footer-link::after {
        content: '';
        position: absolute;
    }

    /* 25x19 */
    .hamburger::before {
        inset: -13px -10px;
    }

    /* 32 or 36px square, 12px apart */
    .social-link::before {
        inset: -6px;
    }

    /* Text links about 23 to 26px tall */
    .breadcrumb a::before,
    .article-link::before,
    .opening-link::before {
        inset: -11px -6px;
    }

    /* Footer rows are 0.6rem apart; each takes half of the gap on both sides */
    .footer-link::after {
        inset: -0.3rem 0;
    }
}

/* The staggered reveal sets an inline transition-delay (up to 500ms); without
   this the same delay applied to hover, so later cards reacted late. */
[data-aos]:hover {
    transition-delay: 0s !important;
}

/* If scripts are off, reveal-on-scroll content must still be readable */
@media (scripting: none) {
    [data-aos] {
        opacity: 1;
        transform: none;
    }
}

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

    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }

    [data-aos] {
        opacity: 1;
        transform: none;
    }
}

@media (max-width: 768px) {
    .project-image {
        height: 200px;
    }

    .services-grid,
    .projects-grid {
        gap: 1.1rem;
    }
}

/* ── 10. Motion layer (assets/scripts/motion.js, plan in MOTION.md) ────────────
   <html> carries "nj-motion" while the GSAP effects are active, which is never
   under reduced motion and never when the scripts are missing. Everything in
   this section only refines states those effects pass through. */

/* An element waiting for its entrance or going through it. Its own CSS
   transitions are parked until it has arrived. */
html.nj-motion .nj-pending {
    transition: none;
}

/* 10.1 Scroll reveals */

/* A block waiting for its entrance that has no hidden state of its own. The
   [data-aos] blocks have theirs in styles.css; the card grids on Team,
   Careers, Knowledge and Project get this one from motion.js, and only when
   they sit below the first screen. */
html.nj-motion .nj-wait {
    opacity: 0;
}

/* 10.2 Hero headline roll */

/* A line whose characters are rolling. It is the window they roll through,
   drawn a little taller than its box so that descenders are not cut. Each
   character rides up out of it while its twin, the same letter, follows. */
.nj-roll-window {
    clip-path: inset(-0.15em -1em);
}

.nj-roll-char {
    position: relative;
    display: inline-block;
}

.nj-roll-char::after {
    content: attr(data-char);
    position: absolute;
    left: 0;
    top: 100%;
}

/* The copy of a line that rolls whole. motion.js places it on the original. */
.nj-roll-twin {
    position: absolute;
    margin: 0;
    pointer-events: none;
}

/* 10.3 Headings and labels decode */

/* A heading while its text is resolving. The text is pinned where it sits
   (motion.js sets a matching text-indent), so letters that have settled do not
   shift while the remaining ones still change width. */
.nj-decoding {
    text-align: left;
    white-space: nowrap;
}

.nj-decoding::before,
.nj-decoding::after {
    text-indent: 0;
}

/* A label that hugs its text keeps its size instead (motion.js sets a width) */
.hero-eyebrow.nj-decoding {
    box-sizing: border-box;
    overflow: hidden;
}

/* 10.4 Orbit logo */

/* motion.js tilts the three rings into a gyroscope and owns their transform
   from then on, so the plain rotation stops. A ring turning in place cannot
   be seen anyway. What shows its orbit is a satellite travelling round it,
   and that part needs no script. */
.nj-orbit .floating-logo {
    perspective: 900px;
}

.nj-orbit .hero-visual {
    will-change: transform;
}

.nj-orbit .orbit-ring {
    animation: none;
}

.nj-orbit .orbit-ring::before {
    content: '';
    position: absolute;
    inset: -6px;
    background: radial-gradient(circle at 50% 5px, var(--accent-cyan) 0 3.5px, transparent 4.5px);
    filter: drop-shadow(0 0 6px var(--accent-cyan));
    animation: rotate 20s linear infinite;
}

.nj-orbit .ring-2::before {
    background: radial-gradient(circle at 50% 5px, var(--light-blue) 0 3.5px, transparent 4.5px);
    filter: drop-shadow(0 0 6px var(--light-blue));
    animation-duration: 25s;
    animation-direction: reverse;
}

.nj-orbit .ring-3::before {
    animation-duration: 30s;
}

/* As the hero scrolls away the assembly drifts down and the rings open up and
   fade, tied to the scroll position. Scroll-driven animations run this on the
   compositor; motion.js sets the class where they exist and does the same
   with GSAP where they do not. "translate" and "scale" are their own
   properties, so the transforms motion.js keeps on these elements (the lean
   toward the pointer, each ring's tilt) are untouched. */
@supports (animation-timeline: scroll()) {
    .nj-orbit-scroll .hero-visual {
        animation: njOrbitDrift linear both;
        animation-timeline: scroll(root);
        animation-range: 0 100svh;
    }

    .nj-orbit-scroll .orbit-ring {
        animation: njOrbitOpen linear both;
        animation-timeline: scroll(root);
        animation-range: 0 100svh;
    }
}

@keyframes njOrbitDrift {
    to {
        translate: 0 var(--nj-drift, 40px);
    }
}

@keyframes njOrbitOpen {
    to {
        scale: 1.25;
        opacity: 0.4;
    }
}

/* 10.5 Cards under the pointer */

/* motion.js drives the tilt and lift of these cards on hover (service cards,
   and the card grids on About, Team and Careers), so transform comes out of
   their transitions and their CSS hover lift steps aside; the hover colours
   keep their transitions. */
html.nj-motion .nj-tilt {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    transition:
        border-color var(--transition-medium),
        box-shadow var(--transition-medium),
        background var(--transition-medium);
}

html.nj-motion .nj-tilt:hover {
    transform: none;
}

/* The glow that follows the pointer inside a hovered card. motion.js adds the
   element and moves it. It sits on the card's background, under its content. */
.nj-tilt .nj-glow {
    position: absolute;
    top: 0;
    left: 0;
    z-index: -1;
    width: 280px;
    height: 280px;
    margin: -140px 0 0 -140px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(74, 144, 226, 0.22), transparent 70%);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

.nj-tilt:hover .nj-glow {
    will-change: transform;
}

/* An icon while its strokes are being drawn: on a layer of its own, so each
   frame repaints the small drawing and not the card around it */
.service-icon svg.nj-drawing {
    will-change: transform;
}

/* 10.6 Project images */

/* While motion.js uncovers a card's image, the picture's own hover transition
   is parked along with the card's */
html.nj-motion .nj-pending .project-placeholder {
    transition: none;
}

/* The hover overlay blurs what is behind it, also while it is invisible. Until
   the card has arrived there is nothing to hover, so the blur waits: redoing
   it on every frame of the entrance costs frames for no visible result. */
html.nj-motion .nj-pending .project-overlay {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
}

/* 10.7 About timeline */

/* The centre line draws on scroll through CSS where scroll-driven animations
   exist (about-us.css). In other browsers motion.js sets this class and
   scrubs the property, for the same line. */
.timeline.nj-line::before {
    transform: scaleY(var(--nj-line, 1));
}

/* A timeline dot before the page has reached its entry. "scale" is its own
   property, so the transform that centres the dot on the line is untouched. */
html.nj-motion .nj-dot-wait {
    scale: 0.6;
    opacity: 0.45;
}

/* The section that holds the timeline, while cards slide in from the side */
.nj-clip-x {
    overflow-x: clip;
}

/* 10.8 Magnetic buttons */

/* motion.js moves these buttons toward the pointer, hover lift included. So
   their transform leaves CSS for the time being: no transition on it, and no
   hover value of its own. Colours and shadows keep their transitions. */
html.nj-motion .nj-magnetic {
    transition:
        background var(--transition-medium),
        border-color var(--transition-medium),
        color var(--transition-fast),
        box-shadow var(--transition-medium);
}

html.nj-motion .nj-magnetic:hover {
    transform: none;
}

/* 10.9 Cursor ring */

/* A ring that trails the pointer. motion.js adds it on screens with hover and
   a fine pointer, moves it, and shows it after the first pointer movement.
   It sits beside the native cursor, which is never hidden. */
.nj-cursor {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 10001;
    width: 32px;
    height: 32px;
    margin: -16px 0 0 -16px;
    border: 1.5px solid var(--accent-cyan);
    border-radius: 50%;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    will-change: transform;
    transition: background-color var(--transition-fast), border-color var(--transition-fast);
}

/* Over a project card */
.nj-cursor.is-card {
    background-color: rgba(74, 144, 226, 0.15);
    border-color: var(--light-blue);
}

@media print {
    .nj-cursor {
        display: none;
    }
}

/* 10.10 Page transition */

/* Moving between the site's own pages: the page being left wipes upward and
   uncovers the brand gradient, then the next page wipes in over it. This is
   the browser's cross-document view transition, so there is no script. It
   starts only once the next page is ready to draw, it runs on navigations
   inside the site and never on a first visit, and a browser without it simply
   navigates as before.

   The opt-in itself (@view-transition) is a one-line <style> in each page's
   <head>, not here: the browser reads it the moment <body> starts, and this
   file may not have loaded by then. */
@media (prefers-reduced-motion: no-preference) {
    ::view-transition {
        background: linear-gradient(135deg, var(--accent-blue), var(--accent-cyan));
    }

    ::view-transition-old(root),
    ::view-transition-new(root) {
        mix-blend-mode: normal;
    }

    ::view-transition-old(root) {
        animation: njPageLeave 0.35s cubic-bezier(0.7, 0, 0.84, 0) both;
    }

    ::view-transition-new(root) {
        animation: njPageArrive 0.45s cubic-bezier(0.16, 1, 0.3, 1) 0.28s both;
    }
}

@keyframes njPageLeave {
    to {
        clip-path: inset(0 0 100% 0);
    }
}

@keyframes njPageArrive {
    from {
        clip-path: inset(100% 0 0 0);
    }
}

/* 10.11 Menu */

/* While motion.js brings the menu links in, or takes them out, their own
   transitions are parked */
.nj-menu-moving .nav-link {
    transition: none;
}
