SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
10.5 KB · 271 lines css
Raw Blame History
1@import "tailwindcss";23@custom-variant dark (&:where(.dark, .dark *));45/* ------------------------------------------------------------------------------------------6   DataCenterIndex — design tokens7   Near-monochrome: ink + warm graphite + paper. ONE accent (signal amber) for pipeline/emphasis.8   Semantic status scale is fixed and shared with the map (see lib/labels.ts).9   ------------------------------------------------------------------------------------------ */10@theme {11  --font-sans: var(--font-inter), ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;12  --font-mono: var(--font-jetbrains), ui-monospace, "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;1314  --color-paper: var(--paper);15  --color-surface: var(--surface);16  --color-surface-2: var(--surface-2);17  --color-surface-3: var(--surface-3);18  --color-ink: var(--ink);19  --color-ink-2: var(--ink-2);20  --color-ink-3: var(--ink-3);21  --color-ink-4: var(--ink-4);22  --color-hair: var(--hair);23  --color-hair-2: var(--hair-2);24  --color-accent: var(--accent);25  --color-accent-ink: var(--accent-ink);26  --color-accent-soft: var(--accent-soft);2728  --color-st-operational: var(--st-operational);29  --color-st-construction: var(--st-construction);30  --color-st-planned: var(--st-planned);31  --color-st-permitting: var(--st-permitting);32  --color-st-delayed: var(--st-delayed);33  --color-st-closed: var(--st-closed);34  --color-st-rumored: var(--st-rumored);35  --color-st-unknown: var(--st-unknown);3637  --color-ok: var(--ok);38  --color-warn: var(--warn);39  --color-danger: var(--danger);40  --color-info: var(--info);4142  --radius-xs: 2px;43  --radius-sm: 3px;44  --radius-md: 4px;4546  --animate-fade-in: fade-in 0.18s ease-out both;47  --animate-sheet-up: sheet-up 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;48  --animate-sheet-side: sheet-side 0.22s cubic-bezier(0.2, 0.8, 0.2, 1) both;49  --animate-row-in: row-in 0.5s ease-out both;50  --animate-pulse-soft: pulse-soft 1.8s ease-in-out infinite;5152  @keyframes fade-in {53    from { opacity: 0; transform: translateY(-2px); }54    to { opacity: 1; transform: translateY(0); }55  }56  @keyframes sheet-up {57    from { opacity: 0; transform: translateY(24px); }58    to { opacity: 1; transform: translateY(0); }59  }60  @keyframes sheet-side {61    from { opacity: 0; transform: translateX(16px); }62    to { opacity: 1; transform: translateX(0); }63  }64  @keyframes row-in {65    from { background-color: color-mix(in oklab, var(--accent) 16%, transparent); }66    to { background-color: transparent; }67  }68  @keyframes pulse-soft {69    0%, 100% { opacity: 1; }70    50% { opacity: 0.45; }71  }72}7374:root {75  color-scheme: light;76  --paper: #f5f4f1;77  --surface: #ffffff;78  --surface-2: #efeeea;79  --surface-3: #e6e4df;80  --ink: #0b0f14;81  --ink-2: #3b4148;82  --ink-3: #666c73; /* 5.1:1 on paper (was #6b7178 = 4.49:1) */83  --ink-4: #9a9fa5; /* hint level only (placeholders, kbd, "/total") — below 4.5:1 by design */84  --hair: #e3e1dc;85  --hair-2: #c9c6bf;86  --accent: #c8541a;87  --accent-ink: #a3420f;88  --accent-soft: #f7e6dc;8990  /* status scale — validated (dataviz six checks): lightness-separated blue/violet, labels always accompany colour */91  --st-operational: #1f6b3a;92  --st-construction: #b87a0a;93  --st-planned: #5f8fc4;94  --st-permitting: #5b3f8f;95  --st-delayed: #a94a2a;96  --st-closed: #858a90;97  --st-rumored: #8f95a0;98  --st-unknown: #b3b6ba;99100  --ok: #1f6b3a;101  --warn: #b87a0a;102  --danger: #a94a2a;103  --info: #3f5b7a;104105  --map-water: #e9ecef;106}107108.dark {109  color-scheme: dark;110  --paper: #0b0f14;111  --surface: #10161d;112  --surface-2: #161d25;113  --surface-3: #1d252e;114  --ink: #eceae5;115  --ink-2: #b9b6ae;116  --ink-3: #8f959c; /* 6.2:1 on paper (was #858a90 = 5.4:1) */117  --ink-4: #7a8189; /* 4.8:1 on paper, 4.6:1 on surface (was #5b6168 = 3.1:1) */118  --hair: #1f272f;119  --hair-2: #2f3941;120  --accent: #e8763a;121  --accent-ink: #f08b52;122  --accent-soft: #2a1a10;123124  --st-operational: #3fa465;125  --st-construction: #d4962a;126  --st-planned: #7fb0e6;127  --st-permitting: #8a6bc9;128  --st-delayed: #d9704a;129  --st-closed: #6f757c;130  --st-rumored: #7b828c;131  --st-unknown: #4d545b;132133  --ok: #3fa465;134  --warn: #d4962a;135  --danger: #d9704a;136  --info: #7f9bb8;137}138139@layer base {140  html {141    -webkit-text-size-adjust: 100%;142    text-rendering: optimizeLegibility;143    scroll-padding-top: 4rem;144  }145  body {146    background: var(--paper);147    color: var(--ink);148    font-family: var(--font-sans);149    font-size: 14px;150    line-height: 1.45;151    font-feature-settings: "cv11", "ss01";152    overflow-x: hidden;153  }154  ::selection {155    background: color-mix(in oklab, var(--accent) 28%, transparent);156  }157  :focus-visible {158    outline: 2px solid var(--accent);159    outline-offset: 2px;160    border-radius: 2px;161  }162  button, [role="button"] {163    cursor: pointer;164  }165  a {166    text-underline-offset: 3px;167  }168  h1, h2, h3, h4 {169    letter-spacing: -0.01em;170  }171  table {172    border-collapse: separate;173    border-spacing: 0;174  }175  input::-webkit-search-cancel-button {176    -webkit-appearance: none;177  }178  @media (prefers-reduced-motion: reduce) {179    *, *::before, *::after {180      animation-duration: 0.01ms !important;181      animation-iteration-count: 1 !important;182      transition-duration: 0.01ms !important;183      scroll-behavior: auto !important;184    }185  }186}187188@utility tnum {189  font-variant-numeric: tabular-nums lining-nums;190}191@utility figure {192  font-family: var(--font-mono);193  font-variant-numeric: tabular-nums lining-nums;194  letter-spacing: -0.02em;195}196@utility no-scrollbar {197  scrollbar-width: none;198  &::-webkit-scrollbar { display: none; }199}200@utility hair-b {201  border-bottom: 1px solid var(--hair);202}203@utility safe-b {204  padding-bottom: env(safe-area-inset-bottom);205}206@utility label-caps {207  font-size: 10.5px;208  letter-spacing: 0.08em;209  text-transform: uppercase;210  color: var(--ink-3);211  font-weight: 500;212}213214/* Graticule texture for the home hero — thin lines, low contrast, never noisy. */215.graticule {216  background-image:217    linear-gradient(to right, color-mix(in oklab, var(--ink) 6%, transparent) 1px, transparent 1px),218    linear-gradient(to bottom, color-mix(in oklab, var(--ink) 6%, transparent) 1px, transparent 1px);219  background-size: 48px 48px;220  mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.9), transparent 90%);221}222223/* Responsive table → compact two-line rows under 640 px (each cell carries data-label).224   Line 1: [data-primary] cell (name + meta) with the [data-lead] figure on the right.225   Line 2: the remaining cells wrap as "LABEL value" pairs. Cells hidden via `hidden sm:table-cell` stay hidden. */226.dci-table { width: 100%; }227.dci-table th { position: sticky; top: 0; z-index: 1; background: var(--surface); }228@media (max-width: 639.98px) {229  .dci-table.responsive thead { display: none; }230  .dci-table.responsive, .dci-table.responsive tbody { display: block; width: 100%; }231  .dci-table.responsive tr { display: flex; flex-wrap: wrap; align-items: baseline; column-gap: 12px; row-gap: 3px; width: 100%; border-bottom: 1px solid var(--hair); padding: 11px 0; min-height: 40px; }232  .dci-table.responsive td { order: 2; display: inline-flex; flex: 0 0 auto; align-items: baseline; gap: 5px; min-width: 0; max-width: 100%; padding: 0 !important; border: 0 !important; font-size: 12px; line-height: 1.35; text-align: left; }233  .dci-table.responsive td.hidden { display: none; }234  .dci-table.responsive td[data-label]::before { content: attr(data-label); color: var(--ink-3); font-family: var(--font-inter), ui-sans-serif, system-ui, sans-serif; font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; flex: 0 0 auto; }235  .dci-table.responsive td[data-primary] { order: 0; display: block; flex: 1 1 55%; font-size: 13px; line-height: 1.35; font-weight: 500; }236  .dci-table.responsive tr:not(:has(td[data-lead])) td[data-primary] { flex-basis: 100%; }237  .dci-table.responsive td[data-primary]::before { display: none; }238  .dci-table.responsive td[data-lead] { order: 1; flex: 1 0 auto; justify-content: flex-end; font-size: 13px; color: var(--ink); }239  .dci-table.responsive td[data-lead]::before { color: var(--ink-4); }240  /* Whole row = tap target for the primary RowLink (≥ 40 px); other links in the row stay clickable above it. */241  .dci-table.responsive tr { position: relative; }242  .dci-table.responsive td a { position: relative; z-index: 1; }243  .dci-table.responsive td[data-primary] a.dci-rowlink { position: static; }244  .dci-table.responsive td[data-primary] a.dci-rowlink::after { content: ""; position: absolute; inset: 0; z-index: 0; }245  /* Collapsed long lists: only the first 10 rows until "Show all N" (see <CollapsibleRows/>). */246  .dci-collapse[data-collapsed] tbody > tr:nth-child(n + 11),247  .dci-collapse[data-collapsed] > :is(ol, ul) > li:nth-child(n + 11) { display: none; }248}249250/* Dual range slider — two overlaid native inputs, only the thumbs are visible. */251.dual-range { pointer-events: none; }252.dual-range::-webkit-slider-runnable-track { background: transparent; height: 2px; }253.dual-range::-moz-range-track { background: transparent; height: 2px; }254.dual-range::-webkit-slider-thumb { pointer-events: auto; -webkit-appearance: none; appearance: none; width: 22px; height: 22px; margin-top: -10px; border-radius: 50%; background: var(--surface); border: 2px solid var(--ink); cursor: grab; }255.dual-range::-moz-range-thumb { pointer-events: auto; width: 18px; height: 18px; border-radius: 50%; background: var(--surface); border: 2px solid var(--ink); cursor: grab; }256.dual-range:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--accent); outline-offset: 2px; }257258/* MapLibre overrides — keep controls on brand and small. */259.maplibregl-ctrl-group { border-radius: 3px !important; box-shadow: none !important; border: 1px solid var(--hair-2); background: var(--surface) !important; }260.maplibregl-ctrl-group button { width: 32px !important; height: 32px !important; }261@media (max-width: 767.98px) { .maplibregl-ctrl-group button { width: 40px !important; height: 40px !important; } .maplibregl-ctrl-group button .maplibregl-ctrl-icon { background-size: 20px; } }262.dark .maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: invert(1); }263.maplibregl-ctrl-attrib { font-size: 10px !important; background: color-mix(in oklab, var(--surface) 85%, transparent) !important; color: var(--ink-3) !important; }264.maplibregl-ctrl-attrib a { color: var(--ink-2) !important; }265.maplibregl-canvas:focus { outline: none; }266/* Keep MapLibre controls above the fixed mobile bottom nav. */267@media (max-width: 767.98px) {268  body:not([data-hide-mobile-nav]) .maplibregl-ctrl-bottom-right,269  body:not([data-hide-mobile-nav]) .maplibregl-ctrl-bottom-left { bottom: calc(3.5rem + env(safe-area-inset-bottom)); }270}271