/* ========================================================================== CTAO Science Portal — design tokens per the official CTAO Brand Guidelines (April 2024): colors D.2, typography D.3 (Inter body / Space Grotesk display at medium weight per D.3.4), flash D.4. All look-and-feel lives in the tokens below; WCAG 2.1 AA contrast (photo-hero worst cases computed — see DESIGN.md). Light theme only (brand is light-first; navy bands carry the dark identity). System rules (spacing / elevation / surfaces / motion): see DESIGN.md. ========================================================================== */ :root { color-scheme: light; /* Brand palette — hex values straight from the brand book */ --galaxy: #00004a; /* Galaxy Blue — headings, header/footer bands */ --cherenkov: #00e4d8; /* Cherenkov Blue — primary CTA, flash, hero subtitle, logo cluster ONLY */ --moon: #f5f5f5; /* Moon Gray — surfaces */ --azure: #007aff; /* Cosmic Azure — focus rings (UI only on light) */ --indigo: #00009c; /* Interestellar Indigo — gradient depth */ /* Derived, WCAG-adjusted */ --text: #101228; /* near-Galaxy body ink */ --muted: #46536a; --border: #e2e5ee; --link: #0057c2; /* Cosmic Azure darkened to pass AA (4.5:1) for body links */ --on-galaxy-muted: #c7cde9; --tint-cherenkov: color-mix(in srgb, var(--cherenkov) 16%, transparent); /* highlight only */ --cherenkov-hover: color-mix(in oklab, var(--cherenkov), var(--galaxy) 12%); /* CTA hover state */ --ico-ext: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"%3E%3Cpath d="M7 17 17 7M9 7h8v8" fill="none" stroke="black" stroke-width="2.5"/%3E%3C/svg%3E'); /* Type — fonts (BRAND D.3.1/D.3.2) and the ONLY font sizes in this codebase */ --font-body: "Inter", "Inter Fallback", Arial, system-ui, sans-serif; --font-display: "Space Grotesk", var(--font-body); /* headlines only */ --fs-xs: 0.8125rem; /* chips, meta, badges, labels */ --fs-s: 0.9rem; /* secondary text, nav, forms, footer */ --fs-l: 1.125rem; /* card titles, standfirst, panel titles */ --fs-xl: 1.5rem; /* prose h2, featured title, auth h1 */ --fs-h2: clamp(1.75rem, 3.5vw, 2.5rem); /* section heads */ --fs-h1: clamp(2.25rem, 5vw, 3.5rem); /* page/article headlines */ --fs-display: clamp(2.5rem, 7vw, 4.75rem); /* home hero only */ /* ctao.org uses generous rounding (15/30/60px tokens) and pill buttons */ --radius: 16px; --radius-pill: 999px; --maxw: 1120px; /* Fluid spacing scale (Apple-scale rhythm: sections breathe, mobile stays compact). Every section / container / card padding sits on this scale — no ad-hoc values. */ --gutter: clamp(18px, 4vw, 28px); /* page side padding */ --space-s: 12px; --space-m: 24px; --space-l: clamp(24px, 2vw + 16px, 40px); --space-xl: clamp(56px, 7vw, 120px); /* section rhythm */ --space-band: clamp(72px, 10vw, 160px); /* hero / auth / landing-head padding */ /* Elevation — navy-tinted, soft/large/low-alpha; the ONLY two shadows allowed */ --shadow-ambient: 0 1px 2px rgba(0, 0, 74, 0.04), 0 8px 24px rgba(0, 0, 74, 0.06); --shadow-lifted: 0 2px 6px rgba(0, 0, 74, 0.05), 0 24px 48px rgba(0, 0, 74, 0.14); /* Motion — ONE hover language everywhere (ctao.org: quiet .25s fades) */ --dur: 0.25s; --ease: ease-out; /* Nebula glow (BRAND D.5) — auth band, static AND animated. Alphas are contrast ceilings (DESIGN.md): Cherenkov ≤.38 / Azure ≤.35; cores sit in opposite corners — cores never meet. */ --nebula: radial-gradient(52% 58% at 78% 76%, rgba(0, 228, 216, 0.38), transparent 66%), radial-gradient(46% 52% at 12% 18%, rgba(0, 122, 255, 0.33), transparent 64%), radial-gradient(75% 85% at 40% 55%, rgba(0, 0, 156, 0.8), transparent 72%); /* Photo-hero variant: no indigo depth blob over photography, Cherenkov ≤.30 with its core pinned ≥80% band width (only ~.15 fringe reaches text). */ --nebula-glow: radial-gradient(52% 58% at 82% 80%, rgba(0, 228, 216, 0.3), transparent 66%), radial-gradient(46% 52% at 10% 15%, rgba(0, 122, 255, 0.28), transparent 64%); /* Hero scrim over the photo — worst-case AA computed in DESIGN.md: small text lives left of 52% band width (α ≥ .76 there). */ --hero-scrim: linear-gradient(to right, rgba(0, 0, 74, 0.84), rgba(0, 0, 74, 0.76) 52%, rgba(0, 0, 74, 0.44) 78%, rgba(0, 0, 74, 0.26)); } /* ≤640px the hero text spans the full width — flat scrim, computed ≥5.28:1 */ @media (max-width: 640px) { :root { --hero-scrim: linear-gradient(rgba(0, 0, 74, 0.8), rgba(0, 0, 74, 0.8)); } } /* Self-hosted brand fonts — latin subset, variable weight, swap + metric-matched fallback */ @font-face { font-family: "Inter"; font-weight: 400 700; font-display: swap; src: url("/fonts/inter-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } @font-face { font-family: "Space Grotesk"; font-weight: 500 700; font-display: swap; src: url("/fonts/space-grotesk-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } @font-face { font-family: "Inter Fallback"; src: local("Arial"); size-adjust: 107%; ascent-override: 90.2%; descent-override: 22.48%; line-gap-override: 0%; } /* Screen-reader-only utility (visually hidden, still announced) */ .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; } * { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; scroll-padding-top: 76px; /* sticky header + anchor jumps */ } /* Guard, not a solution (DESIGN.md): every wide element is contained at its own level (tables scroll in their box, long words wrap); clip only catches regressions so one offender can never remove the page gutters. `clip`, unlike `hidden`, creates no scroll container — sticky header keeps working. */ html, body { overflow-x: clip; } body { margin: 0; font-family: var(--font-body); background: #fff; color: var(--text); line-height: 1.5; min-height: 100dvh; } /* Display type: Space Grotesk at medium weight (BRAND D.3.4 — regular/medium), tight tracking at display sizes, compact leading. Hierarchy comes from size. */ h1, h2 { font-family: var(--font-display); font-weight: 500; letter-spacing: -0.025em; line-height: 1.1; } h3 { font-weight: 600; } h1, h2, h3, .hero p { text-wrap: balance; } /* Overlong unbroken words (slugs, codes) may break rather than overflow the page */ h1, h2, h3 { overflow-wrap: break-word; } .prose p, .desc { text-wrap: pretty; } /* Inline SVG icons — stroke inherits currentColor */ .ico { width: 18px; height: 18px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; flex: none; } /* All disclosures (menu, search, TOC) share the plain-button look */ summary { cursor: pointer; list-style: none; } summary::-webkit-details-marker { display: none; } a { color: var(--link); text-decoration: underline; text-underline-offset: 3px; } a:hover { color: var(--indigo); } /* Placeholder links ( without href) don't pretend to be clickable */ a:not([href]) { text-decoration: none; cursor: default; } img { max-width: 100%; height: auto; display: block; } /* Visible keyboard focus everywhere (WCAG 2.4.7) */ :focus-visible { outline: 2px solid var(--azure); outline-offset: 2px; } /* Skip link — first tab stop, hidden until focused (WCAG 2.4.1) */ .skip { position: absolute; left: 16px; top: -52px; z-index: 20; background: var(--galaxy); color: #fff; text-decoration: none; padding: 10px 16px; border-radius: 0 0 var(--radius) var(--radius); } .skip:focus { top: 0; } /* Safety net: even third-party/UA leftovers never move for reduced-motion users */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition: none !important; animation: none !important; } } .container { max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); } /* Surface rhythm: white (editorial + landing heads) → Moon Gray (service/utility bands) → Galaxy (home hero, auth, footer) */ .band--moon { background: var(--moon); } /* Flash — the REAL brand star (BRAND D.4): inline SVG path lifted from the official logo, never a CSS approximation. Budget: one accent per view; static. */ .flash { width: 12px; height: 12px; color: var(--cherenkov); flex: none; } /* Header — Galaxy Blue band, reverse (white) logo per BRAND B.1.2; translucent over content where backdrop blur is supported (AA computed: worst underlying white → bar (31,31,96), muted nav text 9.4:1). Solid Galaxy fallback. */ .site-header { background: var(--galaxy); position: sticky; top: 0; z-index: 10; } @supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) { .site-header { background: color-mix(in srgb, var(--galaxy) 88%, transparent); -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px); } } .nav { display: flex; align-items: center; gap: 4px; min-height: 56px; flex-wrap: nowrap; } .nav > nav { display: flex; align-items: center; gap: 4px; } /* One-row guarantee: everything keeps its natural size except the spacer (links are single words, they can never wrap) */ .nav > * { flex: none; } /* Header tiers, one row always: ≥1025px full nav · ≤1024px disclosure menu · ≤640px sub-brand hidden · ≤480px icon-only menu, smaller logo · ≤360px compact. Search is the same icon-reveal at every width (one pattern, no width math). */ .menu { display: none; } .menu .i-close { display: none; } .menu[open] .i-menu { display: none; } .menu[open] .i-close { display: block; } .brand { display: flex; align-items: center; gap: 10px; margin-right: 10px; text-decoration: none; } .brand img { height: 26px; width: auto; } .brand .brand-sub { color: var(--cherenkov); font-size: var(--fs-xs); font-weight: 500; border-left: 1px solid rgba(255, 255, 255, 0.25); padding-left: 10px; } .nav .spacer { flex: 1; } .nav a.navlink { color: var(--on-galaxy-muted); font-weight: 500; font-size: var(--fs-s); padding: 10px 10px; text-decoration: none; border-bottom: 2px solid transparent; } .nav a.navlink:hover { color: #fff; } .nav a.navlink[aria-current="page"] { color: #fff; border-bottom-color: #fff; } /* AAI sign-in entry — ghost/outline: Cherenkov fill is reserved for the page's primary CTA, the header only whispers. */ .nav .login-mock { color: #fff; background: transparent; font: inherit; font-weight: 600; font-size: var(--fs-s); border: 1px solid rgba(255, 255, 255, 0.55); border-radius: var(--radius-pill); padding: 7px 18px; margin-left: 6px; text-decoration: none; white-space: nowrap; } .nav .login-mock:hover { background: rgba(255, 255, 255, 0.12); color: #fff; border-color: #fff; } /* Tap targets ≥44px (WCAG 2.5.5); inline-flex keeps the active bottom border */ .nav a.navlink, .nav .login-mock { display: inline-flex; align-items: center; gap: 6px; min-height: 44px; } /* Site search. Desktop ≥1025px: VISIBLE quiet field — NN/g magnifying-glass- icon research: icon-only search reduces discoverability/usage; a visible input is recommended when search is an important task (content-heavy portal, spec-required search). Chrome stays whisper-quiet: hairline on navy. Flexes 140-200px (only it and the spacer flex — one-row invariant). */ .nav-search { position: relative; display: flex; align-items: center; flex: 0 1 200px; min-width: 140px; margin-left: 6px; } .nav-search .search-ico { left: 11px; width: 14px; height: 14px; color: rgba(255, 255, 255, 0.7); } .nav-search input { width: 100%; min-width: 0; font: inherit; font-size: var(--fs-s); color: #fff; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.45); /* 3.55:1 vs Galaxy — WCAG 1.4.11 */ border-radius: var(--radius-pill); padding: 7px 14px 7px 32px; } .nav-search input::placeholder { color: rgba(255, 255, 255, 0.65); } /* 6.9:1 */ .nav-search input:focus { border-color: #fff; } /* Autocomplete panel: never narrower than the field, wide enough for a typical title in ≤2 lines (Baymard autocomplete; DocSearch ~500px) */ .nav-search .suggest { left: auto; right: 0; width: min(480px, calc(100vw - 2 * var(--gutter))); top: calc(100% + 10px); } /* ≤1024px replacement: a 44px magnifier
reveals a search row under the header (NN/g mobile-search pattern) — see the menu media block. */ .search-pop { display: none; } .search-pop summary { display: inline-flex; align-items: center; justify-content: center; min-width: 44px; min-height: 44px; color: #fff; } .search-pop form { position: absolute; left: 0; right: 0; top: 100%; display: flex; justify-content: center; gap: 8px; padding: 12px var(--gutter) 16px; background: var(--galaxy); border-top: 1px solid rgba(255, 255, 255, 0.15); box-shadow: var(--shadow-lifted); } .search-pop input { flex: 0 1 560px; min-width: 0; font: inherit; color: var(--text); background: #fff; border: 1px solid transparent; border-radius: var(--radius-pill); padding: 10px 16px; } .search-pop input:focus { border-color: var(--azure); } .search-pop .btn { padding: 10px 18px; } .search-pop .suggest { left: max(var(--gutter), 50vw - 330px); right: max(var(--gutter), 50vw - 330px); top: calc(100% + 4px); } @media (max-width: 1024px) { .nav > nav { display: none; } .nav-search { display: none; } /* field is desktop-only (NN/g tiers) */ .search-pop { display: block; } /* magnifier reveal takes over ≤1024px */ .menu { display: block; } .menu summary { display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 10px; margin-left: 6px; color: #fff; font-weight: 500; white-space: nowrap; } .menu nav { position: absolute; left: 0; right: 0; top: 100%; display: flex; flex-direction: column; padding: 6px var(--gutter) 14px; background: var(--galaxy); border-top: 1px solid rgba(255, 255, 255, 0.15); box-shadow: var(--shadow-lifted); } .menu nav a { display: flex; align-items: center; min-height: 44px; color: #fff; font-weight: 500; text-decoration: none; border-bottom: 1px solid rgba(255, 255, 255, 0.12); } .menu nav a:last-child { border-bottom: 0; } .menu nav a[aria-current="page"] { font-weight: 700; } } @media (max-width: 640px) { .brand .brand-sub { display: none; } } @media (max-width: 480px) { .brand img { height: 22px; } .menu summary .menu-label { display: none; } .menu summary { min-width: 44px; justify-content: center; padding: 0; margin-left: 2px; } } @media (max-width: 360px) { .nav { padding: 0 14px; gap: 2px; } .brand { margin-right: 4px; } /* logo stays 22px — 22px tall ≈ 105px wide, brand minimum is 100px digital */ .nav .login-mock { padding: 7px 12px; } } /* External-link marker — SVG arrow via mask, inherits currentColor */ .external::after { content: ""; display: inline-block; width: 0.72em; height: 0.72em; margin-left: 3px; background: currentColor; -webkit-mask: var(--ico-ext) center / contain no-repeat; mask: var(--ico-ext) center / contain no-repeat; } /* Hero (home only) — image-led: LST-1 under the night sky, full-bleed beneath a Galaxy scrim (--hero-scrim; worst-case AA computed in DESIGN.md). With motion allowed a Cherenkov/Azure glow layer drifts above the photo (see motion block). */ .hero-band { background: var(--hero-scrim), url("/brand/hero.jpg") center / cover, var(--galaxy); color: #fff; padding: var(--space-band) 0; position: relative; /* NO overflow:hidden here — it would clip the search-suggest dropdown. The animated glow is clipped by its own .band-bg frame instead. */ } .band-bg { position: absolute; inset: 0; overflow: hidden; pointer-events: none; } .hero { position: relative; } /* content above the animated glow layer */ .hero h1 { font-size: var(--fs-display); margin: 0 0 12px; } .hero p { color: var(--cherenkov); font-size: var(--fs-l); max-width: 62ch; margin: 0; } /* subtitle per BRAND D.3.4 */ .eyebrow { color: var(--on-galaxy-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.14em; font-size: var(--fs-xs); margin-bottom: 8px; } /* Hero CTAs — the two core scientist tasks (SPEC §3.1): one Cherenkov primary, one quiet ghost. Links styled as buttons because they navigate. */ .hero-cta { display: flex; flex-wrap: wrap; gap: var(--space-s); margin-top: var(--space-l); } a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: none; } /* Compound selector: outranks the later-defined .btn base (source-order tie would otherwise fill the ghost with Cherenkov) */ .btn.btn--ghost { background: transparent; color: #fff; border: 1px solid rgba(255, 255, 255, 0.55); } .btn.btn--ghost:hover { background: rgba(255, 255, 255, 0.12); color: #fff; border-color: #fff; } /* Section-landing head (news/search/proposals/dashboard/support) — white page, display-scale Galaxy h1 + standfirst; the navy band is reserved for / and /login. */ .page-head { padding-block: var(--space-band) var(--space-l); } .page-head h1 { color: var(--galaxy); font-size: var(--fs-h1); margin: 0 0 10px; } .page-head .standfirst { margin: 0; max-width: 62ch; } .page-head + .page { padding-block-start: 0; } /* white-on-white: head already spaces */ /* Search field (/search page) — large pill input with inline magnifier; real GET form */ .search-form { position: relative; display: flex; gap: 8px; max-width: 560px; } .search-ico { position: absolute; left: 16px; top: 50%; translate: 0 -50%; color: var(--muted); pointer-events: none; } .search-form input { flex: 1; min-width: 0; font: inherit; color: var(--text); background: #fff; border: 1px solid var(--border); border-radius: var(--radius-pill); padding: 13px 18px 13px 44px; } .search-form input:focus { border-color: var(--azure); } /* Live suggestions — plain links, Tab-accessible */ .suggest { position: absolute; top: calc(100% + 8px); left: 0; right: 0; z-index: 11; /* above the sticky header (10) — never slides under it on scroll */ list-style: none; margin: 0; padding: 8px; text-align: left; background: #fff; border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow-lifted); max-height: min(60vh, 480px); overflow: auto; /* long lists scroll on mobile */ } .suggest:empty { display: none; } .suggest li { display: flex; align-items: flex-start; gap: var(--space-s); padding: 0 12px; border-radius: calc(var(--radius) - 8px); } .suggest li:hover, .suggest li:focus-within { background: var(--moon); } /* Focused-empty state labels ("Recent" / "Latest news") — quiet, non-interactive */ li.suggest-label { color: var(--muted); font-size: var(--fs-xs); font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; padding-top: 8px; } .suggest li.suggest-label:hover { background: none; } .search-results li.suggest-label { background: none; border: 0; box-shadow: none; padding: 8px 0 0; } /* The link holds -split text nodes — it must lay out as flowing text, never as a flex row (flex would turn every fragment into its own item and stop titles from wrapping). ≥44px target comes from the block padding. */ .suggest a { flex: 1; min-width: 0; display: block; padding-block: 10px; color: var(--text); font-weight: 500; font-size: var(--fs-s); text-decoration: none; overflow-wrap: break-word; } .suggest small { margin-top: 12px; } /* date tracks the first title line */ .suggest::after { content: "Press Enter for all results"; display: block; margin-top: 4px; padding: 8px 12px 2px; border-top: 1px solid var(--border); color: var(--muted); font-size: var(--fs-xs); } /* Section header row (h2 + optional "All news →" link) */ .section-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-s); margin: var(--space-l) 0 var(--space-m); } .section-head:first-child { margin-top: 0; } .section-head h2 { margin: 0; color: var(--galaxy); font-size: var(--fs-h2); } .more { font-weight: 600; text-decoration: none; white-space: nowrap; } .more:hover { text-decoration: underline; } /* Responsive 1/2/3-column grid (news cards + service tiles); container for @container */ .grid { display: grid; gap: var(--space-m); grid-template-columns: 1fr; container-type: inline-size; } @media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } } @media (min-width: 940px) { .grid { grid-template-columns: repeat(3, 1fr); } } /* Subgrid: cover/title/desc/meta rows align across each card row */ @supports (grid-template-rows: subgrid) { .grid > .card:not(.card--featured, .tile) { display: grid; grid-template-rows: subgrid; grid-row: span 4; gap: 0; } .grid > .card:not(.card--featured, .tile) .body { display: grid; grid-template-rows: subgrid; grid-row: span 3; gap: 10px; } } /* News cards — image + clamped title/description +