spb/countryatlas
Public
TypeScript 57%
Python 38.6%
JavaScript 3.6%
CSS 0.6%
1@import 'tailwindcss';23/*4 CountryAtlas design tokens.5 Editorial, calm, data-dense. Raw values live on :root / .dark; Tailwind utilities read them through6 `@theme inline`, so `bg-paper`, `text-ink-2`, `border-rule`, `text-up`, `fill-series-1` … follow the theme.7 Series/sequential colours are the validated dataviz palette (skills/dataviz references/palette.md),8 re-validated against these surfaces on 2026-09-11 (see apps/web/README.md → Design tokens).9*/10@custom-variant dark (&:where(.dark, .dark *));1112:root {13 color-scheme: light;14 --paper: #fbfaf7; /* page plane */15 --surface: #ffffff; /* chart surface, sheets, dialogs */16 --surface-2: #f3f1ec; /* hover wash, chips */17 --ink: #1a1917;18 --ink-2: #5c5a55;19 --ink-3: #8a877f;20 --rule: #e6e3dc;21 --rule-strong: #c9c6bd;22 --accent: #1c5cab;23 --accent-ink: #ffffff;24 --accent-soft: #e8f0fb;25 --up: #006300;26 --down: #b02a2a;27 --warn: #9a6a00;28 --nodata: #d9d6ce;29 --forecast: #8a877f;3031 --series-1: #2a78d6;32 --series-2: #eb6834;33 --series-3: #1baf7a;34 --series-4: #eda100;35 --series-5: #e87ba4;36 --series-6: #008300;37 --series-7: #4a3aa7;38 --series-8: #e34948;3940 --seq-1: #cde2fb;41 --seq-2: #9ec5f4;42 --seq-3: #6da7ec;43 --seq-4: #3987e5;44 --seq-5: #256abf;45 --seq-6: #184f95;46 --seq-7: #0d366b;4748 /* Direction of change (neutral semantics: increase / decrease, NOT good / bad). Teal ↔ sienna diverging ramp. */49 --inc: #0f7b8a;50 --dec: #b4632d;51 --div-1: #8c4a12; /* strong decrease */52 --div-2: #c98a4e;53 --div-3: #e8c9a6;54 --div-4: #ece9e1; /* no change */55 --div-5: #a6d4d9;56 --div-6: #4fa2ad;57 --div-7: #0b5f6b; /* strong increase */58 /* Second sequential ramp (warm) for a second map layer / category maps */59 --seq2-1: #fbe4d0;60 --seq2-2: #f5c39c;61 --seq2-3: #eb9a63;62 --seq2-4: #d97636;63 --seq2-5: #b55b21;64 --seq2-6: #8a4416;65 --seq2-7: #5c2d0c;66 --map-water: #f4f2ec;67 --map-stroke: #ffffff;6869 --shadow-sheet: 0 -8px 32px rgba(26, 25, 23, 0.12);70 --shadow-pop: 0 4px 24px rgba(26, 25, 23, 0.14);71}7273.dark {74 color-scheme: dark;75 --paper: #151513;76 --surface: #1c1c1a;77 --surface-2: #24241f;78 --ink: #f2f0ea;79 --ink-2: #c9c6bd;80 --ink-3: #8a877f;81 --rule: #2c2b28;82 --rule-strong: #3a3935;83 --accent: #5598e7;84 --accent-ink: #0d1b2e;85 --accent-soft: #17304f;86 --up: #0ca30c;87 --down: #e66767;88 --warn: #e0a100;89 --nodata: #33322f;90 --forecast: #8a877f;9192 --series-1: #3987e5;93 --series-2: #d95926;94 --series-3: #199e70;95 --series-4: #c98500;96 --series-5: #d55181;97 --series-6: #008300;98 --series-7: #9085e9;99 --series-8: #e66767;100101 --seq-1: #0d366b;102 --seq-2: #184f95;103 --seq-3: #256abf;104 --seq-4: #3987e5;105 --seq-5: #6da7ec;106 --seq-6: #9ec5f4;107 --seq-7: #cde2fb;108109 --inc: #4fb6c2;110 --dec: #e0955f;111 --div-1: #d58a4a;112 --div-2: #a86a37;113 --div-3: #5e4530;114 --div-4: #2a2926;115 --div-5: #234f55;116 --div-6: #2e8791;117 --div-7: #63c3cf;118 --seq2-1: #3a2410;119 --seq2-2: #5c3512;120 --seq2-3: #8a4a16;121 --seq2-4: #b8641f;122 --seq2-5: #d98a3c;123 --seq2-6: #eeb277;124 --seq2-7: #f8dcb8;125 --map-water: #101010;126 --map-stroke: #1c1c1a;127128 --shadow-sheet: 0 -8px 32px rgba(0, 0, 0, 0.5);129 --shadow-pop: 0 4px 24px rgba(0, 0, 0, 0.55);130}131132@theme inline {133 --color-paper: var(--paper);134 --color-surface: var(--surface);135 --color-surface-2: var(--surface-2);136 --color-ink: var(--ink);137 --color-ink-2: var(--ink-2);138 --color-ink-3: var(--ink-3);139 --color-rule: var(--rule);140 --color-rule-strong: var(--rule-strong);141 --color-accent: var(--accent);142 --color-accent-ink: var(--accent-ink);143 --color-accent-soft: var(--accent-soft);144 --color-up: var(--up);145 --color-down: var(--down);146 --color-warn: var(--warn);147 --color-nodata: var(--nodata);148 --color-forecast: var(--forecast);149 --color-series-1: var(--series-1);150 --color-series-2: var(--series-2);151 --color-series-3: var(--series-3);152 --color-series-4: var(--series-4);153 --color-series-5: var(--series-5);154 --color-series-6: var(--series-6);155 --color-series-7: var(--series-7);156 --color-series-8: var(--series-8);157 --color-seq-1: var(--seq-1);158 --color-seq-2: var(--seq-2);159 --color-seq-3: var(--seq-3);160 --color-seq-4: var(--seq-4);161 --color-seq-5: var(--seq-5);162 --color-seq-6: var(--seq-6);163 --color-seq-7: var(--seq-7);164 --color-inc: var(--inc);165 --color-dec: var(--dec);166 --color-div-1: var(--div-1);167 --color-div-2: var(--div-2);168 --color-div-3: var(--div-3);169 --color-div-4: var(--div-4);170 --color-div-5: var(--div-5);171 --color-div-6: var(--div-6);172 --color-div-7: var(--div-7);173 --color-map-water: var(--map-water);174175 /* Container widths: editorial pages ~1280, wide tables 1400, explorers = viewport. */176 --container-editorial: 80rem;177 --container-wide: 87.5rem;178179 --font-ui: var(--font-ui), ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;180 --font-display: var(--font-display), 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;181 --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;182183 /* Typographic scale (rem). Body 15px on mobile, 16px from md. */184 --text-2xs: 0.6875rem;185 --text-2xs--line-height: 1rem;186 --text-xs: 0.75rem;187 --text-xs--line-height: 1.1rem;188 --text-sm: 0.875rem;189 --text-sm--line-height: 1.3rem;190 --text-base: 1rem;191 --text-base--line-height: 1.55rem;192 --text-lg: 1.125rem;193 --text-lg--line-height: 1.6rem;194 --text-xl: 1.3125rem;195 --text-xl--line-height: 1.75rem;196 --text-2xl: 1.625rem;197 --text-2xl--line-height: 2rem;198 --text-3xl: 2rem;199 --text-3xl--line-height: 2.3rem;200 --text-4xl: 2.5rem;201 --text-4xl--line-height: 2.75rem;202 --text-5xl: 3.25rem;203 --text-5xl--line-height: 3.4rem;204205 --shadow-sheet: var(--shadow-sheet);206 --shadow-pop: var(--shadow-pop);207208 --radius-xs: 2px;209 --radius-sm: 4px;210 --radius-md: 6px;211}212213@layer base {214 html {215 font-family: var(--font-ui);216 background: var(--paper);217 color: var(--ink);218 -webkit-text-size-adjust: 100%;219 text-rendering: optimizeLegibility;220 scroll-padding-top: 6.5rem;221 }222 html,223 body {224 /* Never scroll horizontally; individual scrollers opt in with overflow-x-auto. */225 overflow-x: clip;226 max-width: 100%;227 }228 body {229 font-size: 15px;230 line-height: 1.55;231 min-height: 100dvh;232 background: var(--paper);233 color: var(--ink);234 }235 @media (min-width: 48rem) {236 body {237 font-size: 16px;238 }239 }240 h1,241 h2,242 h3 {243 text-wrap: balance;244 }245 p {246 text-wrap: pretty;247 }248 a {249 text-underline-offset: 0.15em;250 }251 :focus-visible {252 outline: 2px solid var(--accent);253 outline-offset: 2px;254 border-radius: 2px;255 }256 ::selection {257 background: var(--accent-soft);258 color: var(--ink);259 }260 svg {261 display: block;262 }263 button {264 cursor: pointer;265 }266 [hidden] {267 display: none !important;268 }269 @media (prefers-reduced-motion: reduce) {270 *,271 *::before,272 *::after {273 animation-duration: 0.01ms !important;274 animation-iteration-count: 1 !important;275 transition-duration: 0.01ms !important;276 scroll-behavior: auto !important;277 }278 }279}280281@utility tnum {282 font-variant-numeric: tabular-nums lining-nums;283 font-feature-settings: 'tnum' 1, 'lnum' 1;284}285@utility pnum {286 font-variant-numeric: proportional-nums lining-nums;287}288@utility display {289 font-family: var(--font-display);290 font-weight: 500;291 letter-spacing: -0.01em;292}293@utility eyebrow {294 font-size: var(--text-2xs);295 line-height: 1rem;296 letter-spacing: 0.08em;297 text-transform: uppercase;298 font-weight: 600;299 color: var(--ink-3);300}301@utility hairline {302 border-top: 1px solid var(--rule);303}304@utility tap {305 min-height: 44px;306 min-width: 44px;307}308@utility scrollbar-none {309 scrollbar-width: none;310 &::-webkit-scrollbar {311 display: none;312 }313}314@utility safe-bottom {315 padding-bottom: env(safe-area-inset-bottom, 0px);316}317@utility no-data-hatch {318 background-color: var(--nodata);319 background-image: repeating-linear-gradient(135deg, transparent 0 3px, rgba(26, 25, 23, 0.14) 3px 4px);320}321@utility link-quiet {322 color: inherit;323 text-decoration: none;324 &:hover {325 color: var(--accent);326 }327}328@utility container-x {329 padding-left: 1rem;330 padding-right: 1rem;331 @media (min-width: 40rem) {332 padding-left: 1.5rem;333 padding-right: 1.5rem;334 }335}336337@utility container-editorial {338 max-width: var(--container-editorial);339 margin-left: auto;340 margin-right: auto;341}342@utility container-wide {343 max-width: var(--container-wide);344 margin-left: auto;345 margin-right: auto;346}347/* Break out of the centred container to the viewport edges. */348@utility full-bleed {349 width: 100vw;350 margin-left: calc(50% - 50vw);351 margin-right: calc(50% - 50vw);352}353/* Horizontal ticker strip: snap-scrolling figures on one rule, no scrollbar. */354@utility ticker {355 display: flex;356 overflow-x: auto;357 scroll-snap-type: x proximity;358 scrollbar-width: none;359 &::-webkit-scrollbar {360 display: none;361 }362 & > * {363 scroll-snap-align: start;364 flex: 0 0 auto;365 }366}367/* Small data badges (quality, freshness, direction). Text + glyph always accompany the colour. */368@utility badge {369 display: inline-flex;370 align-items: center;371 gap: 0.25rem;372 border-radius: 2px;373 padding: 1px 6px;374 font-size: var(--text-2xs);375 line-height: 1rem;376 font-weight: 500;377 letter-spacing: 0.02em;378 border: 1px solid var(--rule);379 color: var(--ink-2);380}381382/* Range inputs (year slider): large thumb, hairline track, accent fill via CSS var --pct set by the component. */383.ca-range {384 -webkit-appearance: none;385 appearance: none;386 background: transparent;387 cursor: pointer;388}389.ca-range:focus-visible {390 outline: none;391}392.ca-range::-webkit-slider-runnable-track {393 height: 4px;394 border-radius: 2px;395 background: var(--rule-strong);396}397.ca-range::-moz-range-track {398 height: 4px;399 border-radius: 2px;400 background: var(--rule-strong);401}402.ca-range::-webkit-slider-thumb {403 -webkit-appearance: none;404 appearance: none;405 width: 22px;406 height: 22px;407 margin-top: -9px;408 border-radius: 50%;409 background: var(--accent);410 border: 3px solid var(--surface);411 box-shadow: 0 0 0 1px var(--rule-strong);412}413.ca-range::-moz-range-thumb {414 width: 22px;415 height: 22px;416 border-radius: 50%;417 background: var(--accent);418 border: 3px solid var(--surface);419 box-shadow: 0 0 0 1px var(--rule-strong);420}421.ca-range:focus-visible::-webkit-slider-thumb {422 box-shadow: 0 0 0 3px var(--accent-soft), 0 0 0 4px var(--accent);423}424.ca-range:disabled {425 opacity: 0.4;426 cursor: default;427}428429/* Chart chrome shared by the SVG kit (class names instead of inline styles so theme swaps repaint). */430.ca-chart text {431 font-family: var(--font-ui);432 font-variant-numeric: tabular-nums;433 fill: var(--ink-3);434 font-size: 11px;435}436.ca-chart .axis line,437.ca-chart .grid line {438 stroke: var(--rule);439 stroke-width: 1;440 shape-rendering: crispEdges;441}442.ca-chart .baseline {443 stroke: var(--rule-strong);444}445.ca-chart .series-line {446 fill: none;447 stroke-width: 2;448 stroke-linejoin: round;449 stroke-linecap: round;450}451.ca-chart .forecast {452 stroke-dasharray: 4 4;453}454.ca-chart .ring {455 stroke: var(--surface);456 stroke-width: 2;457}458.ca-chart .label {459 fill: var(--ink-2);460 font-size: 11px;461}462.ca-chart .label-strong {463 fill: var(--ink);464 font-weight: 600;465}466.ca-hatch {467 fill: url(#ca-nodata-hatch);468}469470/* Motion tokens */471@keyframes ca-sheet-up {472 from {473 transform: translateY(24px);474 opacity: 0;475 }476 to {477 transform: none;478 opacity: 1;479 }480}481@keyframes ca-fade {482 from {483 opacity: 0;484 }485 to {486 opacity: 1;487 }488}489.animate-sheet {490 animation: ca-sheet-up 220ms cubic-bezier(0.2, 0.8, 0.2, 1);491}492.animate-fade {493 animation: ca-fade 160ms ease-out;494}495