/* Shared visual layer, loaded after this site's own stylesheet.
   It re-tokenises the palette, hosts the 3D scene canvases and restyles the
   shared surfaces. Every existing component rule keeps working underneath;
   nothing here replaces them. */

/* Palette lifted from styles.css's own @media (prefers-color-scheme: dark)
   block and applied unconditionally.

   This file used to declare its own shorter palette here. It set --bg,
   --body and --ink to dark values and left --surface, --hero-a, --hero-b,
   --heading and --header-bg at the light ones underneath, so on a
   light-mode machine the hero and every card stayed white while the text
   on them was coloured for a dark background. Taking the site's own dark
   set wholesale means there is one palette, and it cannot drift out of
   sync with the components that read from it. */
:root {
    color-scheme: dark;
    --ink: #e9eff7;
    --body: #b8c6d8;
    --muted: #8fa1b6;
    --accent: #7db3e8;
    --accent-dark: #a5c9f0;
    --accent-soft: #142e49;
    --line: #233852;
    --line-strong: #35506e;
    --bg: #0b1622;
    --bg-alt: #0e1c2b;
    --surface: #122334;
    --header-bg: rgba(11, 22, 34, 0.88);
    --hero-g1: #0d1b2a;
    --hero-g2: #0b1622;
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35), 0 1px 1px rgba(0, 0, 0, 0.25);
    --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.3), 0 12px 30px rgba(0, 0, 0, 0.4);
    --shadow-lg: 0 2px 6px rgba(0, 0, 0, 0.35), 0 22px 44px rgba(0, 0, 0, 0.5);
    --aero-bg:var(--bg);
    --aero-text:var(--body);
    --aero-signal:var(--accent);
    --aero-grid:rgba(120,190,220,.045);
    --scene-ease: cubic-bezier(.16, 1, .3, 1);
    --scene-r: 8px;
}

html { scroll-behavior: smooth; }

body {
    background: var(--aero-bg, #080d16);
    color: var(--aero-text, #cbd5ec);
    -webkit-font-smoothing: antialiased;
}

/* the faint engineering grid that sits under everything */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background-image:
        linear-gradient(var(--aero-grid, rgba(130,160,230,.045)) 1px, transparent 1px),
        linear-gradient(90deg, var(--aero-grid, rgba(130,160,230,.045)) 1px, transparent 1px);
    background-size: 72px 72px;
    -webkit-mask-image: radial-gradient(ellipse 120% 80% at 50% 0%, #000 25%, transparent 75%);
    mask-image: radial-gradient(ellipse 120% 80% at 50% 0%, #000 25%, transparent 75%);
}

h1, h2, h3, h4 { text-wrap: balance; letter-spacing: -0.03em; }

/* Header, measured rather than assumed.

   Between roughly 1000px and 1150px this bar was two rows tall with the
   wordmark overlapping the first item by 25px. Two separate causes, and the
   first hides the second:

   The row is flex-wrap: nowrap, so when it gets tight the items do not wrap.
   The text inside them does, and "CDSCO Approvals" becomes two lines. Every
   item still measures as fitting, so a width check passes while the bar stands
   at two rows.

   And the brand was being squeezed narrower than its own text, so the wordmark
   overflowed its box and crossed into the nav whatever gap was set. Refusing to
   shrink it is the fix; a margin alone does nothing.

   This site's labels are long — "CDSCO Approvals" and "Pharmacovigilance" carry
   most of the width — so the row needs about 1150px. The handover to the menu
   button moves to match, in styles.css. */
.site-header .brand { flex-shrink: 0; }
.site-header .brand span { white-space: nowrap; }

@media (min-width: 1151px) {
    .site-header .nav-links a { white-space: nowrap; }
    .site-header .nav-cta { white-space: nowrap; }
    .site-header .nav-links { margin-left: 1.5rem; }
}

/* ------------------------------------------------------------- scenes --- */

/* Sections that host a canvas. The container must outrank the scrim: a
   container with its own z-index creates a stacking context that a child
   cannot escape, which is what put an earlier scrim on top of its own copy. */
.scene-host { position: relative; overflow: hidden; }
.scene-host > .container,
.scene-host > .container-narrow { position: relative; z-index: 3; }

.scene-host > canvas[data-scene] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    z-index: 0;
    opacity: 0;
    pointer-events: none;
    transition: opacity 1.5s var(--scene-ease);
    /* each host overrides this to say where its copy sits */
    --scene-mask: radial-gradient(135% 115% at 50% 50%, transparent 0%, rgba(0, 0, 0, .5) 42%, #000 82%);
    -webkit-mask-image: var(--scene-mask);
    mask-image: var(--scene-mask);
}

.scene-host > canvas[data-scene].live { opacity: .62; }

/* alternate which side the geometry occupies down the page, so scrolling
   reads as one continuous object rather than a stack of unrelated panels */
.scene-host:nth-of-type(odd) > canvas[data-scene] {
    --scene-mask: radial-gradient(82% 125% at 88% 50%, #000 8%, rgba(0, 0, 0, .55) 40%, transparent 68%);
}

.scene-host:nth-of-type(even) > canvas[data-scene] {
    --scene-mask: radial-gradient(82% 125% at 12% 50%, #000 8%, rgba(0, 0, 0, .55) 40%, transparent 68%);
}

/* only a whisper of vignette now — the mask does the legibility work */
.scene-host::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    background: radial-gradient(128% 98% at 50% 45%, transparent 36%, var(--aero-bg, #080d16) 94%);
}

/* the landing hero reads left, so the geometry is cleared off the copy column */
.hero.scene-host > canvas[data-scene].live,
.hero-new.scene-host > canvas[data-scene].live { opacity: .92; }

.hero.scene-host > canvas[data-scene],
.hero-new.scene-host > canvas[data-scene] {
    --scene-mask: radial-gradient(105% 118% at 82% 46%, #000 18%, rgba(0, 0, 0, .62) 48%, transparent 74%);
}

.hero.scene-host::after,
.hero-new.scene-host::after {
    background: linear-gradient(100deg, var(--aero-bg, #080d16) 2%,
        rgba(8, 13, 22, .55) 34%, rgba(8, 13, 22, .12) 62%, transparent 82%);
}

.page-hero.scene-host > canvas[data-scene].live,
.page-header.scene-host > canvas[data-scene].live { opacity: .66; }

.page-hero.scene-host > canvas[data-scene],
.page-header.scene-host > canvas[data-scene] {
    --scene-mask: radial-gradient(95% 130% at 84% 50%, #000 12%, rgba(0, 0, 0, .5) 46%, transparent 72%);
}

/* An article body is a tall column of prose with the scene running its whole
   length, so the mask clears a vertical channel rather than a pocket. The
   geometry survives in the margins and drifts past as the reader scrolls. */
.article.scene-host > canvas[data-scene],
.blog-content.scene-host > canvas[data-scene] {
    --scene-mask: linear-gradient(90deg, #000 0%, rgba(0, 0, 0, .5) 17%, transparent 30%,
                                          transparent 70%, rgba(0, 0, 0, .5) 83%, #000 100%);
}
.article.scene-host > canvas[data-scene].live,
.blog-content.scene-host > canvas[data-scene].live { opacity: .5; }

/* hero copy is the one place type sits over live geometry, so it carries its
   own shadow — cheap insurance against whatever the scene draws behind it */
.hero h1, .hero-new h1, .page-hero h1, .page-header h1,
.hero .hero-lede, .hero-new .hero-lede,
.hero .eyebrow, .hero-new .eyebrow,
.hero-text h1, .hero-text p {
    text-shadow: 0 2px 26px rgba(4, 6, 14, .92), 0 1px 4px rgba(4, 6, 14, .7);
}

/* --------------------------------------------------------- long-form --- */

/* Body copy in the insights pieces is justified to both margins.

   Justification without hyphenation is worse than ragged-right, not better:
   the browser can only stretch the word spaces, so a long word forces a line
   of wide gaps and those gaps line up down the column as rivers. `hyphens:
   auto` lets it break words instead, which is what makes the spacing even, and
   it needs the lang attribute on <html> to pick a dictionary — every page here
   has lang="en".

   Only paragraphs. Headings, list items, pull quotes, captions and anything in
   a table stay ragged: they are short enough that justifying them produces the
   stretched-out lines this is meant to avoid. The pre/code exclusion is
   insurance rather than a fix for anything currently published — justifying
   preformatted text would destroy the alignment that is the reason for showing
   it, and these are pieces that tend to grow samples later. */
.article-prose p,
.article p {
    text-align: justify;
    -webkit-hyphens: auto;
    hyphens: auto;
    hyphenate-limit-chars: 7 3 3;
}

.article-prose pre,
.article-prose pre p,
.article-prose code,
.article pre,
.article code {
    text-align: left;
    -webkit-hyphens: none;
    hyphens: none;
}

/* A narrow column cannot absorb justification — under about 45 characters
   there are too few word spaces per line to distribute the slack, and the
   result is gappy however good the hyphenation is. Phones get ragged-right. */
@media (max-width: 640px) {
    .article-prose p,
    .article p {
        text-align: left;
        -webkit-hyphens: none;
        hyphens: none;
    }
}

/* ------------------------------------------------------- micro-motion --- */

/* Gated behind .js, set on <html> before anything else can throw. */
.js [data-reveal] {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity .85s var(--scene-ease), transform .85s var(--scene-ease);
}

.js [data-reveal].in { opacity: 1; transform: none; }

:focus-visible {
    outline: 2px solid var(--aero-signal, #3fd6a4);
    outline-offset: 3px;
    border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .js [data-reveal] { opacity: 1 !important; transform: none !important; }
    canvas[data-scene] { display: none !important; }
}


/* ---- lifted component rules -------------------------------------------
   These sat inside styles.css's @media (prefers-color-scheme: dark) block
   alongside the token overrides. The palette above is applied
   unconditionally, so these have to be too: without them the wordmark stays
   navy on a navy header, and the filled buttons keep an accent that was
   lightened for use as *text* on dark, giving white labels at 2.2:1. */
.brand span {
    color: var(--ink);
}

/* --accent is lightened for text on dark; keep filled buttons on a blue
   that holds AA with white label. */
.btn-primary {
    background: #1d69b8;
    color: #fff;
}

.btn-primary:hover {
    background: #2a79cf;
    color: #fff;
}

.btn-light:hover {
    background: #dce8f6;
}

.step .num {
    background: #1d69b8;
}

.bento-featured {
    background: linear-gradient(135deg, var(--accent-soft) 0%, var(--surface) 60%);
}

/* .hero h1 takes color: var(--navy) in styles.css, and --navy is dual purpose
   on this site: seven dark band backgrounds and four text colours. It cannot
   be flipped without inverting those bands. The headline is the one text use
   that lands on a dark surface, so it is set directly here.

   This was already broken in the site's own dark scheme, where the same
   #0a2540 heading would have sat on a #0d1b2a hero. Nobody had viewed it
   in dark mode. */
.hero h1 { color: var(--ink); }
