/* ==========================================================================
   EO Robotics — editorial pattern layer

   A small set of reusable primitives in the spirit of the reference site
   (bostondynamics.com): heavy tight display type, generous air, arrow links
   that grow a disc behind them, tinted full-bleed panels, and film grain over
   dark media.

   Everything reads from tokens.css and uses the existing brand blue — no new
   colours are introduced. Loaded after the page stylesheets so these
   primitives win, but every rule is opt-in by class: nothing changes unless
   markup asks for it.
   ========================================================================== */


/* ==========================================================================
   1. GRAIN

   The site already carried this noise SVG inline in five stylesheets, each
   with its own baseFrequency and opacity. This is the single reusable
   version: add `grain` to any element with a dark or image background.

   The element needs a stacking context (position other than static). Grain
   sits above the background and below the content, and never intercepts
   clicks.
   ========================================================================== */

.grain {
    position: relative;
}

.grain::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    opacity: var(--grain-opacity, 0.16);
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 180px 180px;
}

/* Deliberately no `.grain > * { position: relative }` here: that overrides
   absolutely-positioned children — it collapsed the home hero's video
   background out of place and left the section black. Grain sits at z-index 1
   over the background; content that must sit above it carries its own
   stacking context (.hero-content already does). Static content stays under a
   16%-opacity overlay, which is the point of grain. */

/* Strength modifiers. */
.grain--subtle { --grain-opacity: 0.14; }
.grain--strong { --grain-opacity: 0.4; }

/* Over a light surface, overlay blending washes out; multiply keeps the
   texture readable without darkening the panel. */
.grain--light::after {
    mix-blend-mode: multiply;
    opacity: var(--grain-opacity, 0.12);
}

/* The service-page heroes sit on a near-black gradient (#000 → #1d1d1f), and
   the Obward specs band on --brand-ink-deep (#071a21), which is just as dark.
   Overlay blending has almost nothing to work with that dark, so the grain
   was applied but invisible. Soft-light at a much higher opacity gives the
   same texture the home hero gets from its mid-toned video. */
.service-hero.grain::after,
.products-hero.grain::after,
.ob-specs.grain::after {
    mix-blend-mode: soft-light;
    opacity: var(--grain-opacity, 0.6);
    background-size: 140px 140px;
}



/* ==========================================================================
   2. ARROW LINK

   The reference site's signature call to action: a text link whose arrow
   slides up and out while a soft disc grows from behind the first character.
   Upgrades the existing .showcase-link as well as anything marked
   .arrow-link.
   ========================================================================== */

.arrow-link,
.showcase-link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    margin-left: calc(var(--space-3) * -1);   /* keep the text optically flush */
    border-bottom: none;
    border-radius: var(--radius-pill);
    color: var(--brand-dark);
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    text-decoration: none;
    isolation: isolate;
    transition: color var(--duration-base) var(--ease);
}

/* The disc. Sized to a circle at rest, expanded to a pill on hover. */
.arrow-link::before,
.showcase-link::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    z-index: -1;
    width: 2.2em;
    height: 2.2em;
    transform: translateY(-50%);
    border-radius: var(--radius-pill);
    background: var(--brand-tint);
    transition: width var(--duration-base) var(--ease),
                background-color var(--duration-base) var(--ease);
}

.arrow-link:hover,
.showcase-link:hover {
    color: var(--brand-dark);
    border-bottom: none;
}

.arrow-link:hover::before,
.showcase-link:hover::before {
    width: 100%;
    background: var(--brand-tint-strong);
}

.arrow-link span,
.showcase-link span {
    transition: transform var(--duration-base) var(--ease);
}

.arrow-link:hover span,
.showcase-link:hover span {
    transform: translate(4px, -4px);
}

/* On dark ground the tint disappears; use the on-inverse scale instead. */
.arrow-link--inverse {
    color: var(--on-inverse);
}

.arrow-link--inverse::before {
    background: rgba(255, 255, 255, 0.14);
}

.arrow-link--inverse:hover {
    color: var(--on-inverse);
}

.arrow-link--inverse:hover::before {
    background: rgba(255, 255, 255, 0.22);
}

@media (prefers-reduced-motion: reduce) {
    .arrow-link::before,
    .showcase-link::before,
    .arrow-link span,
    .showcase-link span {
        transition: none;
    }
}


/* ==========================================================================
   2b. SWEEP BUTTON

   The reference site's primary CTA. Reading its DOM, the button itself is
   transparent and carries two pseudo-elements:

     a.fl-button          position: relative; padding: 12px 30px 12px 18px
     a.fl-button::before  48x48, border-radius pill, pinned to the left edge
     a.fl-button::after   arrow icon, pinned to the right edge
     transition           0.3s on both

   So at rest a disc sits behind the first few characters of the label; on
   hover it sweeps across to fill the whole pill while the arrow nudges
   right. The label overlapping the disc is the detail that makes it read as
   designed rather than as a plain button.

   Ours uses the brand blue rather than their palette.
   ========================================================================== */

.btn-sweep {
    position: relative;
    display: inline-flex;
    align-items: center;
    isolation: isolate;
    min-height: 48px;
    /* Left padding is less than the disc radius on purpose — the label starts
       inside the disc, exactly as the reference does. */
    padding: 0 52px 0 18px;
    border: none;
    background: none;
    color: var(--on-inverse);
    font-family: var(--font-sans);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-snug);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--duration-base) var(--ease);
}

/* The disc that sweeps. */
.btn-sweep::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    z-index: -1;
    width: 48px;
    border-radius: var(--radius-pill);
    background: var(--brand);
    transition: width var(--duration-base) var(--ease),
                background-color var(--duration-base) var(--ease);
}

/* The arrow, pinned right. Points up-right (outward) rather than straight
   across — the same cue an external / go-there link uses. */
.btn-sweep::after {
    content: '\2197';
    position: absolute;
    right: 22px;
    font-size: var(--text-base);
    line-height: 1;
    transition: transform var(--duration-base) var(--ease);
}

.btn-sweep:hover::before,
.btn-sweep:focus-visible::before {
    width: 100%;
    background: var(--brand-dark);
}

.btn-sweep:hover::after,
.btn-sweep:focus-visible::after {
    transform: translate(4px, -4px);
}

/* Secondary: a translucent disc on dark ground that fills to white, with the
   label flipping to ink so it stays legible once the pill is solid. */
.btn-sweep--ghost::before {
    background: rgba(255, 255, 255, 0.18);
    box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.55);
}

.btn-sweep--ghost:hover,
.btn-sweep--ghost:focus-visible {
    color: var(--ink);
}

.btn-sweep--ghost:hover::before,
.btn-sweep--ghost:focus-visible::before {
    background: var(--on-inverse);
    box-shadow: none;
}

/* On a light background the label has to start dark and end white. */
.btn-sweep--on-light {
    color: var(--ink);
}

.btn-sweep--on-light:hover,
.btn-sweep--on-light:focus-visible {
    color: var(--on-inverse);
}

.btn-sweep--on-light::before {
    background: var(--brand-tint-strong);
}

@media (prefers-reduced-motion: reduce) {
    .btn-sweep::before,
    .btn-sweep::after {
        transition: none;
    }
}


/* ==========================================================================
   3. TINT PANEL

   The pale full-bleed band the reference site uses to close a section — a
   statement, a line of copy, and one arrow link, on brand tint.
   ========================================================================== */

.tint-panel {
    background: var(--brand-tint);
    border-radius: var(--radius-lg);
    padding: clamp(var(--space-8), 5vw, var(--space-16));
    margin: var(--section-y-tight) 0;
}

.tint-panel-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-6) var(--space-10);
    align-items: center;
    max-width: var(--container-max);
    margin: 0 auto;
}

.tint-panel h2 {
    margin: 0 0 var(--space-3);
    font-size: var(--text-display-sm);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tighter);
    color: var(--ink);
}

.tint-panel p {
    margin: 0;
    max-width: var(--measure);
    font-size: var(--text-lg);
    line-height: var(--leading-relaxed);
    color: var(--ink-soft);
}

/* Dark variant, for closing a page rather than a section. */
.tint-panel--dark {
    background: var(--brand-ink);
    border-radius: 0;
    margin: 0;
}

.tint-panel--dark h2 { color: var(--on-inverse); }
.tint-panel--dark p  { color: var(--on-inverse-soft); }

@media (max-width: 860px) {
    .tint-panel-inner {
        grid-template-columns: 1fr;
        align-items: start;
    }
}


/* ==========================================================================
   3b. DEEP BAND

   The reference site's strongest section: a saturated blue field, lit from
   one side so it is never a flat fill, carrying heavy white display type —
   and photographs that straddle its top edge, half on the white section
   above and half on the colour. That overlap is what stops the page reading
   as a stack of separate rectangles.

   Grain runs hard here. On a large area of flat colour it is the difference
   between a printed surface and a CSS rectangle.
   ========================================================================== */

.deep-band {
    position: relative;
    /* Lit off-centre so the field has a light source, like the reference. */
    background:
        radial-gradient(120% 120% at 65% 45%,
            var(--brand-dark) 0%,
            var(--brand-darker) 45%,
            var(--brand-ink) 78%,
            var(--brand-ink-deep) 100%);
    color: var(--on-inverse);
    padding-block: var(--section-y);
}

/* Only bands with media straddling the top edge need room made for it. */
.deep-band--overlap {
    margin-top: var(--space-24);
}

/* Real selectors, not :where() — that has zero specificity and lost to
   .section-title, leaving the heading dark ink on a near-black field. */
.deep-band h2,
.deep-band h3,
.deep-band h4,
.deep-band .section-title {
    color: var(--on-inverse);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tighter);
    line-height: var(--leading-tight);
}

.deep-band p,
.deep-band li,
.deep-band .section-subtitle,
.deep-band .about-feature-caption {
    color: var(--on-inverse-soft);
}

/* Links on the dark band. The brand blue they inherit is close to
   unreadable on --brand-ink — "careers@eoroboticslab.com", "Apply Now" and
   "Send Email" were all rendering as dark blue on near-black. White, with an
   underline so the inline ones still read as links without relying on hue. */
.deep-band a {
    color: var(--on-inverse);
}

.deep-band p a {
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-color: var(--on-inverse-faint);
    transition: text-decoration-color var(--duration-base) var(--ease);
}

.deep-band p a:hover {
    text-decoration-color: var(--on-inverse);
}

/* The sweep button keeps its white label through every state. */
.deep-band .btn-sweep,
.deep-band .btn-sweep:hover,
.deep-band .btn-sweep:visited,
.deep-band .btn-sweep:focus-visible {
    color: var(--on-inverse);
    text-decoration: none;
}

/* Cards inside the band keep their own light background from the page
   stylesheet, so the white text above rendered white-on-near-white and
   vanished. Re-ground every card as glass on the dark field rather than
   reverting the text — a light card on this gradient would fight it.

   Listed by class rather than a blanket rule so nothing that legitimately
   wants a light surface (the straddling photo's frame, for one) gets caught. */
.deep-band .mv-card,
.deep-band .value-card,
.deep-band .stat-card,
.deep-band .feature-block,
.deep-band .capability-item {
    background: rgba(255, 255, 255, 0.06);
    border: none;
    border-radius: var(--radius-lg);
    backdrop-filter: blur(2px);
    box-shadow: none;
}

.deep-band .mv-card,
.deep-band .value-card {
    border-radius: 0;
}

/* Editorial headers and the Core Values heading carry a hairline rule under
   them (layout.css) drawn in --line-soft, which is tuned for the white
   sections it was designed on. On this dark field that grey has nothing to
   sit against and reads as a faint smudge across the band rather than a
   deliberate rule. Space alone separates the heading here.

   Scoped to .deep-band so the same hairline still does its job on every
   light section. */
.deep-band .section-header,
.deep-band .capabilities-header,
.deep-band .process-header,
.deep-band .technologies-header,
.deep-band .internship-header,
.deep-band .content-wrapper,
.deep-band .culture-intro,
.deep-band .career-intro,
.deep-band .position-header,
.deep-band .values-section h3 {
    border-bottom: none;
    padding-bottom: 0;
}

.deep-band .mv-card h3,
.deep-band .value-card h3,
.deep-band .value-card h4,
.deep-band .stat-card h3 {
    color: var(--on-inverse);
}

/* Grain over the field. Heavier than anywhere else on the site, and
   soft-light rather than overlay so it textures the colour without
   flattening the gradient underneath. */
.deep-band.grain::after {
    mix-blend-mode: soft-light;
    opacity: var(--grain-opacity, 0.75);
    /* Finer noise at this strength — a coarse grain this heavy reads as
       dirt rather than texture. */
    background-size: 120px 120px;
}

/* Media that straddles the band's top edge: half on the section above, half
   on the colour. Put this on the element wrapping the image. */
.band-overlap {
    margin-top: calc(var(--space-24) * -2);
    position: relative;
    z-index: 2;
}

.band-overlap img,
.band-overlap video {
    display: block;
    width: 100%;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-xl);
}

@media (max-width: 768px) {
    .deep-band--overlap {
        margin-top: var(--space-16);
    }

    .band-overlap {
        margin-top: calc(var(--space-16) * -1.5);
    }
}


/* ==========================================================================
   3c. LOGO WALL

   Partner and customer marks. The reference site runs these as a single
   quiet row at a consistent height, in full colour, with no container around
   each one — the logos are the content, so any box competes with them.

   Replaces a treatment that put each mark in a 307px square card and then
   applied grayscale(1) at 0.7 opacity, which erased the lighter artwork
   completely: the ITU, AI for Good and Raspberry Pi marks rendered as empty
   boxes. The home page strip had already reached the same conclusion.
   ========================================================================== */

/* Header matches the left-aligned heading/lede pattern used elsewhere,
   replacing a centred 56px title that sat on its own scale. */
.partners-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-6) var(--space-16);
    align-items: end;
    text-align: left;
    margin-bottom: var(--space-12);
}

.partners-eyebrow {
    display: block;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--brand);
    margin-bottom: var(--space-3);
}

.partners-header h2 {
    margin: 0;
    font-size: var(--text-display-sm);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tighter);
    color: var(--ink);
}

.partners-header > p {
    margin: 0;
    max-width: var(--measure);
    font-size: var(--text-lg);
    line-height: var(--leading-relaxed);
    color: var(--ink-soft);
}

@media (max-width: 900px) {
    .partners-header {
        grid-template-columns: 1fr;
        align-items: start;
        gap: var(--space-4);
    }
}

/* Flex rather than grid: auto-fit columns are computed from the track size,
   not the item count, so six marks were being laid into seven columns and
   left a gap on the end. Flex distributes exactly what is there. */
.logo-wall {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-10) var(--space-8);
    margin: 0;
    padding: 0;
    list-style: none;
}

.logo-wall li {
    flex: 0 1 auto;
}

.logo-wall img {
    /* Sized by height so marks of different proportions read as one row. */
    height: 52px;
    width: 100%;
    max-width: 150px;
    object-fit: contain;
    /* No grayscale: several of these are light artwork and vanish against a
       white section. Full fidelity, held slightly back so the row stays quiet. */
    opacity: 0.85;
    transition: opacity var(--duration-base) var(--ease);
}

.logo-wall img:hover {
    opacity: 1;
}

@media (max-width: 640px) {
    .logo-wall {
        justify-content: center;
        gap: var(--space-8) var(--space-6);
    }

    .logo-wall img {
        height: 40px;
        max-width: 112px;
    }
}


/* ==========================================================================
   3d. TRAILING CURSOR

   A filled dot that tracks the pointer exactly, with a hairline ring that
   lags behind it. Over anything interactive the dot balloons and the ring
   fades away. Built by scripts/cursor.js; the geometry lives here.

   The lag is not scripted — it comes from the blanket `transition` on the
   ring, which catches the transform the script sets each mousemove. The dot
   has no transform transition, so it stays under the pointer.
   ========================================================================== */

.mouse-cursor {
    position: fixed;
    left: 0;
    top: 0;
    pointer-events: none;
    border-radius: var(--radius-full);
    /* Promote to its own layer — this repaints on every mousemove. */
    transform: translateZ(0);
    visibility: hidden;
}

.cursor-inner {
    width: 6px;
    height: 6px;
    z-index: 10000001;
    background-color: var(--brand);
    transition: width var(--duration-base) var(--ease-in-out),
                height var(--duration-base) var(--ease-in-out),
                margin var(--duration-base) var(--ease-in-out),
                opacity var(--duration-base) var(--ease-in-out);
}

.cursor-inner.cursor-hover {
    /* Margin scales with the box so the circle grows from its centre. */
    margin-left: -35px;
    margin-top: -35px;
    width: 70px;
    height: 70px;
    background-color: var(--brand);
    opacity: 0.3;
}

.cursor-outer {
    margin-left: -12px;
    margin-top: -12px;
    width: 30px;
    height: 30px;
    border: 1px solid var(--brand);
    box-sizing: border-box;
    z-index: 10000000;
    opacity: 0.5;
    transition: 0.08s ease-out;
}

.cursor-outer.cursor-hover {
    opacity: 0;
}

/* Coarse pointers get nothing — the script bails out too, this is belt and
   braces in case it runs before the media query is evaluated. */
@media (pointer: coarse) {
    .mouse-cursor { display: none; }
}

/* Drop the lag, keep the cursor. Trailing motion is exactly the kind of
   thing this setting is asking us not to do. */
@media (prefers-reduced-motion: reduce) {
    .cursor-outer { transition: opacity 0.08s ease-out; }
    .cursor-inner { transition: opacity var(--duration-base) var(--ease-in-out); }
}


/* ==========================================================================
   3e. SQUARE MEDIA — HOME PAGE

   Home page photography runs with hard corners. The rounding came from three
   places: .showcase-media and .about-feature-media (24px, layout.css) and
   .band-overlap img (16px, set above).

   .showcase-media is scoped through #services because services/robotics.html
   uses the same class and is not part of this change; #services exists only
   on index.html. The other two classes appear on the home page alone.
   ========================================================================== */

#services .showcase-media,
.about-feature-media,
.band-overlap,
/* Robotics' showcase sits on the deep band and takes the same sharp corners. */
.deep-band .showcase-media {
    border-radius: 0;
}

.band-overlap img,
.band-overlap video,
#services .showcase-media img,
#services .showcase-media video,
.about-feature-media img,
.deep-band .showcase-media img,
.deep-band .showcase-media video {
    border-radius: 0;
}

/* Give the photograph the larger share of the row and more height — at an
   even split it was competing with the copy rather than leading it. */
.deep-band .showcase-row {
    grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
    gap: var(--space-12);
    align-items: center;
}

.deep-band .showcase-media img,
.deep-band .showcase-media video {
    width: 100%;
    height: clamp(380px, 42vw, 560px);
    object-fit: cover;
}

@media (max-width: 900px) {
    .deep-band .showcase-row {
        grid-template-columns: 1fr;
        gap: var(--space-8);
    }

    .deep-band .showcase-media img,
    .deep-band .showcase-media video {
        height: clamp(260px, 56vw, 400px);
    }
}


/* ==========================================================================
   4. DISPLAY TYPE

   The reference site sets headlines heavy and tight, in sentence case. This
   is opt-in via .display-tight so existing headings are untouched.
   ========================================================================== */

.display-tight {
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tighter);
    line-height: var(--leading-tight);
    text-wrap: balance;
}


/* ==========================================================================
   5. SECTION HEADERS

   The reference site never centres a section heading. It sets the statement
   hard left and heavy, with the supporting line beside it rather than under
   it, so the eye lands on the claim first and the column edges stay aligned
   with the content below.

   .section-header / .section-title / .section-subtitle appear only on
   index.html, so restyling them here changes nothing elsewhere.
   ========================================================================== */

.section-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-6) var(--space-16);
    align-items: end;
    text-align: left;
    max-width: var(--container-max);
    margin: 0 auto var(--space-12);
}

.section-title {
    margin: 0;
    /* Heavier and tighter than the site default — the reference site's
       headlines are bold, not semibold, and set on a tight measure. */
    font-size: var(--text-display);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tighter);
    line-height: var(--leading-tight);
    color: var(--ink);
    max-width: 14ch;
}

.section-subtitle {
    margin: 0;
    max-width: var(--measure);
    font-size: var(--text-lg);
    line-height: var(--leading-relaxed);
    color: var(--ink-soft);
}

@media (max-width: 900px) {
    .section-header {
        grid-template-columns: 1fr;
        align-items: start;
        gap: var(--space-4);
    }

    .section-title {
        max-width: none;
    }
}


/* ==========================================================================
   6. SECTION RHYTHM

   The reference site's defining quality is air. These sections carried a
   tighter rhythm than the type scale wants.
   ========================================================================== */

.services,
.about {
    padding-block: var(--section-y);
}


/* ==========================================================================
   7. FULL-BLEED MEDIA

   Breaks an element out of its container to the viewport edge, the way the
   reference site runs footage between text sections. Expects to be a direct
   child of a normal-flow container.
   ========================================================================== */

.full-bleed {
    width: 100vw;
    max-width: 100vw;
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
}

.full-bleed img,
.full-bleed video {
    display: block;
    width: 100%;
    height: clamp(320px, 56vh, 620px);
    object-fit: cover;
}
