/* Inter, self-hosted (was Google Fonts) — single variable-font file covers weights 100-900 */
@font-face {
    font-family: 'Inter';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('/fonts/inter-variable-latin.woff2') format('woff2');
}

/* CSS Variables for consistent theming */
:root {
    /* --primary-color history: #00BFFF (Deep Sky Blue, ~2.1:1, failed WCAG AA) ->
       #0077A3 (~5:1, fixed contrast but read as generic SaaS-blue, not "deep tech") ->
       #0B4F6C (current): deeper, teal-leaning navy chosen for gravitas/confidence
       specifically, not just contrast. Also darker than #0077A3, so the hero-overlay
       contrast (which depends on this value - see .hero-overlay below) improved when
       this changed: 3.18:1 -> 4.42:1 at the same alpha, re-verified by compositing
       onto the image. (brand.html no longer keeps a separate retired-colors section -
       removed Aug 2026 - this comment and the hero-overlay one below are now the only
       record of prior values.) */
    --primary-color: #0B4F6C;
    /* Both derived via color-mix() (not hardcoded rgba()/hex) so neither can go
       stale the way the old hardcoded hero-overlay color once did if primary
       changes again - but they mix TOWARD DIFFERENT THINGS, deliberately:
       --primary-color-50 mixes toward transparent - genuinely see-through, used
       for the modal backdrop (index.php/en/index.php), which has to let the
       dimmed page show through behind it. A solid color there would just hide
       the page instead of dimming it - not a style choice, a functional one.
       Currently resolves to #0B4F6C at 50% alpha; composited over white that
       reads as ~#85A7B6, but it is NOT that color - it's genuinely translucent,
       and will look different over any other background.
       --primary-color-25 mixes toward white - a solid, opaque tint, used for the
       focus ring (:focus-visible below). Every place a ring appears on this site
       sits on a white/near-white background, so an opaque tint and 25% alpha
       render pixel-identical here - opaque was chosen anyway for predictability
       (one fixed color regardless of context) since the ring doesn't have the
       backdrop's reason to need transparency. Currently resolves to exactly
       #C2D3DA - unlike the 50% token, this one legitimately IS that hex value,
       not just "looks like it over white". */
    --primary-color-50: color-mix(in srgb, var(--primary-color) 50%, transparent);
    --primary-color-25: color-mix(in srgb, var(--primary-color) 25%, white);
    --text-dark: #1d3557; /* Dark Blue-Grey: High contrast text */
    /* --text-light was #6c757d - 4.45:1 against --bg-light, just under the 4.5:1 AA
       floor for normal-size text. Darkened slightly to ~5.7:1. */
    --text-light: #5a6470;
    --bg-light: #f8f9fa; /* Off-white background */
    --white: #ffffff;
    /* Contact-form status text (send.php success/error), currently the only place
       on the site using red/green semantically. Plain CSS "red" (#ff0000) measures
       4.00:1 against white - under the 4.5:1 AA floor - hence a specific hex here
       rather than the named color. "green" (#008000) already passed at 5.14:1 but
       is centralized too, so both live in one place instead of as literals in JS. */
    --text-danger: #c00000;
    --text-success: #008000;
    --spacing-unit: 1.5rem;
    --max-width: 1200px;
    --border-radius: 8px;
    --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}

/* Reset and Base Styles */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Focus ring, site-wide. Nothing defined one existed before this - real WCAG 2.4.7
   gap, not just a palette nicety. :focus-visible (not :focus) so it only shows for
   keyboard navigation, not every mouse click. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
textarea:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px var(--primary-color-25);
    transition: box-shadow 0.15s ease;
}

/* Primary button. background-color lives here, not inline, specifically so :hover
   can override it - an inline style's specificity beats a plain external selector,
   and #joinBtn/#submitBtn's OTHER properties (padding, font-size, etc.) stay inline
   as before; only background-color moved, to keep this a small, targeted change. */
.btn-primary {
    background-color: var(--primary-color);
}

.btn-primary:hover {
    background-color: color-mix(in srgb, var(--primary-color) 85%, black);
}

/* Contact modal open/close. Was an instant display:none/flex toggle in JS -
   visibility+opacity here instead, since display can't be transitioned (an
   element that's display:none isn't in the render tree to animate from).
   visibility is deliberately listed alongside opacity: per spec, a transitioned
   visibility change takes effect immediately going hidden->visible, but is
   delayed to match the other property's duration going visible->hidden - so
   the modal opens instantly-interactive and only leaves the accessibility
   tree/tab order once the fade-out finishes, no JS timing or reflow tricks
   needed. visibility:hidden (not opacity alone) is what keeps the closed
   modal out of hit-testing and tab order, same guarantee display:none gave
   before switching to this. */
#contactModal {
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.2s ease, visibility 0.2s ease;
}

#contactModal.is-open {
    opacity: 1;
    visibility: visible;
}

#contactModalDialog {
    transform: translateY(8px) scale(0.96);
    transition: transform 0.2s ease;
}

#contactModal.is-open #contactModalDialog {
    transform: translateY(0) scale(1);
}

body {
    font-family: 'Inter', sans-serif;
    line-height: 1.6;
    color: var(--text-dark);
    background-color: var(--bg-light);
    -webkit-font-smoothing: antialiased;
}

/* Banner Section */
.hero-banner {
    width: 100%;
    height: 40vh; /* Takes up 40% of viewport height */
    min-height: 400px;
    /* Was background-image: url('/images/hero-banner.webp') (cover/center) -
       removed, not just backgrounded-behind-the-video: the video only fills
       a centered 400x400-ish square (height:auto width, see .hero-video),
       so the photo would still show either side of it, in a visibly
       different color/content to the video's own plain white background -
       a hard seam right where the video's edge sits. Flat white matches
       the video's actual background exactly, everywhere, so there's
       nothing for the overlay gradient to tint inconsistently. The photo
       still works fine as the <video>'s own poster (shows briefly while it
       loads, inside the video's own box, so no seam there either) and as
       the true no-JS/no-video fallback, just not as this element's CSS
       background anymore. */
    background-color: var(--white);
    display: flex;
    align-items: center;
    justify-content: center;
    position: relative;
    /* The rotating-sphere video (.hero-video below) is a 900x900 square
       sized to the banner's height, not stretched to its width - on a
       narrow viewport that square can be wider than the banner itself.
       Needed only once a DOM child (the video) could overflow; a plain
       background-image never required this, it's always clipped to its
       box by default. */
    overflow: hidden;
}

/* Rotating nanocarrier visual behind the overlay/text, replacing what used
   to be purely a static photo. Source is a 900x900 square render (see
   project memory: the liposome Fibonacci-sphere Blender work) - sized to
   the banner's height with width:auto so it scales uniformly (never
   stretched into an oval), centered horizontally since the banner is much
   wider than it is tall. object-fit: cover is a safety net, not the primary
   sizing mechanism - width:auto already preserves the true 1:1 ratio.
   muted + playsinline are required for autoplay to work at all in most
   browsers (iOS Safari forces fullscreen without playsinline); looping and
   playback are started from JS, gated on prefers-reduced-motion, rather
   than the autoplay attribute, so a reduced-motion visitor's browser never
   even attempts to decode/play it - see the index.php/en/index.php script
   and the prefers-reduced-motion block further down for the other half of
   this. aria-hidden + tabindex="-1": purely decorative, same treatment as
   any other non-informational background visual on this site. */
.hero-video {
    position: absolute;
    /* Was top:0 + height:100% (sphere diameter == banner height exactly).
       Sized larger than the banner on purpose now and centered on both
       axes so the extra height crops symmetrically top/bottom via
       .hero-banner's overflow:hidden - makes the sphere read as a
       prominent visual anchor rather than something the hero-title text
       (which scales on a completely different axis - viewport *width*,
       via its own clamp() - vs. the banner's viewport *height* - can
       dwarf, depending on the exact window shape it's viewed at. */
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    height: 140%;
    width: auto;
    object-fit: cover;
}

.hero-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* hero-banner.webp is a very light image (avg. luminance ~85%) - this overlay is
       the only thing keeping hero-title/subtitle legible, and it's kept close to the
       WCAG large-text floor (3:1) deliberately, to show as much of the image as
       asked for, repeatedly (this has been retuned three times now for exactly that
       reason - it's not settled, it moves whenever --primary-color or the requested
       transparency does).

       Top stop is color-mix(var(--primary-color), transparent), not a hardcoded
       hex, specifically because the alpha needed to hit a given contrast level is
       NOT stable across color changes - it depends on the exact luminance of
       whatever --primary-color currently is. Concretely: #005f73 (original) needed
       0.62/0.72 for ~3.4:1; swapping to #0077A3 at the same alpha only measured
       2.81:1 (a lighter color needs MORE alpha for the same darkening, not the
       same); #0077A3 needed 0.70/0.78 to recover ~3.2-4.4:1; the current #0B4F6C,
       being darker again, needed only 0.55/0.63 for the equivalent ~3.2:1 - same
       target contrast, third different alpha pair, because the color kept changing
       under it. There is no fixed alpha that's "correct" independent of the color.
       Whenever --primary-color changes, or the transparency is adjusted again,
       re-run the actual compositing check against the real image - don't reuse a
       previous alpha value and don't estimate from luminance alone.

       Bottom stop was rgba(0, 0, 0, 0.63) - plain black - until feedback that the
       banner read as "grey and murky". Turned out to be two compounding things:
       a black-based wash desaturates whatever's under it toward neutral grey
       rather than deepening it, AND the source photo itself measured genuinely
       close to neutral grey in the sampled region (~217-232 on all three
       channels, checked by sampling the actual file, not guessed). Replaced the
       black with a dark shade of --primary-color instead - same darkening job,
       but reads as a deliberate deep-teal wash instead of a neutral one. Nested
       color-mix(), not a hardcoded rgba() of the primary color's RGB values:
       the inner mix darkens --primary-color toward black *before* the outer mix
       adds transparency, so this still can't go stale the way a literal rgba()
       tuple would if --primary-color changes again - same reasoning as the top
       stop, just two mixes deep instead of one.

       TEMPORARILY DISABLED (Oct 2026) - gradient commented out below, not
       deleted, specifically so it's a one-line uncomment to restore. With
       this off, hero-title text has no contrast guarantee against the
       video/photo behind it - not meant to stay this way, just to see the
       hero video unmasked for now. */
    /* background: linear-gradient(to bottom, color-mix(in srgb, var(--primary-color) 55%, transparent), color-mix(in srgb, color-mix(in srgb, var(--primary-color) 55%, black) 70%, transparent)); */
}

.hero-content {
    position: relative;
    z-index: 2;
    text-align: center;
    color: var(--white);
    padding: 0 var(--spacing-unit);
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
}

.hero-title {
    /* Fluid instead of a fixed 6rem: at 96px "NanoNeo" is wider than a 375px
       viewport (measured - real horizontal page overflow, not hypothetical).
       Scales from 2.5rem up to the original 6rem; the 10vw middle term is
       what actually does the scaling, the two fixed values just bound it. */
    font-size: clamp(2.5rem, 10vw, 6rem);
    font-weight: 300;
    letter-spacing: -0.02em;
    margin-bottom: 0.5rem;
}

/* Language Switcher */
.lang-switch {
    position: absolute;
    top: 1.5rem;
    right: 1.5rem;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    background: rgba(255, 255, 255, 0.85);
    padding: 0.4rem 0.9rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: 0.02em;
}

.lang-switch a,
.lang-switch span {
    color: var(--text-dark);
    text-decoration: none;
    transition: color 0.15s ease;
}

.lang-switch span {
    opacity: 0.4;
}

.lang-switch a:hover {
    color: var(--primary-color);
}

/* Main Container */
.container {
    max-width: var(--max-width);
    margin: 0 auto;
    padding: calc(var(--spacing-unit) * 2) var(--spacing-unit);
}

/* Intro Section */
.intro-section {
    text-align: center;
    max-width: 800px;
    margin: 0 auto 4rem auto;
}

.intro-text {
    font-size: 1.2rem;
    color: var(--text-light);
    font-weight: 600;
}

/* Partner & Funder Logos */
.partners-section {
    margin-bottom: 2rem;
}

.partners-caption {
    font-size: 0.9rem;
    color: var(--text-light);
    margin-bottom: 1.5rem;
}

.partners-logos {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
}

.partners-logos img {
    display: block;
    width: 100%;
    height: auto;
}

/* Below ~640px, scaling the strip to full-width shrinks the logos
   past legibility (2400x150 source = 16:1). Switch to a fixed
   height + horizontal scroll instead of squashing them. */
@media (max-width: 600px) {
    .partners-logos {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }

    .partners-logos img {
        width: auto;
        height: 44px;
        max-width: none;
    }
}

/* Footer */
footer {
    background-color: var(--white);
    text-align: center;
    padding: 1rem 1.5rem 2rem;
    margin-top: 1.5rem;
    border-top: 1px solid #dee2e6;
    color: var(--text-light);
    font-size: 0.9rem;
}

.footer-links {
    margin-top: 0.5rem;
}

.footer-links a {
    color: inherit;
    text-decoration: underline;
    transition: color 0.15s ease;
}

.footer-links a:hover {
    color: var(--primary-color);
}

/* Legal pages (Impressum, Datenschutz) */
.legal-page {
    max-width: 800px;
    margin: 0 auto;
    padding: 4rem 1.5rem;
}

.back-link {
    display: inline-block;
    margin-bottom: 2.5rem;
    color: var(--text-light);
    text-decoration: none;
    font-size: 0.9rem;
}

.back-link:hover {
    color: var(--primary-color);
}

.legal-page h1 {
    font-size: 2.25rem;
    font-weight: 300;
    margin-bottom: 2rem;
}

.legal-page h2 {
    font-size: 1.1rem;
    font-weight: 600;
    color: var(--primary-color);
    margin-top: 2.5rem;
    margin-bottom: 0.75rem;
}

.legal-page p {
    margin-bottom: 1rem;
}

.legal-page address {
    font-style: normal;
    margin-bottom: 1rem;
}

.legal-page a {
    color: var(--primary-color);
}

/* Motion preferences: everything above with a `transition` is purely cosmetic
   (fades/scales/color shifts, nothing that carries information) - honor
   prefers-reduced-motion by disabling all of it. Placed last deliberately:
   same specificity as the rules it overrides, so source order is what makes
   it win. */
@media (prefers-reduced-motion: reduce) {
    a:focus-visible,
    button:focus-visible,
    input:focus-visible,
    textarea:focus-visible,
    .lang-switch a,
    .lang-switch span,
    .footer-links a,
    #contactModal,
    #contactModalDialog {
        transition: none;
    }

    /* Belt and suspenders with the JS play-gating in index.php/en/index.php
       (which already skips calling .play() under this same preference) -
       if the video somehow started anyway, this stops it being seen. The
       static background-image on .hero-banner shows through instead. */
    .hero-video {
        display: none;
    }
}
