df9c40847d
- About page with identity cards and equipment lists (about.json) - Contact page: station info, obfuscated e-mail, lazy Leaflet/OSM map with antenna tower marker and dark-mode tiles - Privacy, license and disclaimer pages from a legal collection - QRT-themed 404 page - LICENSE (MIT) and LICENSE-CONTENT (CC BY-SA 4.0 legal code) - Shared PageHeader and CodeCopy components Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
270 lines
6.7 KiB
CSS
270 lines
6.7 KiB
CSS
@import 'tailwindcss';
|
|
@import '@fontsource-variable/rubik';
|
|
@import '@fontsource-variable/inter';
|
|
@plugin '@tailwindcss/typography';
|
|
|
|
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
|
|
|
|
/*
|
|
* Palette: Huemint "Magazine" — #f2efee #d5d09b #111012 #c95748
|
|
* Contrast notes (WCAG): ink on red = 4.46 → only large/bold text (≥19px bold) on --accent.
|
|
* Small text on a red fill uses --accent-fill + --on-accent-fill (5.3 light / 6.5 dark).
|
|
* Small red text uses --accent-text, which falls back to --fg on khaki sections (red/khaki < 4.5).
|
|
*/
|
|
:root {
|
|
color-scheme: light;
|
|
--bg: #f2efee;
|
|
--bg-alt: #d5d09b;
|
|
--surface: #f8f6f5;
|
|
--fg: #111012;
|
|
--fg-muted: #4a474b;
|
|
--line: color-mix(in oklab, #111012 18%, transparent);
|
|
--accent: #c95748;
|
|
--accent-strong: #a8412f;
|
|
--on-accent: #111012;
|
|
--accent-fill: #a8412f;
|
|
--on-accent-fill: #f2efee;
|
|
--accent-on-alt: var(--fg);
|
|
--accent-text: var(--accent-strong);
|
|
}
|
|
|
|
[data-theme='dark'] {
|
|
color-scheme: dark;
|
|
--bg: #111012;
|
|
--bg-alt: #1c1b17;
|
|
--surface: #19181a;
|
|
--fg: #ebe7e4;
|
|
--fg-muted: #b3ad8a;
|
|
--line: color-mix(in oklab, #d5d09b 16%, transparent);
|
|
--accent: #e07a6b;
|
|
--accent-strong: #e07a6b;
|
|
--on-accent: #111012;
|
|
--accent-fill: #e07a6b;
|
|
--on-accent-fill: #111012;
|
|
--accent-on-alt: var(--accent);
|
|
--accent-text: var(--accent-strong);
|
|
}
|
|
|
|
@theme inline {
|
|
--color-bg: var(--bg);
|
|
--color-bg-alt: var(--bg-alt);
|
|
--color-surface: var(--surface);
|
|
--color-fg: var(--fg);
|
|
--color-fg-muted: var(--fg-muted);
|
|
--color-line: var(--line);
|
|
--color-accent: var(--accent);
|
|
--color-accent-strong: var(--accent-strong);
|
|
--color-accent-text: var(--accent-text);
|
|
--color-accent-fill: var(--accent-fill);
|
|
--color-on-accent: var(--on-accent);
|
|
--color-on-accent-fill: var(--on-accent-fill);
|
|
--color-khaki: #d5d09b;
|
|
--color-paper: #f2efee;
|
|
--color-ink: #111012;
|
|
--font-display: 'Rubik Variable', ui-sans-serif, system-ui, sans-serif;
|
|
--font-sans: 'Inter Variable', ui-sans-serif, system-ui, sans-serif;
|
|
--font-mono: ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace;
|
|
}
|
|
|
|
@layer base {
|
|
html {
|
|
@apply bg-bg font-sans text-fg antialiased;
|
|
scroll-padding-top: 5rem;
|
|
}
|
|
|
|
body {
|
|
@apply min-h-dvh text-[1.0625rem] leading-relaxed;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4 {
|
|
@apply font-display;
|
|
}
|
|
|
|
:focus-visible {
|
|
@apply outline-3 outline-offset-2 outline-accent;
|
|
}
|
|
|
|
::selection {
|
|
@apply bg-accent text-on-accent;
|
|
}
|
|
}
|
|
|
|
@layer components {
|
|
/* Khaki (alt) section: switches small red text to an accessible colour. */
|
|
.surface-alt {
|
|
@apply bg-bg-alt;
|
|
--accent-text: var(--accent-on-alt);
|
|
}
|
|
|
|
/* Magazine headline: heavy, uppercase, tight. */
|
|
.headline {
|
|
@apply font-display leading-[0.95] font-black tracking-tight uppercase;
|
|
}
|
|
|
|
/* "Learn More" style link: thick underline that turns red on hover. */
|
|
.link-strong {
|
|
@apply font-display font-bold underline decoration-fg decoration-3 underline-offset-[0.35em] transition-colors hover:decoration-accent;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.01ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
transition-duration: 0.01ms !important;
|
|
scroll-behavior: auto !important;
|
|
}
|
|
}
|
|
|
|
/* ---------- Article typography (unlayered: overrides the typography plugin) ---------- */
|
|
.article-prose {
|
|
--tw-prose-body: var(--fg);
|
|
--tw-prose-headings: var(--fg);
|
|
--tw-prose-lead: var(--fg-muted);
|
|
--tw-prose-links: var(--accent-strong);
|
|
--tw-prose-bold: var(--fg);
|
|
--tw-prose-counters: var(--fg-muted);
|
|
--tw-prose-bullets: var(--accent);
|
|
--tw-prose-hr: var(--line);
|
|
--tw-prose-quotes: var(--fg);
|
|
--tw-prose-quote-borders: var(--accent);
|
|
--tw-prose-captions: var(--fg-muted);
|
|
--tw-prose-kbd: var(--fg);
|
|
--tw-prose-code: var(--fg);
|
|
--tw-prose-pre-code: var(--fg);
|
|
--tw-prose-pre-bg: var(--surface);
|
|
--tw-prose-th-borders: var(--fg);
|
|
--tw-prose-td-borders: var(--line);
|
|
}
|
|
|
|
.article-prose :where(h2, h3, h4) {
|
|
font-family: var(--font-display);
|
|
font-weight: 800;
|
|
scroll-margin-top: 2rem;
|
|
}
|
|
|
|
.article-prose :where(a) {
|
|
text-decoration-thickness: 2px;
|
|
text-underline-offset: 0.2em;
|
|
}
|
|
|
|
.article-prose :where(:not(pre) > code) {
|
|
background: var(--bg-alt);
|
|
padding: 0.1em 0.35em;
|
|
border-radius: 2px;
|
|
font-weight: 500;
|
|
}
|
|
.article-prose :where(:not(pre) > code)::before,
|
|
.article-prose :where(:not(pre) > code)::after {
|
|
content: none;
|
|
}
|
|
|
|
.article-prose :where(blockquote) {
|
|
font-style: normal;
|
|
background: var(--surface);
|
|
padding-block: 0.25em;
|
|
}
|
|
.article-prose :where(blockquote p)::before,
|
|
.article-prose :where(blockquote p)::after {
|
|
content: none;
|
|
}
|
|
|
|
.article-prose :where(figure) {
|
|
margin-inline: 0;
|
|
}
|
|
.article-prose :where(figcaption) {
|
|
border-left: 3px solid var(--accent);
|
|
padding-left: 0.75em;
|
|
}
|
|
|
|
/* Shiki dual theme (defaultColor: false) */
|
|
.astro-code {
|
|
color: var(--shiki-light);
|
|
background-color: var(--surface) !important;
|
|
border: 1px solid var(--line);
|
|
border-radius: 2px;
|
|
}
|
|
.astro-code span {
|
|
color: var(--shiki-light);
|
|
}
|
|
[data-theme='dark'] .astro-code,
|
|
[data-theme='dark'] .astro-code span {
|
|
color: var(--shiki-dark);
|
|
}
|
|
|
|
.code-block {
|
|
position: relative;
|
|
}
|
|
/* Room for the copy button so it never covers the first line. */
|
|
.code-block > pre {
|
|
padding-top: 2.5rem;
|
|
}
|
|
.code-copy {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: 0.5rem;
|
|
padding: 0.15rem 0.6rem;
|
|
font: 700 0.75rem/1.5 var(--font-display);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: var(--bg);
|
|
background: var(--fg);
|
|
border-radius: 2px;
|
|
cursor: pointer;
|
|
}
|
|
.code-copy:hover {
|
|
background: var(--accent-fill);
|
|
color: var(--on-accent-fill);
|
|
}
|
|
|
|
/* ---------- Contact map (Leaflet) ---------- */
|
|
.tower-marker {
|
|
background: none;
|
|
border: 0;
|
|
}
|
|
/* Rotate an inner element: `rotate` on the marker itself would also rotate Leaflet's translate3d. */
|
|
.tower-pin {
|
|
display: flex;
|
|
width: 100%;
|
|
height: 100%;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: var(--accent-fill);
|
|
color: var(--on-accent-fill);
|
|
border: 2px solid var(--fg);
|
|
border-radius: 50% 50% 50% 0;
|
|
rotate: -45deg;
|
|
}
|
|
.tower-pin svg {
|
|
rotate: 45deg;
|
|
}
|
|
.leaflet-container .leaflet-control-attribution {
|
|
background: var(--surface);
|
|
color: var(--fg);
|
|
}
|
|
.leaflet-container .leaflet-control-attribution a {
|
|
color: var(--accent-strong);
|
|
}
|
|
.leaflet-container {
|
|
font: inherit;
|
|
background: var(--bg-alt);
|
|
}
|
|
.leaflet-popup-content {
|
|
font-family: var(--font-display);
|
|
font-weight: 700;
|
|
}
|
|
/* Dark tiles without a second tile provider. */
|
|
[data-theme='dark'] .leaflet-tile-pane {
|
|
filter: invert(1) hue-rotate(180deg) brightness(0.85) contrast(0.9);
|
|
}
|
|
.leaflet-container .leaflet-popup-content-wrapper,
|
|
.leaflet-container .leaflet-popup-tip {
|
|
background: var(--surface);
|
|
color: var(--fg);
|
|
}
|