diff --git a/app/components/marketing.css b/app/components/marketing.css index 0c69695..351560d 100644 --- a/app/components/marketing.css +++ b/app/components/marketing.css @@ -89,6 +89,63 @@ } } +/* Release banner */ +.release-banner { + border-bottom: 1px solid hsl(var(--border)); + background: color-mix(in srgb, hsl(var(--warning)) 10%, hsl(var(--card))); +} + +.release-banner__row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.5rem 1rem; + padding-block: 0.75rem; + border-inline-start: 4px solid hsl(var(--warning)); +} + +.release-banner__badge { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-family: var(--font-mono); + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: hsl(var(--foreground)); + white-space: nowrap; +} + +.release-banner__dot { + width: 0.5rem; + height: 0.5rem; + border-radius: 50%; + background: hsl(var(--warning)); + flex-shrink: 0; +} + +.release-banner__text { + margin: 0; + font-size: 0.875rem; + color: hsl(var(--muted-foreground)); +} + +.release-banner__text strong { + color: hsl(var(--foreground)); +} + +.release-banner__link { + color: hsl(var(--primary)); + text-decoration: underline; + text-underline-offset: 2px; +} + +.release-banner__link:hover, +.release-banner__link:focus-visible { + color: hsl(var(--foreground)); +} + /* Hero */ .hero { position: relative; @@ -138,6 +195,14 @@ grid-template-columns: 1.1fr 0.9fr; } +.hero__copy { + /* The headline's available width is this column's width, not the + viewport's -- at the two-column desktop breakpoint this column is well + under half the viewport. A container query (not a vw-based clamp) lets + the headline size track its actual column. */ + container-type: inline-size; +} + .hero__eyebrow { font-family: var(--font-mono); font-size: 0.875rem; @@ -148,18 +213,33 @@ } .hero__headline { - font-size: 3.25rem; - line-height: 1.1; + /* A floor for the smallest phones, a cqw-driven middle so it scales with + this column's own width (see .hero__copy's container-type), a ceiling + so it never grows past the original design size. Tuned so the two-line + headline stays at or under 3 wrapped lines at every width this site is + checked at, including the narrower column the two-column desktop + layout leaves it. */ + font-size: clamp(1.25rem, 0.7rem + 3.3cqw, 2.6rem); + line-height: 1.15; font-weight: 700; display: flex; flex-direction: column; margin-bottom: 1.5rem; } +.hero__headline span { + text-wrap: balance; +} + .hero__headline-accent { color: hsl(var(--primary)); } +.hero__headline-icon { + display: inline-block; + white-space: nowrap; +} + .hero__lede { font-size: 1.125rem; color: hsl(var(--muted-foreground)); @@ -184,10 +264,6 @@ .hero__grid { grid-template-columns: 1fr; } - - .hero__headline { - font-size: 2.5rem; - } } /* Terminal card */ @@ -282,6 +358,28 @@ margin-bottom: 2rem; } +/* Split layout: head and body side by side in the same column ratio as the + hero's grid, so a section whose body is a single block of prose still + uses the section's full width instead of sitting in a narrow column with + empty space beside it. The prose itself still wraps at a readable width + (its own max-width, below) inside that column. */ +.section__grid { + display: grid; + gap: 3rem; + grid-template-columns: 1.1fr 0.9fr; + align-items: start; +} + +.section__grid .section__head { + margin-bottom: 0; +} + +@media (max-width: 860px) { + .section__grid { + grid-template-columns: 1fr; + } +} + .section__eyebrow { font-family: var(--font-mono); font-size: 0.875rem; @@ -297,6 +395,11 @@ line-height: 1.1; } +.section__heading-icon { + display: inline-block; + white-space: nowrap; +} + .section__lede { margin-top: 0.75rem; color: hsl(var(--muted-foreground)); @@ -371,6 +474,7 @@ .footer__links { list-style: none; display: flex; + flex-wrap: wrap; gap: 1.5rem; margin: 0; padding: 0; diff --git a/app/components/release-banner.tsx b/app/components/release-banner.tsx new file mode 100644 index 0000000..3be15fc --- /dev/null +++ b/app/components/release-banner.tsx @@ -0,0 +1,53 @@ +/* +Copyright The CryptOS Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import "./marketing.css"; + +interface ReleaseBannerCta { + label: string; + href: string; +} + +interface ReleaseBannerProps { + badge: string; + heading: string; + body: string; + cta?: ReleaseBannerCta; +} + +// Base-layer component: a pre-release status strip shown above the hero. +// Text stays in the foreground/muted-foreground pair against the card +// background (both exceed WCAG AA contrast in light and dark) -- the +// warning token is used only as a left accent border and the badge icon, +// never as a text/background text pair, because `--warning-foreground` on +// `--warning` falls short of AA for normal text in the light theme. +export const ReleaseBanner = ({ badge, heading, body, cta }: ReleaseBannerProps) => ( +
+ {heading} {body}{" "} + {cta ? ( + + {cta.label} + + ) : null} +
+{eyebrow}
: null} -{lede}
: null} +}: SectionProps) => { + const [headingLead, headingTail] = splitTrailingWord(heading); + + return ( +{eyebrow}
: null} +{lede}
: null} +