339 lines
9.9 KiB
CSS
339 lines
9.9 KiB
CSS
/* ============================================================================
|
|
Novarix Networks — Global stylesheet
|
|
============================================================================
|
|
|
|
This file controls the visual *theme* of the site (colours, fonts,
|
|
background effects, animations). Most page layout is done with Tailwind
|
|
utility classes directly inside `app/page.tsx` — this file only handles
|
|
things that don't fit neatly into utilities.
|
|
|
|
What's in here, top to bottom:
|
|
1. Design tokens — brand colours, font, shadows (@theme block)
|
|
2. Theme variables — light + dark mode CSS variables
|
|
3. Base styles — body font, focus rings, text selection
|
|
4. Site shell + ambient — background gradient, floating orbs, grid
|
|
5. Brand wordmark swap — light/dark logo switcher
|
|
6. Intro overlay — first-visit animated logo
|
|
7. Animations — keyframes used above
|
|
8. Reduced motion — respect "prefers-reduced-motion" setting
|
|
|
|
To change the brand colour palette, edit the `--color-brand-*` lines in
|
|
the @theme block below. Everything else cascades from those.
|
|
============================================================================ */
|
|
|
|
@import "tailwindcss";
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
1. Design tokens — registered with Tailwind v4 via @theme.
|
|
These become utility classes (e.g. `bg-brand-500`, `text-ink-900`) and
|
|
custom CSS variables you can use anywhere in the stylesheet.
|
|
--------------------------------------------------------------------------- */
|
|
@theme {
|
|
--font-sans:
|
|
Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
|
"Segoe UI", sans-serif;
|
|
|
|
--color-brand-50: oklch(0.97 0.015 240);
|
|
--color-brand-100: oklch(0.93 0.04 240);
|
|
--color-brand-200: oklch(0.86 0.07 240);
|
|
--color-brand-300: oklch(0.78 0.11 240);
|
|
--color-brand-400: oklch(0.7 0.14 240);
|
|
--color-brand-500: oklch(0.62 0.16 240);
|
|
--color-brand-600: oklch(0.54 0.18 245);
|
|
--color-brand-700: oklch(0.46 0.18 250);
|
|
|
|
--color-ink-50: oklch(0.985 0.003 250);
|
|
--color-ink-100: oklch(0.96 0.005 250);
|
|
--color-ink-200: oklch(0.92 0.008 250);
|
|
--color-ink-300: oklch(0.84 0.012 250);
|
|
--color-ink-400: oklch(0.65 0.018 250);
|
|
--color-ink-500: oklch(0.5 0.02 250);
|
|
--color-ink-600: oklch(0.38 0.022 250);
|
|
--color-ink-700: oklch(0.28 0.024 250);
|
|
--color-ink-800: oklch(0.2 0.026 250);
|
|
--color-ink-900: oklch(0.13 0.028 255);
|
|
--color-ink-950: oklch(0.08 0.03 260);
|
|
|
|
--radius-card: 1.25rem;
|
|
--shadow-card: 0 1px 0 0 oklch(1 0 0 / 0.04) inset, 0 12px 32px -12px oklch(0 0 0 / 0.18);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
2. Theme variables (light + dark)
|
|
These are the runtime CSS variables read by both this file and
|
|
app/page.tsx (via classes like `bg-[var(--surface)]`). Each variable has
|
|
a light value here and a dark override further down inside the
|
|
`@media (prefers-color-scheme: dark)` block.
|
|
--------------------------------------------------------------------------- */
|
|
:root {
|
|
color-scheme: light dark;
|
|
|
|
--bg: var(--color-ink-50);
|
|
--surface: oklch(1 0 0 / 0.7);
|
|
--surface-strong: oklch(1 0 0 / 0.92);
|
|
--text: var(--color-ink-900);
|
|
--text-soft: var(--color-ink-500);
|
|
--border: oklch(0.13 0.028 255 / 0.1);
|
|
--border-strong: oklch(0.13 0.028 255 / 0.18);
|
|
--accent: var(--color-brand-500);
|
|
--accent-soft: oklch(0.62 0.16 240 / 0.12);
|
|
--ring: oklch(0.62 0.16 240 / 0.4);
|
|
--grid: oklch(0.5 0.02 250 / 0.08);
|
|
|
|
--button-bg: var(--color-ink-900);
|
|
--button-fg: var(--color-ink-50);
|
|
--button-hover: var(--color-ink-800);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root {
|
|
--bg: var(--color-ink-950);
|
|
--surface: oklch(0.13 0.028 255 / 0.55);
|
|
--surface-strong: oklch(0.13 0.028 255 / 0.85);
|
|
--text: var(--color-ink-100);
|
|
--text-soft: var(--color-ink-400);
|
|
--border: oklch(1 0 0 / 0.08);
|
|
--border-strong: oklch(1 0 0 / 0.16);
|
|
--accent: var(--color-brand-400);
|
|
--accent-soft: oklch(0.7 0.14 240 / 0.18);
|
|
--ring: oklch(0.7 0.14 240 / 0.5);
|
|
--grid: oklch(0.84 0.012 250 / 0.06);
|
|
|
|
--button-bg: var(--color-ink-100);
|
|
--button-fg: var(--color-ink-950);
|
|
--button-hover: var(--color-ink-50);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
3. Base styles — applied to plain HTML elements before any classes hit.
|
|
--------------------------------------------------------------------------- */
|
|
@layer base {
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font-family: var(--font-sans);
|
|
-webkit-font-smoothing: antialiased;
|
|
-moz-osx-font-smoothing: grayscale;
|
|
text-rendering: optimizeLegibility;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
::selection {
|
|
background: var(--accent-soft);
|
|
color: var(--text);
|
|
}
|
|
|
|
:focus-visible {
|
|
outline: 2px solid var(--ring);
|
|
outline-offset: 3px;
|
|
border-radius: 4px;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
4. Site shell + ambient effects
|
|
The .site-shell class is on the <main> element in page.tsx. The two
|
|
radial gradients here follow the user's mouse via the --mx / --my CSS
|
|
variables that page.tsx writes on every mousemove.
|
|
--------------------------------------------------------------------------- */
|
|
.site-shell {
|
|
position: relative;
|
|
isolation: isolate;
|
|
min-height: 100vh;
|
|
background:
|
|
radial-gradient(
|
|
circle at var(--mx, 50%) var(--my, 22%),
|
|
oklch(0.62 0.16 240 / 0.12),
|
|
transparent 20%
|
|
),
|
|
radial-gradient(
|
|
circle at calc(var(--mx, 50%) * 0.65) calc(var(--my, 22%) * 1.2),
|
|
oklch(0.6 0.14 280 / 0.09),
|
|
transparent 24%
|
|
),
|
|
var(--bg);
|
|
}
|
|
|
|
.ambient-grid {
|
|
position: absolute;
|
|
inset: 0;
|
|
background-image:
|
|
linear-gradient(to right, var(--grid) 1px, transparent 1px),
|
|
linear-gradient(to bottom, var(--grid) 1px, transparent 1px);
|
|
background-size: 56px 56px;
|
|
mask-image: linear-gradient(to bottom, oklch(0 0 0 / 0.5), transparent 80%);
|
|
}
|
|
|
|
.ambient-orb {
|
|
position: absolute;
|
|
border-radius: 9999px;
|
|
filter: blur(80px);
|
|
opacity: 0.22;
|
|
animation: drift 18s ease-in-out infinite;
|
|
will-change: transform;
|
|
}
|
|
|
|
.ambient-orb-a {
|
|
width: 32rem;
|
|
height: 32rem;
|
|
top: 4rem;
|
|
right: -10rem;
|
|
background: linear-gradient(
|
|
135deg,
|
|
oklch(0.7 0.14 240 / 0.55),
|
|
oklch(0.6 0.14 200 / 0.1)
|
|
);
|
|
}
|
|
|
|
.ambient-orb-b {
|
|
width: 26rem;
|
|
height: 26rem;
|
|
top: 28rem;
|
|
left: -8rem;
|
|
background: linear-gradient(
|
|
135deg,
|
|
oklch(0.6 0.14 280 / 0.35),
|
|
oklch(0.78 0.11 200 / 0.12)
|
|
);
|
|
animation-duration: 22s;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
5. Brand wordmark colour-scheme swap
|
|
The header logo has two PNG variants — a colour one for light mode and a
|
|
white one for dark mode. The `brand-light` / `brand-dark` classes (set
|
|
in page.tsx) live on both <Image> tags; only the matching one is shown.
|
|
--------------------------------------------------------------------------- */
|
|
.brand-light {
|
|
display: block;
|
|
}
|
|
|
|
.brand-dark {
|
|
display: none;
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
.brand-light {
|
|
display: none;
|
|
}
|
|
|
|
.brand-dark {
|
|
display: block;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
6. Intro overlay
|
|
The first-visit animated SVG wordmark. The SVG itself runs a 3-second
|
|
stroke-then-fill animation (defined inside
|
|
/public/branding/animated_logo_intro.svg). The overlay fades out at the
|
|
3-second mark so the SVG completes before it disappears. Total intro
|
|
length is matched in page.tsx's setTimeout (3600ms).
|
|
|
|
When .intro-running is on the <main> element, the page content
|
|
underneath is hidden so it doesn't flash through during the animation.
|
|
--------------------------------------------------------------------------- */
|
|
.intro-overlay {
|
|
position: fixed;
|
|
inset: 0;
|
|
z-index: 2000;
|
|
display: grid;
|
|
place-items: center; /* dead-centre the SVG in the viewport */
|
|
animation: intro-fade-out 600ms ease 3s forwards;
|
|
}
|
|
|
|
.intro-backdrop {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: var(--bg);
|
|
}
|
|
|
|
.intro-svg {
|
|
position: relative; /* sits above .intro-backdrop in the stacking order */
|
|
display: block;
|
|
width: min(70vw, 900px);
|
|
max-height: 70vh;
|
|
height: auto;
|
|
object-fit: contain;
|
|
}
|
|
|
|
.intro-running .site-header,
|
|
.intro-running main > section,
|
|
.intro-running main > footer {
|
|
opacity: 0;
|
|
}
|
|
|
|
@media (max-width: 720px) {
|
|
.intro-svg {
|
|
width: min(86vw, 560px);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.intro-svg {
|
|
width: 92vw;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
7. Animations — keyframes used by the orbs and the intro overlay above.
|
|
--------------------------------------------------------------------------- */
|
|
@keyframes drift {
|
|
0%,
|
|
100% {
|
|
transform: translate3d(0, 0, 0) scale(1);
|
|
}
|
|
50% {
|
|
transform: translate3d(0, 18px, 0) scale(1.06);
|
|
}
|
|
}
|
|
|
|
@keyframes intro-wordmark {
|
|
0% {
|
|
opacity: 0;
|
|
transform: translateY(110%) scale(0.98);
|
|
}
|
|
100% {
|
|
opacity: 1;
|
|
transform: translateY(0) scale(1);
|
|
}
|
|
}
|
|
|
|
@keyframes intro-fade-out {
|
|
to {
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
8. Reduced motion
|
|
Respects the user's OS-level "reduce motion" preference by disabling
|
|
the orb drift, the intro animation, and any transition/animation
|
|
durations across the site.
|
|
--------------------------------------------------------------------------- */
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html {
|
|
scroll-behavior: auto;
|
|
}
|
|
|
|
.ambient-orb,
|
|
.intro-overlay,
|
|
.intro-wordmark {
|
|
animation: none !important;
|
|
}
|
|
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
transition-duration: 0ms !important;
|
|
animation-duration: 0ms !important;
|
|
}
|
|
}
|