@@ -1,7 +1,8 @@
/* ==========================================================================
CTAO Science Portal — design tokens per the official CTAO Brand Guidelines
(April 2024): colors D.2, typography D.3 (Inter body / Space Grotesk display),
flash D.4. All look-and-feel lives in the tokens below; WCAG 2.1 AA contrast.
(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.
========================================================================== */
@@ -9,9 +10,9 @@
color-scheme : light ;
/* Brand palette — hex values straight from the brand book */
--galaxy : #00004a ; /* Galaxy Blue — headings, header/footer bands */
--cherenkov : #00e4d8 ; /* Cherenkov Blue — accents, flash, CTAs; NOT for small text on light */
--cherenkov : #00e4d8 ; /* Cherenkov Blue — primary CTA, flash, hero subtitle, logo cluster ONLY */
--moon : #f5f5f5 ; /* Moon Gray — surfaces */
--azure : #007aff ; /* Cosmic Azure — brand hyperlink hue (large/ UI only on light) */
--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 */
@@ -19,35 +20,58 @@
--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 ) ; /* chip/badge fill */
--tint-cherenkov : color-mix ( in srgb , var ( - - cherenkov ) 16 % , transparent ) ; /* <mark> 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 & layout */
--font-body : "Inter" , "Inter Fallback" , Arial , system-ui , sans-serif ; /* BRAND D.3.1 */
--font-display : "Space Grotesk" , var ( - - font - body ) ; /* BRAND D.3.2 — headlines only */
/* 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.8125 rem ; /* chips, meta, badges, labels */
--fs-s : 0.9 rem ; /* secondary text, nav, forms, footer */
--fs-l : 1.125 rem ; /* card titles, standfirst, panel titles */
--fs-xl : 1.5 rem ; /* prose h2, featured title, auth h1 */
--fs-h2 : clamp ( 1.75 rem , 3.5 vw , 2.5 rem ) ; /* section heads */
--fs-h1 : clamp ( 2.25 rem , 5 vw , 3.5 rem ) ; /* page/article headlines */
--fs-display : clamp ( 2.5 rem , 7 vw , 4.75 rem ) ; /* home hero only */
/* ctao.org uses generous rounding (15/30/60px tokens) and pill buttons */
--radius : 16 px ;
--radius-pill : 999 px ;
--maxw : 1120 px ;
/* Fluid spacing scale (ctao.org rhythm: sections breathe, mobile stays compact).
/* 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 ( 18 px , 4 vw , 28 px ) ; /* page side padding */
--space-s : 12 px ;
--space-m : 20 px ;
--space-m : 24 px ;
--space-l : clamp ( 24 px , 2 vw + 16 px , 40 px ) ;
--space-xl : clamp ( 40 px , 3 vw + 24 px , 72 px ) ; /* section rhythm */
--space-band : clamp ( 48 px , 5 vw + 28 px , 96 px ) ; /* hero / auth ban d padding */
/* Elevation — navy-tinted; the ONLY two shadows allowed in this codebase */
--shadow-ambient : 0 1 px 2 px rgba ( 0 , 0 , 74 , 0.05 ) , 0 4 px 14 px rgba ( 0 , 0 , 74 , 0.07 ) ;
--shadow-lifted : 0 4 px 10 px rgba ( 0 , 0 , 74 , 0.08 ) , 0 18 px 44 px rgba ( 0 , 0 , 74 , 0.18 ) ;
/* Nebula glow (BRAND D.5) — one stack for hero + auth bands, static AND animated.
Alphas are contrast ceilings (DESIGN.md): Cherenkov ≤.38, Azure ≤.35 keep the
Cherenkov subtitle/eyebrow ≥4.5:1 even with a blob core right under the text.
Cherenkov and Azure cores sit in opposite corners — cores never meet. */
--space-xl : clamp ( 56 px , 7 vw , 120 px ) ; /* section rhythm */
--space-band : clamp ( 72 px , 10 vw , 160 px ) ; /* hero / auth / landing-hea d padding */
/* Elevation — navy-tinted, soft/large/low-alpha ; the ONLY two shadows allowed */
--shadow-ambient : 0 1 px 2 px rgba ( 0 , 0 , 74 , 0.04 ) , 0 8 px 24 px rgba ( 0 , 0 , 74 , 0.06 ) ;
--shadow-lifted : 0 2 px 6 px rgba ( 0 , 0 , 74 , 0.05 ) , 0 24 px 48 px rgba ( 0 , 0 , 74 , 0.14 ) ;
/* Motion — ONE hover language everywhere (ctao.org: quiet .25s fades) */
--dur : 0.25 s ;
--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 */
@@ -73,7 +97,7 @@
}
* { box-sizing : border-box ; }
html { -webkit- text-size-adjust : 100 % ; scroll-padding-top : 84 px ; /* sticky header + anchor jumps */ }
html { -webkit- text-size-adjust : 100 % ; scroll-padding-top : 76 px ; /* 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
@@ -84,11 +108,14 @@ body {
font-family : var ( - - font - body ) ;
background : #fff ;
color : var ( - - text ) ;
line-height : 1.65 ;
line-height : 1.5 ;
min-height : 100 dvh ;
}
h1 , h2 { font-family : var ( - - font - display ) ; letter-spacing : -0.01 em ; }
/* 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.025 em ; 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 ; }
@@ -97,6 +124,10 @@ h1, h2, h3 { overflow-wrap: break-word; }
/* Inline SVG icons — stroke inherits currentColor */
. ico { width : 18 px ; height : 18 px ; fill : none ; stroke : currentColor ; stroke-width : 2 ; stroke-linecap : round ; flex : none ; }
/* All <summary> 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 : 3 px ; }
a : hover { color : var ( - - indigo ) ; }
/* Placeholder links (<a> without href) don't pretend to be clickable */
@@ -120,7 +151,8 @@ img { max-width: 100%; height: auto; display: block; }
}
. container { max-width : var ( - - maxw ) ; margin : 0 auto ; padding : 0 var ( - - gutter ) ; }
/* Surface rhythm: white (editorial) → Moon Gray (service/utility bands) → Galaxy (hero/footer) */
/* 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
@@ -128,69 +160,99 @@ img { max-width: 100%; height: auto; display: block; }
. flash { width : 12 px ; height : 12 px ; color : var ( - - cherenkov ) ; flex : none ; }
/* Header — Galaxy Blue band, reverse (white) logo per BRAND B.1.2 */
/* 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 ; }
. nav { display : flex ; align-items : center ; gap : 4 px ; min-height : 64 px ; flex-wrap : nowrap ; }
@ 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 ( 14 px ) ; backdrop-filter : blur ( 14 px ) ;
}
}
. nav { display : flex ; align-items : center ; gap : 4 px ; min-height : 56 px ; flex-wrap : nowrap ; }
. nav > nav { display : flex ; align-items : center ; gap : 4 px ; }
/* One-row guarantee: only the search field and the spacer flex — everything
else keeps its natural size (links are single words, they can never wrap) */
/* 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 — width math in DESIGN.md):
≥1100px full nav + inline search + sub-brand · 1025– 1099px sub-brand hidden ·
≤1024px disclosure menu + magnifier reveal (both native <details>, CSS-only) ·
≤480px icon-only menu button, smaller logo · ≤360px compact paddings */
@ media ( max-width : 1099px ) { . brand . brand-sub { display : 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 : 10 px ; margin-right : 10 px ; }
. brand { display : flex ; align-items : center ; gap : 10 px ; margin-right : 10 px ; text-decoration : none ; }
. brand img { height : 26 px ; width : auto ; }
. brand . brand-sub {
color : var ( - - cherenkov ) ; font-size : 0.78 rem ; font-weight : 500 ;
border-left : 1 px solid rgba ( 255 , 255 , 255 , 0.25 ) ; padding-left : 10 px ;
color : var ( - - cherenkov ) ; font-size : var ( - - fs - xs ) ; font-weight : 500 ;
border-left : 1 px solid rgba ( 255 , 255 , 255 , 0.25 ) ; padding-left : 10 px ;
}
. nav . spacer { flex : 1 ; }
. nav a . navlink {
color : var ( - - on - galaxy - muted ) ; font-weight : 500 ; font-size : 0.95 rem ;
color : var ( - - on - galaxy - muted ) ; font-weight : 500 ; font-size : var ( - - fs - s ) ;
padding : 10 px 10 px ; text-decoration : none ; border-bottom : 2 px solid transparent ;
}
. nav a . navlink : hover { color : #fff ; }
. nav a . navlink [ aria-current = "page" ] { color : #fff ; border-bottom-color : var ( - - cherenkov ) ; }
/* AAI sign-in entry — leads to the /login mock page */
. 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 : var ( - - galaxy ) ; background : var ( - - cherenkov ) ; font : inherit ; font-weight : 600 ;
border : 0 ; border-radius : var ( - - radius - pill ) ; padding : 8 px 18 px ; margin-left : 6 px ;
text-decoration : none ; white-space : nowrap ;
color : #fff ; background : transparent ; font : inherit ; font-weight : 600 ; font-size : var ( - - fs - s ) ;
border : 1 px solid rgba ( 255 , 255 , 255 , 0.55 ) ; border-radius : var ( - - radius - pill ) ;
padding : 7 px 18 px ; margin-left : 6 px ; text-decoration : none ; white-space : nowrap ;
}
. nav . login-mock : hover { background : var ( - - cherenkov - hover ) ; color : var ( - - galaxy ) ; }
. 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 : 6 px ; min-height : 44 px ;
}
/* Header search (d esktop) — compact pill in the utility cluster; a plain GET
form to /search (works without JS), suggestions reuse the shared .suggest
dropdown. Flexes 130– 180px so the row can never overflow (width math: DESIGN.md). */
. nav-search { position : relative ; display : flex ; align-items : center ; flex : 0 1 180 px ; min-width : 130 px ; margin-left : 6 px ; }
. nav-search . search-ico { left : 12 px ; width : 15 px ; height : 15 px ; }
/* Site search. D esktop ≥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 200 px ; min-width : 140 px ; margin-left : 6 px ; }
. nav-search . search-ico { left : 11 px ; width : 14 px ; height : 14 px ; color : rgba ( 255 , 255 , 255 , 0.7 ) ; }
. nav-search input {
width : 100 % ; min-width : 0 ; font : inherit ; font-size : 0.9 rem ; color : var ( - - text ) ;
background : #fff ; border : 1 px solid transparent ; border-radius : var ( - - radius - pill ) ;
padding : 8 px 14 px 8 px 34 px ;
width : 100 % ; min-width : 0 ; font : inherit ; font-size : var ( - - fs - s ) ; color : #fff ;
background : rgba ( 255 , 255 , 255 , 0.08 ) ; border : 1 px solid rgba ( 255 , 255 , 255 , 0.45 ) ; /* 3.55:1 vs Galaxy — WCAG 1.4.11 */
border-radius : var ( - - radius - pill ) ; padding : 7 px 14 px 7 px 32 px ;
}
. nav-search input : focus { border-color : var ( - - azure ) ; }
. nav-search input :: placeholder { color : rgba ( 255 , 255 , 255 , 0.65 ) ; } /* 6.9:1 */
. nav-search input : focus { border-color : #fff ; }
. nav-search . suggest { left : auto ; right : 0 ; width : min ( 380 px , 92 vw ) ; top : calc ( 100 % + 10 px ) ; }
/* ≤1024px replacement: magnifier <details> reveal — see the menu media block */
/* ≤1024px replacement: a 44px magnifier <details> 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 : 44 px ; min-height : 44 px ; color : #fff ;
}
. search-pop form {
position : absolute ; left : 0 ; right : 0 ; top : 100 % ;
display : flex ; justify-content : center ; gap : 8 px ; padding : 12 px var ( - - gutter ) 16 px ;
background : var ( - - galaxy ) ; border-top : 1 px solid rgba ( 255 , 255 , 255 , 0.15 ) ;
box-shadow : var ( - - shadow - lifted ) ;
}
. search-pop input {
flex : 0 1 560 px ; min-width : 0 ; font : inherit ; color : var ( - - text ) ;
background : #fff ; border : 1 px solid transparent ; border-radius : var ( - - radius - pill ) ;
padding : 10 px 16 px ;
}
. search-pop input : focus { border-color : var ( - - azure ) ; }
. search-pop . btn { padding : 10 px 18 px ; }
. search-pop . suggest {
left : max ( var ( - - gutter ) , 50 vw - 330 px ) ; right : max ( var ( - - gutter ) , 50 vw - 330 px ) ;
top : calc ( 100 % + 4 px ) ;
}
@ media ( max-width : 1024px ) {
. nav > nav { display : none ; }
. menu { display : block ; }
. menu summary {
display : inline-flex ; align-items : center ; gap : 8 px ; min-height : 44 px ;
padding : 0 10 px ; margin-left : 6 px ; color : #fff ; font-weight : 500 ;
cursor : pointer ; list-style : none ; white-space : nowrap ;
padding : 0 10 px ; margin-left : 6 px ; color : #fff ; font-weight : 500 ; white-space : nowrap ;
}
. menu summary :: -webkit-details-marker { display : none ; }
. menu nav {
position : absolute ; left : 0 ; right : 0 ; top : 100 % ;
display : flex ; flex-direction : column ; padding : 6 px var ( - - gutter ) 14 px ;
@@ -203,32 +265,9 @@ img { max-width: 100%; height: auto; display: block; }
border-bottom : 1 px solid rgba ( 255 , 255 , 255 , 0.12 ) ;
}
. menu nav a : last-child { border-bottom : 0 ; }
. menu nav a [ aria-current = "page" ] { color : var ( - - cherenkov ) ; }
/* Magnifier icon-button reveals a full-width search row under the header
(visible search beats search buried in the menu — NN/g mobile-search).
Same native-<details> panel pattern as the menu. */
. nav-search { display : none ; }
. search-pop { display : block ; }
. search-pop summary {
display : inline-flex ; align-items : center ; justify-content : center ;
min-width : 44 px ; min-height : 44 px ; color : #fff ; cursor : pointer ; list-style : none ;
}
. search-pop summary :: -webkit-details-marker { display : none ; }
. search-pop form {
position : absolute ; left : 0 ; right : 0 ; top : 100 % ;
display : flex ; gap : 8 px ; padding : 10 px var ( - - gutter ) 14 px ;
background : var ( - - galaxy ) ; border-top : 1 px solid rgba ( 255 , 255 , 255 , 0.15 ) ;
box-shadow : var ( - - shadow - lifted ) ;
}
. search-pop input {
flex : 1 ; min-width : 0 ; font : inherit ; color : var ( - - text ) ;
background : #fff ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius - pill ) ;
padding : 10 px 16 px ;
}
. search-pop input : focus { border-color : var ( - - azure ) ; }
. search-pop . btn { padding : 10 px 18 px ; }
. search-pop . suggest { left : 10 px ; right : 10 px ; top : calc ( 100 % + 4 px ) ; }
. 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 : 22 px ; }
. menu summary . menu-label { display : none ; }
@@ -237,8 +276,8 @@ img { max-width: 100%; height: auto; display: block; }
@ media ( max-width : 360px ) {
. nav { padding : 0 14 px ; gap : 2 px ; }
. brand { margin-right : 4 px ; }
. brand img { height : 20 px ; }
. nav . login-mock { padding : 8 px 12 px ; }
/* logo stays 22px — 22px tall ≈ 105px wide, brand minimum is 100px digital */
. nav . login-mock { padding : 7 px 12 px ; }
}
/* External-link marker — SVG arrow via mask, inherits currentColor */
. external :: after {
@@ -248,34 +287,37 @@ img { max-width: 100%; height: auto; display: block; }
mask : var ( - - ico - ext ) center / contain no-repeat ;
}
/* Hero — navy band with a nebula glow (BRAND D.5). The static gradient below is
the reduced-motion fallback; with motion allowed, the glow moves to an animated
pseudo-layer (see the motion block at the end of this file ). */
/* 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 ( - - nebula ) , var ( - - galaxy ) ;
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 nebula is clipped by its own .band-bg frame instead. */
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 nebula layer */
. hero h1 { font-size : clamp ( 2 rem , 6 vw , 3.6 rem ) ; margin : 0 0 10 px ; line-height : 1.12 ; font-weight : 700 ; }
. hero p { color : var ( - - cherenkov ) ; font-size : 1.08 rem ; max-width : 62 ch ; margin : 0 ; } /* subtitle per BRAND D.3.4 */
. hero { position : relative ; } /* content above the animated glow layer */
. hero h1 { font-size : var ( - - fs - display ) ; margin : 0 0 12 px ; }
. hero p { color : var ( - - cherenkov ) ; font-size : var ( - - fs - l ) ; max-width : 62 ch ; margin : 0 ; } /* subtitle per BRAND D.3.4 */
. eyebrow {
color : var ( - - on - galaxy - muted ) ; font-weight : 600 ; text-transform : uppercase ;
letter-spacing : 0.14 em ; font-size : 0.72 rem ; margin-bottom : 8 px ;
letter-spacing : 0.14 em ; font-size : var ( - - fs - xs ) ; margin-bottom : 8 px ;
}
/* 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 - m ) ; }
. 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 : 6 px ; text-decoration : none ; }
. btn--ghost { background : transparent ; color : #fff ; border : 1 px solid rgba ( 255 , 255 , 255 , 0.55 ) ; }
. btn--ghost : hover { background : rgba ( 255 , 255 , 255 , 0.1 ) ; color : #fff ; }
/* One-line latest-news teaser under the CTAs (editorial touch, no widgets) */
. hero . hero-teaser { margin-top : var ( - - space - m ) ; font-size : 0.9 rem ; color : var ( - - on - galaxy - muted ) ; max-width : none ; }
. hero . hero-teaser a { color : #fff ; text-underline-offset : 3 px ; }
. hero . hero-teaser a : hover { color : var ( - - cherenkov ) ; }
. 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 : var ( - - space - band ) 0 var ( - - space - l ) ; }
. page-head h1 { color : var ( - - galaxy ) ; font-size : var ( - - fs - h1 ) ; margin : 0 0 10 px ; }
. page-head . standfirst { margin : 0 ; max-width : 62 ch ; }
. page-head + . page { padding-top : 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 : 8 px ; max-width : 560 px ; }
@@ -286,7 +328,7 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
border-radius : var ( - - radius - pill ) ; padding : 13 px 18 px 13 px 44 px ;
}
. search-form input : focus { border-color : var ( - - azure ) ; }
/* Live suggestions under the hero search — plain links, Tab-accessible */
/* Live suggestions — plain links, Tab-accessible */
. suggest {
position : absolute ; top : calc ( 100 % + 8 px ) ; left : 0 ; right : 0 ;
z-index : 11 ; /* above the sticky header (10) — never slides under it on scroll */
@@ -303,14 +345,14 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
stop titles from wrapping). ≥44px target comes from the block padding. */
. suggest a {
flex : 1 ; min-width : 0 ; display : block ; padding-block : 10 px ;
color : var ( - - text ) ; font-weight : 500 ; font-size : 0.94 rem ; text-decoration : none ;
color : var ( - - text ) ; font-weight : 500 ; font-size : var ( - - fs - s ) ; text-decoration : none ;
overflow-wrap : break-word ;
}
. suggest small { margin-top : 12 px ; } /* date chip tracks the first title line */
. suggest small { margin-top : 12 px ; } /* date tracks the first title line */
. suggest :: after {
content : "Press Enter for all results" ; display : block ; margin-top : 4 px ;
padding : 8 px 12 px 2 px ; border-top : 1 px solid var ( - - border ) ;
color : var ( - - muted ) ; font-size : 0.78 rem ;
color : var ( - - muted ) ; font-size : var ( - - fs - xs ) ;
}
/* Section header row (h2 + optional "All news →" link) */
@@ -319,7 +361,7 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
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 : clamp ( 1.3 rem , 2.5 vw , 1.6 rem ) ; }
. 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 ; }
@@ -333,28 +375,22 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
. grid > . card : not ( . card--featured , . tile ) . body { display : grid ; grid-template-rows : subgrid ; grid-row : span 3 ; gap : 10 px ; }
}
/* News cards — image + category + clamped title/description + <time> meta;
whole card clickable via stretched link (research: NN/g & card-UI guides 2025). */
/* News cards — image + clamped title/description + <time> meta; whole card
clickable via stretched link. Chrome is single-signal: hairline on white,
borderless on Moon (white-on-moon separates); hover = one elevate gesture. */
. card {
position : relative ; background : #fff ; border : 1 px solid var ( - - border ) ;
border-radius : var ( - - radius ) ; overflow : hidden ; box-shadow : var ( - - shadow - ambient ) ;
display : flex ; flex-direction : column ;
display : flex ; flex-direction : column ; line-height : 1.45 ;
}
. card : hover , . card : focus-within { border-color : var ( - - azure ) ; box-shadow : var ( - - shadow - lifted ) ; }
. band--moon : is ( . card , . panel ) { border-color : transparent ; }
. card : hover , . card : focus-within { box-shadow : var ( - - shadow - lifted ) ; }
/* Visible focus ring for the whole-card stretched link (WCAG 2.4.7) */
. card : focus-within { box-shadow : 0 0 0 2 px var ( - - azure ) , var ( - - shadow - lifted ) ; }
. card . cover { width : 100 % ; aspect-ratio : 16 / 9 ; object-fit : cover ; }
. card . body { padding : var ( - - space - m ) ; display : flex ; flex-direction : column ; gap : 10 px ; flex : 1 ; }
/* Cover-less cards get a Cherenkov top accent instead of an image */
. card : not ( : has ( . cover ) ) . body { border-top : 3 px solid var ( - - cherenkov ) ; }
. cat {
display : inline-block ; align-self : flex-start ; font-size : 0.72 rem ; text-transform : uppercase ;
letter-spacing : 0.08 em ; font-weight : 600 ;
color : var ( - - galaxy ) ; background : var ( - - tint - cherenkov ) ;
padding : 4 px 10 px ; border-radius : var ( - - radius - pill ) ;
}
. card h3 {
margin : 2 px 0 0 ; font-size : 1.1 rem ; line-height : 1.35 ; font-family : var ( - - font - body ) , sans-serif ;
margin : 2 px 0 0 ; font-size : var ( - - fs - l ) ; line-height : 1.3 ; font-family : var ( - - font - body ) , sans-serif ;
display : -webkit- box ; -webkit- line-clamp : 3 ; line-clamp : 3 ; -webkit- box-orient : vertical ; overflow : hidden ;
}
. card h3 a { color : var ( - - galaxy ) ; text-decoration : none ; }
@@ -363,21 +399,20 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
. card h3 a :: before { content : "" ; position : absolute ; inset : 0 ; }
. card h3 a : hover { color : var ( - - link ) ; }
. card . desc {
margin : 0 ; color : var ( - - muted ) ; font-size : 0.94 rem ;
margin : 0 ; color : var ( - - muted ) ; font-size : var ( - - fs - s ) ;
display : -webkit- box ; -webkit- line-clamp : 2 ; line-clamp : 2 ; -webkit- box-orient : vertical ; overflow : hidden ;
}
. card . meta { margin-top : auto ; color : var ( - - muted ) ; font-size : 0.8 rem ; }
. card . meta { margin-top : auto ; color : var ( - - muted ) ; font-size : var ( - - fs - xs ) ; }
/* Lead story — larger, splits when the grid itself is wide enough (container query) */
@ container ( min-width : 640px ) {
. card--featured { grid-column : 1 / -1 ; display : grid ; grid-template-columns : 3 fr 2 fr ; }
. card--featured . cover { height : 100 % ; aspect-ratio : auto ; }
. card--featured . body { padding : var ( - - space - l ) ; justify-content : center ; }
. card--featured h3 { font-size : 1.6 rem ; font-family : var ( - - font - display ) , sans-serif ; -webkit- line-clamp : 3 ; line-clamp : 3 ; }
. card--featured h3 { font-size : var ( - - fs - xl ) ; font-family : var ( - - font - display ) , sans-serif ; font-weight : 500 ; -webkit- line-clamp : 3 ; line-clamp : 3 ; }
. card--featured . desc { -webkit- line-clamp : 3 ; line-clamp : 3 ; }
}
/* Pagination (news archive) — prev/next + windowed page numbers; ≥44px targets.
Pattern mined from AstroPaper/Astro blog templates, numbers added for 9 pages. */
/* Pagination (news archive) — prev/next + windowed page numbers; ≥44px targets. */
. pagination {
display : flex ; align-items : center ; justify-content : center ; flex-wrap : wrap ;
gap : 4 px var ( - - space - s ) ; margin-top : var ( - - space - xl ) ;
@@ -388,7 +423,7 @@ a.btn { display: inline-flex; align-items: center; gap: 6px; text-decoration: no
min-width : 44 px ; min-height : 44 px ; padding : 0 12 px ;
border-radius : var ( - - radius - pill ) ; text-decoration : none ; font-weight : 500 ; color : var ( - - text ) ;
}
a . page-link : hover { background : var ( - - moon ) ; color : var ( - - indigo ) ; }
a . page-link : hover { background : var ( - - moon ) ; }
. page-link [ aria-current = "page" ] { background : var ( - - galaxy ) ; color : #fff ; }
. page-step { font-weight : 600 ; }
a . page-step { color : var ( - - link ) ; }
@@ -396,80 +431,96 @@ span.page-step { color: var(--muted); } /* disabled end stop — non-interactive
. page-gap { display : inline-flex ; align-items : center ; justify-content : center ; min-width : 24 px ; min-height : 44 px ; color : var ( - - muted ) ; }
/* Service tiles — card tokens; stroke glyph above an always-visible label
(NN/g: icons differentiate and speed scanning only when labelled) */
(NN/g: icons differentiate and speed scanning only when labelled).
Planned (not-yet-available) tiles are subdued: muted, no link, no chrome —
availability lives in the description text ("— planned"). */
. tile { padding : var ( - - space - m ) ; gap : 8 px ; }
. tile . ico { width : 26 px ; height : 26 px ; stroke-width : 1.75 ; stroke-linejoin : round ; color : var ( - - galaxy ) ; }
. tile h3 { margin : 0 ; }
. tile . badge-mock , . tile . badge-ext { margin : auto 0 0 ; align-self : flex-start ; }
. tile--planned : is ( h3 , . ico ) { color : var ( - - muted ) ; }
/* Article — 70ch column; on ≥1200px a sticky "On this page" rail sits to the right */
. article-layout { display : grid ; grid-template-columns : minmax ( 0 , 70 ch ) ; }
. article { padding : var ( - - space - l ) 0 var ( - - space - xl ) ; max-width : 70 ch ; }
. article . back { color : var ( - - muted ) ; font-size : 0.9 rem ; }
. article h1 {
color : var ( - - galaxy ) ; font-size : clamp ( 2 rem , 5 vw , 3 rem ) ;
margin : 14 px 0 10 px ; line-height : 1.12 ;
}
. article . back { color : var ( - - muted ) ; font-size : var ( - - fs - s ) ; }
. article h1 { color : var ( - - galaxy ) ; font-size : var ( - - fs - h1 ) ; margin : 14 px 0 10 px ; }
. article . meta {
display : flex ; flex-wrap : wrap ; align-items : center ; gap : 8 px 10 px ;
color : var ( - - muted ) ; font-size : 0.9 rem ; margin-bottom : var ( - - space - l ) ;
color : var ( - - muted ) ; font-size : var ( - - fs - s ) ; margin-bottom : var ( - - space - l ) ;
}
/* Lead image — borderless, breaking slightly out of the text column on wide
screens (editorial breakout; the column stays 70ch for reading) */
. article . cover { border-radius : var ( - - radius ) ; margin-bottom : var ( - - space - l ) ; }
@ media ( min-width : 900px ) {
. article . cover { margin-inline : -40 px ; width : calc ( 100 % + 80 px ) ; max-width : none ; }
}
. article . cover { border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ; margin-bottom : var ( - - space - l ) ; }
/* Standfirst — the description shown as the visible lead under the headline
(Guardian/BBC anatomy). Replaces the old first-prose-paragraph lead styling
so an article never shows two competing leads. */
. article . standfirst { margin : 0 0 var ( - - space - s ) ; font-size : 1.12 rem ; line-height : 1.55 ; color : var ( - - muted ) ; }
/* Article TOC (MDN/Stripe pattern, rendered only when ≥3 h2/h3): sticky right rail
on wide screens, collapsed <details> under the title otherwise. */
. toc {
background : var ( - - moon ) ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ;
padding : var ( - - space - s ) var ( - - space - m ) ; font-size : 0.92 rem ;
}
(Guardian/BBC anatomy); also the intro line under section-landing h1s. */
. standfirst { margin : 0 0 var ( - - space - s ) ; font-size : var ( - - fs - l ) ; line-height : 1.55 ; color : var ( - - muted ) ; }
/* Article TOC (Stripe/Apple-docs pattern, rendered only when ≥3 h2/h3):
quiet "On this page" — sticky hairline-rule rail on wide screens, a light
disclosure row otherwise. Links are muted, Galaxy on hover; the scrollspy
active state (Galaxy text + Cherenkov rule segment) is navigation state,
not decoration — it sits outside the one-accent budget. */
. toc { font-size : var ( - - fs - s ) ; }
. toc strong , . toc summary {
font-size : 0.72 rem ; text-transform : uppercase ; font-weight : 600 ;
font-size : var ( - - fs - xs ) ; text-transform : uppercase ; font-weight : 600 ;
letter-spacing : 0.08 em ; color : var ( - - muted ) ;
}
. toc strong { display : block ; margin-bottom : 4 px ; }
. toc summary { cursor : pointer ; padding : 6 px 0 ; }
. toc strong { display : block ; margin-bottom : 10 px ; }
. toc ol { list-style : none ; margin : 0 ; padding : 0 ; }
. toc li { margin : 8 px 0 ; }
. toc li { margin : 6 px 0 ; }
. toc . d3 { padding-left : var ( - - space - s ) ; }
. toc a { text-decoration : none ; display : inline-flex ; align-items : center ; min-height : 24 px ; }
. toc a : hover { text-decoration : underline ; }
. toc a {
color : var ( - - muted ) ; text-decoration : none ;
display : inline-flex ; align-items : center ; min-height : 24 px ; position : relative ;
}
. toc a : hover { color : var ( - - galaxy ) ; }
/* Scrollspy state (set by the [slug].astro script) — functional, not motion */
. toc a [ aria-current = "true" ] { color : var ( - - galaxy ) ; font-weight : 600 ; }
. toc--inline { margin-bottom : var ( - - space - l ) ; }
/* Inline rendering (< 1200px): light disclosure row with a chevron */
. toc--inline { border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ; padding : 4 px var ( - - space - m ) ; margin-bottom : var ( - - space - l ) ; }
. toc--inline summary { display : flex ; align-items : center ; justify-content : space-between ; gap : var ( - - space - s ) ; min-height : 40 px ; }
. toc--inline summary . ico { width : 14 px ; height : 14 px ; }
. toc--inline [ open ] summary . ico { transform : rotate ( 180 deg ) ; }
. toc--inline ol { padding-bottom : var ( - - space - s ) ; }
. toc--rail { display : none ; }
@ media ( min-width : 1200px ) {
. article-layout { grid-template-columns : minmax ( 0 , 70 ch ) 15 rem ; column-gap : var ( - - space - xl ) ; justify-content : space-between ; }
. toc--inline { display : none ; }
. toc--rail {
display : block ; position : sticky ; top : 88 px ; align-self : start ;
display : block ; position : sticky ; top : 80 px ; align-self : start ;
margin-top : calc ( var ( - - space - l ) + 44 px ) ; /* aligns with the article h1 */
max-height : calc ( 100 dvh - 112 px ) ; overflow : auto ;
max-height : calc ( 100 dvh - 104 px ) ; overflow : auto ;
border-left : 1 px solid var ( - - border ) ; padding-left : var ( - - space - m ) ;
}
/* Active rule segment: 2px Cherenkov over the hairline, tracking the link */
. toc--rail a [ aria-current = "true" ] :: before {
content : "" ; position : absolute ; left : calc ( -1 * var ( - - space - m ) - 1 px ) ;
top : 3 px ; bottom : 3 px ; width : 2 px ; background : var ( - - cherenkov ) ;
}
}
/* Long unbroken strings (URLs, identifiers) wrap instead of forcing page overflow */
. prose { overflow-wrap : break-word ; }
. prose { line-height : 1.65 ; overflow-wrap : break-word ; }
. prose a { overflow-wrap : anywhere ; }
. prose h2 { color : var ( - - galaxy ) ; margin : 34 px 0 10 px ; font-size : clamp ( 1.25 rem , 2.6 vw , 1.45 rem ) ; }
. prose h3 { color : var ( - - galaxy ) ; margin : 26 px 0 8 px ; font-size : 1.15 rem ; }
. prose h2 { color : var ( - - galaxy ) ; margin : 44 px 0 12 px ; font-size : var ( - - fs - xl ) ; }
. prose h3 { color : var ( - - galaxy ) ; margin : 30 px 0 8 px ; font-size : var ( - - fs - l ) ; }
. prose p { margin : 0 0 16 px ; }
. prose ul , . prose ol { margin : 0 0 16 px ; padding-left : 22 px ; }
. prose li { margin : 6 px 0 ; }
. prose blockquote {
margin : 22 px 0 ; padding : 12 px 20 px ; border-left : 3 px solid var ( - - cherenkov ) ;
margin : 22 px 0 ; padding : 12 px 20 px ; border-left : 3 px solid var ( - - galaxy ) ;
background : var ( - - moon ) ; color : var ( - - text ) ;
}
. prose img { border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ; }
. prose img + em , . prose p > em : only-child { color : var ( - - muted ) ; font-size : 0.85 rem ; }
. prose img + em , . prose p > em : only-child { color : var ( - - muted ) ; font-size : var ( - - fs - s ) ; }
. prose hr { border : 0 ; border-top : 1 px solid var ( - - border ) ; margin : 32 px 0 ; }
/* Wide tables scroll inside their own box, never the page (mobile guard) */
. prose table { display : block ; width : 100 % ; overflow-x : auto ; border-collapse : collapse ; margin : 0 0 16 px ; font-size : 0.94 rem ; }
. prose table { display : block ; width : 100 % ; overflow-x : auto ; border-collapse : collapse ; margin : 0 0 16 px ; font-size : var ( - - fs - s ) ; }
. prose th { text-align : left ; font-weight : 600 ; color : var ( - - galaxy ) ; }
. prose th , . prose td { padding : 10 px 12 px ; border-bottom : 1 px solid var ( - - border ) ; }
. prose thead th { border-bottom : 2 px solid var ( - - cherenkov ) ; }
. prose thead th { border-bottom : 2 px solid var ( - - galaxy ) ; }
/* Prev/next article links — sequential nav from the date-sorted collection */
. post-nav {
@@ -480,7 +531,7 @@ span.page-step { color: var(--muted); } /* disabled end stop — non-interactive
. post-nav . post-nav-prev { grid-column : 1 ; }
. post-nav . post-nav-next { grid-column : 2 ; text-align : right ; }
. post-nav small {
display : block ; color : var ( - - muted ) ; font-size : 0.72 rem ; font-weight : 600 ;
display : block ; color : var ( - - muted ) ; font-size : var ( - - fs - xs ) ; font-weight : 600 ;
text-transform : uppercase ; letter-spacing : 0.08 em ; margin-bottom : 4 px ;
}
. post-nav . post-nav-title {
@@ -497,7 +548,7 @@ span.page-step { color: var(--muted); } /* disabled end stop — non-interactive
}
. panel--narrow { max-width : 640 px ; }
. form-row { display : flex ; flex-direction : column ; gap : 6 px ; margin-bottom : 16 px ; }
. form-row label { font-size : 0.88 rem ; font-weight : 500 ; }
. form-row label { font-size : var ( - - fs - s ) ; font-weight : 500 ; }
. form-row input , . form-row select , . form-row textarea {
background : #fff ; border : 1 px solid var ( - - border ) ; border-radius : calc ( var ( - - radius ) - 4 px ) ;
color : var ( - - text ) ; padding : 11 px 12 px ; font : inherit ;
@@ -509,35 +560,33 @@ span.page-step { color: var(--muted); } /* disabled end stop — non-interactive
font : inherit ; font-weight : 600 ; cursor : pointer ;
}
. btn : hover { background : var ( - - cherenkov - hover ) ; }
. notice { color : var ( - - muted ) ; font-size : 0.85 rem ; margin-top : 10 px ; }
. notice { color : var ( - - muted ) ; font-size : var ( - - fs - s ) ; margin-top : 10 px ; }
/* Search results (list built by the inline script on /search) — card-like rows */
/* Search results (list built by the inline script on /search) — card-like rows;
same single-signal chrome as cards (borderless on Moon, shadow hover) */
. search-results { list-style : none ; margin : var ( - - space - m ) 0 0 ; padding : 0 ; max-width : 720 px ; display : grid ; gap : var ( - - space - s ) ; }
. search-results li {
display : flex ; align-items : flex-start ; gap : var ( - - space - s ) ;
background : #fff ; border : 1 px solid var ( - - border ) ; border-radius : var ( - - radius ) ;
background : #fff ; border-radius : var ( - - radius ) ;
padding : 12 px 18 px ; box-shadow : var ( - - shadow - ambient ) ;
}
. search-results li : hover { border-color : var ( - - azure ) ; box-shadow : var ( - - shadow - lifted ) ; }
. search-results li : hover { box-shadow : var ( - - shadow - lifted ) ; }
/* Title flows as inline text (never display:flex — see .suggest a note) */
. search-results a { flex : 1 ; min-width : 0 ; overflow-wrap : break-word ; text-decoration : none ; font-weight : 500 ; }
. search-results a : hover { text-decoration : underline ; }
/* Date chip s on results and suggestions */
. search-results small , . suggest small {
color : var ( - - muted ) ; white-space : nowrap ; font-size : 0.78 rem ;
background : var ( - - moon ) ; border-radius : var ( - - radius - pill ) ; padding : 2 px 10 px ;
}
. search-results small { margin-top : 3 px ; } /* optical align with the first title line */
/* Dates on results and suggestions — plain muted text, no chip chrome */
. search-results small , . suggest small { color : var ( - - muted ) ; white-space : nowrap ; font-size : var ( - - fs - xs ) ; }
. search-results small { margin-top : 5 px ; } /* optical align with the first title line */
/* Matched-term highlight in results/suggestions (Starlight search pattern) —
brand tint only , no new hues */
functional state, brand tint, no new hues */
mark { background : var ( - - tint - cherenkov ) ; color : inherit ; border-radius : calc ( var ( - - radius ) - 12 px ) ; padding : 0 2 px ; }
/* Mock/status badges — every mock stays labelled (REQUIREMENTS.md §5), but the
Demo phase banner is loud once, so per-element badges only whisper: quiet
hairline pill, muted text (7.1:1 on Moon), sentence case, Galaxy dot on mocks . */
/* Mock/status badges — whisper-quiet hairline pill, ONLY where a mock
interaction actually happens (/proposals, /dashboard, /login, /support head);
REQUIREMENTS §5 honesty lives at the interaction point, not on every tile . */
. badge-mock , . badge-ext {
display : inline-flex ; align-items : center ; gap : 6 px ; max-width : 100 % ;
font-size : 0.75 rem ; font-weight : 500 ; color : var ( - - muted ) ;
font-size : var ( - - fs - xs ) ; font-weight : 500 ; color : var ( - - muted ) ;
background : var ( - - moon ) ; border : 1 px solid var ( - - border ) ;
border-radius : var ( - - radius - pill ) ; padding : 2 px 10 px ; margin-bottom : 14 px ;
}
@@ -546,43 +595,49 @@ mark { background: var(--tint-cherenkov); color: inherit; border-radius: calc(va
/* Dashboard mock — aggregation panels with status pills */
. dash-grid { display : grid ; gap : var ( - - space - m ) ; grid-template-columns : 1 fr ; margin-top : var ( - - space - s ) ; }
@ media ( min-width : 940px ) { . dash-grid { grid-template-columns : repeat ( 2 , 1 fr ) ; } }
. panel-title { color : var ( - - galaxy ) ; font-size : 1.1 rem ; margin : 0 0 var ( - - space - s ) ; font-family : var ( - - font - body ) , sans-serif ; font-weight : 600 ; }
. panel-title { color : var ( - - galaxy ) ; font-size : var ( - - fs - l ) ; margin : 0 0 var ( - - space - s ) ; font-family : var ( - - font - body ) , sans-serif ; font-weight : 600 ; }
. rows { list-style : none ; margin : 0 ; padding : 0 ; }
. rows li {
display : flex ; align-items : baseline ; gap : 10 px ; flex-wrap : wrap ;
padding : 10 px 0 ; border-bottom : 1 px solid var ( - - border ) ; font-size : 0.92 rem ;
padding : 10 px 0 ; border-bottom : 1 px solid var ( - - border ) ; font-size : var ( - - fs - s ) ;
}
. rows li : last-child { border-bottom : 0 ; }
. row-id { color : var ( - - muted ) ; font-size : 0.8 rem ; min-width : 7.5 em ; }
. row-id { color : var ( - - muted ) ; font-size : var ( - - fs - xs ) ; min-width : 7.5 em ; }
. row-main { flex : 1 ; min-width : 12 em ; }
. row-date { color : var ( - - muted ) ; font-size : 0.8 rem ; }
. row-date { color : var ( - - muted ) ; font-size : var ( - - fs - xs ) ; }
/* Status pills — neutral chrome; the label text carries the state */
. pill {
font-size : 0.72 rem ; font-weight : 600 ; letter-spacing : 0.05 em ; text-transform : uppercase ;
font-size : var ( - - fs - xs ) ; font-weight : 600 ; letter-spacing : 0.05 em ; text-transform : uppercase ;
padding : 3 px 10 px ; border-radius : var ( - - radius - pill ) ; border : 1 px solid var ( - - border ) ;
}
. pill--ok { color : var ( - - galaxy ) ; background : var ( - - tint - cherenkov ) ; }
. pill--review { color : var ( - - galaxy ) ; background : var ( - - moon ) ; }
. pill--ok { color : var ( - - galaxy ) ; background : var ( - - moon ) ; }
. pill--review { color : var ( - - text ) ; background : #fff ; }
. pill--draft { color : var ( - - muted ) ; background : #fff ; }
/* Auth (login mock) — nebula backdrop + floating card, modern auth pattern */
/* Auth (login mock) — nebula backdrop + floating card; the ONLY navy band
besides the home hero */
. auth-band {
min-height : calc ( 100 dvh - 64 px ) ; position : relative ;
min-height : calc ( 100 dvh - 56 px ) ; position : relative ;
display : grid ; place-items : center ; padding : var ( - - space - band ) var ( - - gutter ) ;
background : var ( - - nebula ) , var ( - - galaxy ) ;
}
. auth-card {
position : relative ; background : #fff ; border-radius : calc ( var ( - - radius ) + 8 px ) ;
padding : clamp ( 24 px , 5 vw , 40 px ) clamp ( 20 px , 4.5 vw , 36 px ) ; max-width : 440 px ; width : 100 % ;
padding : var ( - - space - l ) ; max-width : 440 px ; width : 100 % ;
box-shadow : var ( - - shadow - lifted ) ;
}
. auth-logo { height : 24 px ; width : auto ; margin-bottom : var ( - - space - m ) ; }
. auth-card h1 { color : var ( - - galaxy ) ; margin : 6 px 0 10 px ; font-size : 1.8 rem ; }
. auth-card . muted { color : var ( - - muted ) ; font-size : 0.95 rem ; }
/* Whisper badge sits top-right, off the reading flow (back in flow ≤480px
where it would collide with the logo) */
. auth-card . badge-mock { position : absolute ; top : var ( - - space - l ) ; right : var ( - - space - l ) ; margin : 0 ; }
@ media ( max-width : 480px ) { . auth-card . badge-mock { position : static ; margin-bottom : 14 px ; } }
. auth-card h1 { color : var ( - - galaxy ) ; margin : 6 px 0 10 px ; font-size : var ( - - fs - xl ) ; }
. auth-card . muted { color : var ( - - muted ) ; font-size : var ( - - fs - s ) ; }
. btn-wide { width : 100 % ; margin-top : var ( - - space - s ) ; }
/* Footer — brand band: logo + descriptor / spec links (§3.3.1) / demo note + flash.
No top margin: Moon/Galaxy bands meet it flush; white pages pad via .page/.article. */
. footer { background : var ( - - galaxy ) ; color : var ( - - on - galaxy - muted ) ; font-size : 0.85 rem ; }
. footer { background : var ( - - galaxy ) ; color : var ( - - on - galaxy - muted ) ; font-size : var ( - - fs - s ) ; line-height : 1.45 ; }
. footer . container { display : grid ; gap : var ( - - space - m ) ; padding-top : var ( - - space - xl ) ; padding-bottom : var ( - - space - l ) ; }
. foot-top { display : flex ; flex-wrap : wrap ; align-items : center ; gap : var ( - - space - s ) var ( - - space - m ) ; }
. foot-top img { height : 30 px ; width : auto ; }
@@ -593,11 +648,11 @@ mark { background: var(--tint-cherenkov); color: inherit; border-radius: calc(va
. foot-links a : not ( [ href ] ) { color : var ( - - on - galaxy - muted ) ; }
. foot-note { display : flex ; align-items : center ; gap : var ( - - space - s ) ; border-top : 1 px solid rgba ( 255 , 255 , 255 , 0.15 ) ; padding-top : var ( - - space - m ) ; }
/* Whole-page ambient (2026 pattern) — a fixed brand-hue wash behind everything,
opt-in per page via <Base ambient> (home only). Clearly visible pastel fields
(alphas .16/.12/.10 — ceilings: links on the worst blob overlap stay ≥ 5.3:1);
all reading surfaces (cards, panels, bands) are solid and cover it. Static
when motion is reduced. */
/* Whole-page ambient — a fixed brand-hue wash behind everything, opt-in per
page via <Base ambient> (home only). Clearly visible pastel fields (alphas
.16/.12/.10 — ceilings: links on the worst blob overlap stay ≥ 5.3:1); all
reading surfaces (cards, panels, bands) are solid and cover it. Static when
motion is reduced. */
. has-ambient :: before {
content : "" ; position : fixed ; inset : -30 % ; z-index : -1 ; pointer-events : none ;
background :
@@ -608,44 +663,47 @@ mark { background: var(--tint-cherenkov); color: inherit; border-radius: calc(va
/* ==========================================================================
Motion — ALL animation/transition lives in this block (WCAG 2.3.3; the
reduce block above is a second safety net). Nebula translation of ctao.org:
they drift looping vi deo s under Galaxy-Blue vignettes; here an oversized
gradient layer drifts via transform only (compositor-friendly, no repaints).
Worst-case blob-over-text contrast verified ≥4.5:1 (see DESIGN.md).
reduce block above is a second safety net). One hover language: every
interactive element fa des color/background/border/shadow on the SAME two
tokens (--dur/--ease — ctao.org's quiet-fade feel); cards elevate as one
gesture (shadow + 2px lift). Nebula/ambient drift is transform-only and
sized to be clearly perceptible within ~10s of looking.
========================================================================== */
@ media ( prefers-reduced-motion : no-preference ) {
@ view-transition { navigation : auto ; }
html { scroll-behavior : smooth ; }
/* B ands hand their glow to a slowly roaming layer inside the .band-bg clip
frame (same --nebula stack) — the band itself never clips its overlays */
. hero-band , . auth-band { background : var ( - - galaxy ) ; }
/* The auth b and hands its glow to a roaming layer inside the .band-bg clip
frame (same --nebula stack); the photo hero keeps its scrimmed photo and
gets the lighter --nebula-glow drifting above it. */
. auth-band { background : var ( - - galaxy ) ; }
. band-bg :: before {
content : "" ; position : absolute ; inset : -45 % -30 % ;
background : var ( - - nebula ) ;
animation : nebula 22 s ease-in-out infinite alternate ;
animation : nebula 16 s ease-in-out infinite alternate ;
}
/* Whole-page wash — slow drift, clearly alive within ~15s of looking */
. has-ambient :: before { animation : ambient 60 s ease-in-out infinite alternate ; }
. hero-band . band-bg :: before { background : var ( - - nebula - glow ) ; }
/* Whole-page wash — slow drift, clearly alive */
. has-ambient :: before { animation : ambient 36 s ease-in-out infinite alternate ; }
/* Hero content rises in once, gently staggered */
. hero > * { animation : rise 0.5 s ease-out backwards ; }
. hero > * { animation : rise 0.5 s var ( - - ease ) backwards ; }
. hero > : nth-child ( 2 ) { animation-delay : 0.08 s ; }
. hero > : nth-child ( 3 ) { animation-delay : 0.16 s ; }
. hero > : nth-child ( 4 ) { animation-delay : 0.24 s ; }
. hero > : nth-child ( 5 ) { animation-delay : 0.32 s ; }
/* H over feedback */
. card , . search-results li { transition : border-color 0.15 s ease , box-shadow 0.2 s ease , transform 0.2 s ease ; }
. card : hover { transform : translateY ( -2 px ) ; }
/* Editorial cover micro-zoom on card hover (AstroWind pattern; card clips it) */
. card . cover { transition : transform 0.3 s ease ; }
. card : hover . cover , . card : focus-within . cover { transform : scale ( 1.03 ) ; }
. btn , . login-mock , . navlink { transition : background-color 0.15 s ease , color 0.15 s ease ; }
/* Unified h over feedback — single-property fades, shared tokens */
a , . btn , . post-nav-title {
transition : color var ( - - dur ) var ( - - ease ) , background-color var ( - - dur ) var ( - - ease ) ,
border-color var ( - - dur ) var ( - - ease ) , box-shadow var ( - - dur ) var ( - - ease ) ;
}
. card , . search-results li { transition : box-shadow var ( - - dur ) var ( - - ease ) , transform var ( - - dur ) var ( - - ease ) ; }
. card : hover { transform : translateY ( -2 px ) ; } /* the one elevate gesture (no cover zoom) */
}
@ keyframes nebula { /* inert unless applied inside the block above */
from { transform : translate3d ( -6 % , -4 % , 0 ) rotate ( -5 deg ) scale ( 1 ) ; }
to { transform : translate3d ( 6 % , 5 % , 0 ) rotate ( 5 deg ) scale ( 1.22 ) ; }
from { transform : translate3d ( -8 % , -5 % , 0 ) rotate ( -6 deg ) scale ( 1 ) ; }
to { transform : translate3d ( 8 % , 6 % , 0 ) rotate ( 6 deg ) scale ( 1.3 ) ; }
}
@ keyframes ambient {
from { transform : translate3d ( -4 % , -3 % , 0 ) rotate ( -9 deg ) scale ( 1 ) ; opacity : 0.85 ; }
to { transform : translate3d ( 4 % , 4 % , 0 ) rotate ( 9 deg ) scale ( 1.14 ) ; opacity : 1 ; }
from { transform : translate3d ( -5 % , -4 % , 0 ) rotate ( -10 deg ) scale ( 1 ) ; opacity : 0.85 ; }
to { transform : translate3d ( 5 % , 5 % , 0 ) rotate ( 10 deg ) scale ( 1.16 ) ; opacity : 1 ; }
}
@ keyframes rise { from { opacity : 0 ; transform : translateY ( 14 px ) ; } }