From baff2ac4195790a777759ff5ffc3e94fa32b3662 Mon Sep 17 00:00:00 2001
From: Denis Drobyshev
Date: Sun, 23 Aug 2026 12:00:39 +0300
Subject: [PATCH] Rebuild the site on colour-blocked bands, with two themes
The datasheet look was one commission ago. This one follows a different brief:
the shape of an agency site - white ground, a bright band, a dark band, cards
on a small radius, pill controls - in our own palette rather than the
reference's. Amber where theirs is lemon yellow, and the mark's indigo where
theirs is crimson.
Sections are mapped to what an open-source organisation actually has rather
than copied. An agency shows services, expertise, client references and
articles. Here that is projects, the stack and why each piece is in it,
measurements, and the notes and the course. The references section is the one
that could not be transplanted honestly: there are no clients and no case
studies, so it shows stadion's headroom table and the reproduce command
instead, and says plainly that is what it is doing.
Two themes, which reverses the single-scheme decision from the last rebuild.
The system preference decides, the toggle overrides it, and the choice is
stored under `theme`. That makes clause 7 of the privacy policy wrong again in
the other direction - it currently promises nothing is stored - so it now
describes exactly what is kept, where, and what happens when a reader clears
it.
Kept: the OG card and its meta, the apple-touch icon, the JSON-LD graph, the
commercial-enquiries route, and the footer line pointing at check_site.py.
Two contrast failures found and fixed while measuring rather than after:
The rule paragraphs on the dark band came out at 2.4:1, because `.band-dark p`
and `.rule p` have identical specificity and the later one won. The band
overrides are now one class deeper, so a band always beats a component.
The small text on the amber band sat at 4.4:1 - under AA, and only visible
because the browser could not compute a `color-mix()` against its own
background, so the effective colour had to be composited by hand. Raised to
84%, which puts every string on that band above 6.5:1.
Both themes now clear AA on every measured pair: body, eyebrows, card text,
tags, links, table captions, footer links, and the dark and amber bands.
---
404.html | 10 +-
assets/site.css | 845 ++++++++++++++++++-----------------------------
assets/site.js | 57 ++--
index.html | 549 +++++++++++++++---------------
legal/index.html | 27 +-
ru/index.html | 578 ++++++++++++++++----------------
6 files changed, 952 insertions(+), 1114 deletions(-)
diff --git a/404.html b/404.html
index 2719299..7c3177d 100644
--- a/404.html
+++ b/404.html
@@ -4,10 +4,18 @@
Page not found — DrobyshevDev
-
+
+
+
+
diff --git a/assets/site.css b/assets/site.css
index 42ebb66..f05421a 100644
--- a/assets/site.css
+++ b/assets/site.css
@@ -2,27 +2,22 @@
DrobyshevDev - organisation site
One stylesheet, no build step, no external requests.
- One scheme, dark. A toggle means designing the whole thing twice, and the
- second design is always the one nobody checks - the same reasoning the lemma
- documentation is built on.
-
- The visual language is hairlines, flat surfaces and type. There are no
- rounded corners, no shadows and no gradients outside the mark, because every
- one of those is a decoration standing in for a fact. What carries weight
- here is a number with the conditions it was measured under printed beneath
- it.
+ Colour-blocked bands - ground, a bright band, a dark band - with cards on a
+ 4px radius and pill controls. Two themes: the system preference decides,
+ the toggle overrides it, and the choice is remembered.
+
+ No webfont. The typography is a system stack set heavy and tracked tight,
+ because a display face would be a third-party request and this site is
+ checked in CI for making none.
--------------------------------------------------------------------------- */
/* --- Reset ---------------------------------------------------------------- */
-*,
-*::before,
-*::after { box-sizing: border-box; }
+*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
-body,
-h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { margin: 0; padding: 0; }
+body, h1, h2, h3, h4, p, figure, blockquote, dl, dd, ul, ol { margin: 0; padding: 0; }
ul[role="list"], ol[role="list"] { list-style: none; }
@@ -30,30 +25,86 @@ img, svg { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
-/* --- Tokens --------------------------------------------------------------- */
+/* --- Tokens: light -------------------------------------------------------- */
:root {
- --bg: #08080a;
- --bg-2: #0d0d11;
- --bg-3: #121217;
+ --ground: #fdfcfa;
+ --ground-2: #f5f2ec;
+ --card: #ffffff;
+
+ --ink: #1b1b1e;
+ --ink-2: #56565f; /* 7.0:1 on --ground */
+ --ink-3: #74747d; /* 4.6:1 on --ground */
- --line: #1d1d23;
- --line-2: #2b2b34;
+ --line: #e6e2da;
+ --line-2: #d3cec3;
- --fg: #ffffff;
- --fg-2: #a3a3ae; /* 7.4:1 on --bg */
- --fg-3: #74747f; /* 4.6:1 on --bg: small labels still pass AA */
+ --bright: #f0b429; /* the band that carries the page */
+ --bright-ink: #1b1b1e;
+ --dark: #1b1b1e;
+ --dark-ink: #f7f6f3;
+ --dark-ink-2: #a8a8b0;
+ --dark-line: #34343a;
- --accent: #7c83ff;
- --accent-2: #b4b9ff;
- --accent-dim: rgba(124, 131, 255, .12);
+ --accent: #4f46e5;
+ --accent-2: #b4b1ff;
+ --accent-wash: #eeecfd;
- --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, "Noto Sans", sans-serif;
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", "Cascadia Mono", "Segoe UI Mono", "Roboto Mono", Menlo, Consolas, monospace;
- --wrap: 1240px;
- --gutter: clamp(20px, 5vw, 56px);
- --band: clamp(72px, 9vw, 132px);
+ --wrap: 1200px;
+ --gutter: clamp(20px, 5vw, 48px);
+ --band: clamp(64px, 8vw, 116px);
+ --radius: 4px;
+
+ color-scheme: light;
+}
+
+/* --- Tokens: dark --------------------------------------------------------- */
+
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) {
+ --ground: #131316;
+ --ground-2: #0c0c0e;
+ --card: #1a1a1e;
+
+ --ink: #f5f4f1;
+ --ink-2: #a8a8b1;
+ --ink-3: #8a8a94;
+
+ --line: #2a2a31;
+ --line-2: #3a3a43;
+
+ --bright-ink: #16160f;
+ --dark: #0c0c0e;
+ --dark-line: #26262c;
+
+ --accent: #8f8bff;
+ --accent-wash: #1d1b33;
+
+ color-scheme: dark;
+ }
+}
+
+:root[data-theme="dark"] {
+ --ground: #131316;
+ --ground-2: #0c0c0e;
+ --card: #1a1a1e;
+
+ --ink: #f5f4f1;
+ --ink-2: #a8a8b1;
+ --ink-3: #8a8a94;
+
+ --line: #2a2a31;
+ --line-2: #3a3a43;
+
+ --bright-ink: #16160f;
+ --dark: #0c0c0e;
+ --dark-line: #26262c;
+
+ --accent: #8f8bff;
+ --accent-wash: #1d1b33;
color-scheme: dark;
}
@@ -61,25 +112,21 @@ button, input, select, textarea { font: inherit; color: inherit; }
/* --- Base ----------------------------------------------------------------- */
body {
- background: var(--bg);
- color: var(--fg);
+ background: var(--ground);
+ color: var(--ink);
font-family: var(--font-sans);
font-size: 17px;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
- text-rendering: optimizeLegibility;
}
a { color: inherit; }
-code, kbd, samp, pre { font-family: var(--font-mono); font-size: .92em; }
+code, kbd, samp, pre { font-family: var(--font-mono); font-size: .9em; }
-::selection { background: var(--accent); color: #fff; }
+::selection { background: var(--bright); color: var(--bright-ink); }
-:focus-visible {
- outline: 2px solid var(--accent-2);
- outline-offset: 3px;
-}
+:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.wrap {
width: 100%;
@@ -89,600 +136,358 @@ code, kbd, samp, pre { font-family: var(--font-mono); font-size: .92em; }
}
.skip-link {
- position: absolute;
- left: -9999px;
- top: 0;
- z-index: 100;
- background: var(--fg);
- color: var(--bg);
- padding: 10px 16px;
- font-weight: 600;
+ position: absolute; left: -9999px; top: 0; z-index: 100;
+ background: var(--ink); color: var(--ground);
+ padding: 10px 16px; font-weight: 600;
}
.skip-link:focus { left: 0; }
/* --- Type ----------------------------------------------------------------- */
h1, h2, h3, h4 {
- font-weight: 600;
- line-height: 1.05;
- letter-spacing: -.028em;
+ font-weight: 800;
+ line-height: 1.06;
+ letter-spacing: -.03em;
text-wrap: balance;
}
-h1 { font-size: clamp(2.6rem, 6.4vw, 5rem); letter-spacing: -.038em; }
-h2 { font-size: clamp(1.9rem, 3.8vw, 3.1rem); }
-h3 { font-size: clamp(1.25rem, 1.9vw, 1.6rem); letter-spacing: -.022em; }
-h4 { font-size: .8125rem; }
+h1 { font-size: clamp(2.5rem, 6vw, 4.5rem); letter-spacing: -.038em; }
+h2 { font-size: clamp(1.9rem, 3.6vw, 2.9rem); }
+h3 { font-size: clamp(1.15rem, 1.7vw, 1.45rem); letter-spacing: -.022em; }
.eyebrow {
- font-size: .6875rem;
- font-weight: 600;
- letter-spacing: .16em;
- text-transform: uppercase;
- color: var(--fg-3);
- margin-bottom: 22px;
-}
-.eyebrow .rule {
- display: inline-block;
- width: 28px;
- height: 1px;
- background: var(--accent);
- vertical-align: middle;
- margin-right: 12px;
- margin-bottom: 2px;
+ display: inline-flex; align-items: center; gap: 10px;
+ font-size: .6875rem; font-weight: 700;
+ letter-spacing: .14em; text-transform: uppercase;
+ color: var(--ink-3);
+ margin-bottom: 20px;
+}
+.eyebrow::before {
+ content: ""; width: 24px; height: 3px;
+ background: var(--bright); border-radius: 2px;
}
.lead {
- font-size: clamp(1.0625rem, 1.35vw, 1.1875rem);
- line-height: 1.65;
- color: var(--fg-2);
- max-width: 62ch;
+ font-size: clamp(1.0625rem, 1.3vw, 1.1875rem);
+ line-height: 1.65; color: var(--ink-2); max-width: 62ch;
}
-.note {
- font-size: .8125rem;
- line-height: 1.55;
- color: var(--fg-3);
-}
+.note { font-size: .8125rem; line-height: 1.55; color: var(--ink-3); }
/* --- Header --------------------------------------------------------------- */
.site-header {
- position: sticky;
- top: 0;
- z-index: 50;
- background: rgba(8, 8, 10, .78);
- backdrop-filter: saturate(160%) blur(14px);
+ position: sticky; top: 0; z-index: 50;
+ background: color-mix(in srgb, var(--ground) 86%, transparent);
+ backdrop-filter: saturate(160%) blur(12px);
border-bottom: 1px solid var(--line);
}
-.site-header .wrap {
- display: flex;
- align-items: center;
- gap: 32px;
- height: 62px;
-}
+.site-header .wrap { display: flex; align-items: center; gap: 28px; height: 66px; }
.brand {
- display: inline-flex;
- align-items: center;
- gap: 10px;
- font-weight: 600;
- font-size: .9375rem;
- letter-spacing: -.01em;
- text-decoration: none;
- flex: none;
+ display: inline-flex; align-items: center; gap: 10px;
+ font-weight: 800; font-size: .9375rem; letter-spacing: -.02em;
+ text-decoration: none; flex: none;
}
-.brand .mark { width: 22px; height: 22px; }
+.brand .mark { width: 24px; height: 24px; border-radius: var(--radius); }
-.nav {
- display: flex;
- align-items: center;
- gap: 28px;
- margin-left: auto;
-}
+.nav { display: flex; align-items: center; gap: 26px; margin-left: auto; }
.nav a {
- font-size: .8125rem;
- color: var(--fg-2);
- text-decoration: none;
- padding: 6px 0;
- position: relative;
+ font-size: .875rem; font-weight: 500; color: var(--ink-2);
+ text-decoration: none; padding: 6px 0; position: relative;
transition: color .15s;
}
.nav a::after {
- content: "";
- position: absolute;
- left: 0; right: 0; bottom: 0;
- height: 1px;
- background: var(--accent);
- transform: scaleX(0);
- transition: transform .18s;
-}
-.nav a:hover { color: var(--fg); }
+ content: ""; position: absolute; left: 0; right: 0; bottom: 0;
+ height: 3px; background: var(--bright); border-radius: 2px;
+ transform: scaleX(0); transform-origin: left; transition: transform .18s;
+}
+.nav a:hover { color: var(--ink); }
.nav a:hover::after { transform: scaleX(1); }
-.nav-tools {
- display: flex;
- align-items: center;
- gap: 6px;
- flex: none;
-}
+.nav-tools { display: flex; align-items: center; gap: 8px; flex: none; }
.lang-switch {
- font-size: .75rem;
- font-weight: 600;
- letter-spacing: .08em;
- color: var(--fg-2);
- text-decoration: none;
- padding: 7px 11px;
- border: 1px solid var(--line-2);
+ font-size: .75rem; font-weight: 700; letter-spacing: .08em;
+ color: var(--ink-2); text-decoration: none;
+ padding: 7px 12px; border: 1px solid var(--line-2); border-radius: 999px;
transition: border-color .15s, color .15s;
}
-.lang-switch:hover { color: var(--fg); border-color: var(--fg-3); }
+.lang-switch:hover { color: var(--ink); border-color: var(--ink-3); }
.icon-btn {
- display: none;
- align-items: center;
- justify-content: center;
- width: 36px;
- height: 34px;
- background: none;
- border: 1px solid var(--line-2);
- color: var(--fg-2);
- cursor: pointer;
-}
-.icon-btn svg { width: 18px; height: 18px; }
+ display: inline-flex; align-items: center; justify-content: center;
+ width: 36px; height: 36px;
+ background: none; border: 1px solid var(--line-2); border-radius: 999px;
+ color: var(--ink-2); cursor: pointer;
+ transition: border-color .15s, color .15s;
+}
+.icon-btn:hover { color: var(--ink); border-color: var(--ink-3); }
+.icon-btn svg { width: 17px; height: 17px; }
+.nav-toggle { display: none; }
+
+/* The toggle shows the theme it switches to, so only one icon is ever up. */
+.theme-toggle .moon { display: none; }
+:root[data-theme="dark"] .theme-toggle .sun { display: none; }
+:root[data-theme="dark"] .theme-toggle .moon { display: block; }
+@media (prefers-color-scheme: dark) {
+ :root:not([data-theme="light"]) .theme-toggle .sun { display: none; }
+ :root:not([data-theme="light"]) .theme-toggle .moon { display: block; }
+}
/* --- Buttons -------------------------------------------------------------- */
.btn {
- display: inline-flex;
- align-items: center;
- gap: 10px;
- padding: 14px 24px;
- font-size: .875rem;
- font-weight: 600;
- letter-spacing: .01em;
- text-decoration: none;
- border: 1px solid transparent;
- transition: background-color .15s, border-color .15s, color .15s;
+ display: inline-flex; align-items: center; gap: 9px;
+ padding: 13px 24px; border-radius: 999px;
+ font-size: .875rem; font-weight: 700; letter-spacing: .01em;
+ text-decoration: none; border: 2px solid transparent;
+ transition: background-color .15s, border-color .15s, color .15s, transform .12s;
}
.btn svg { width: 16px; height: 16px; flex: none; }
+.btn:active { transform: translateY(1px); }
+
+.btn-primary { background: var(--ink); color: var(--ground); }
+.btn-primary:hover { background: var(--accent); color: #fff; }
-.btn-primary { background: var(--fg); color: var(--bg); }
-.btn-primary:hover { background: var(--accent-2); }
+.btn-secondary { border-color: var(--line-2); color: var(--ink); }
+.btn-secondary:hover { border-color: var(--ink); }
-.btn-secondary { border-color: var(--line-2); color: var(--fg); }
-.btn-secondary:hover { border-color: var(--fg-3); background: var(--bg-2); }
+.btn-bright { background: var(--bright); color: var(--bright-ink); }
+.btn-bright:hover { background: var(--ink); color: var(--ground); }
.actions { display: flex; flex-wrap: wrap; gap: 12px; }
+/* --- Bands ---------------------------------------------------------------- */
+
+.band { padding-block: var(--band); }
+.band-2 { background: var(--ground-2); }
+.band-bright { background: var(--bright); color: var(--bright-ink); }
+.band-dark { background: var(--dark); color: var(--dark-ink); }
+
+.band-bright .eyebrow, .band-bright .lead { color: color-mix(in srgb, var(--bright-ink) 84%, transparent); }
+.band-bright .eyebrow::before { background: var(--bright-ink); }
+.band-dark .eyebrow { color: var(--dark-ink-2); }
+.band-dark .lead, .band-dark p { color: var(--dark-ink-2); }
+.band-dark h2, .band-dark h3 { color: var(--dark-ink); }
+
+.section-head { max-width: 66ch; margin-bottom: clamp(36px, 4.5vw, 60px); }
+.section-head h2 { margin-bottom: 16px; }
+
/* --- Hero ----------------------------------------------------------------- */
-.hero {
- padding-top: clamp(64px, 9vw, 128px);
- padding-bottom: 0;
- border-bottom: 1px solid var(--line);
+.hero { padding-block: clamp(56px, 8vw, 104px) clamp(48px, 6vw, 80px); }
+.hero h1 { max-width: 17ch; margin-bottom: 24px; }
+.hero .lead { margin-bottom: 32px; font-size: clamp(1.125rem, 1.5vw, 1.3125rem); }
+.hero .actions { margin-bottom: 40px; }
+.hero-since {
+ display: inline-flex; align-items: center; gap: 10px;
+ font-size: .875rem; color: var(--ink-3);
}
+.hero-since .dot { width: 8px; height: 8px; border-radius: 999px; background: var(--bright); }
-.hero h1 { max-width: 19ch; margin-bottom: 28px; }
-.hero .lead { margin-bottom: 36px; }
-.hero .actions { margin-bottom: clamp(56px, 7vw, 96px); }
+/* --- Card grid ------------------------------------------------------------ */
-/* The specification strip: the first thing a reader gets after the claim is
- the set of facts that back it, in the shape a datasheet uses. */
-.spec-strip {
+.cards {
display: grid;
- grid-template-columns: repeat(4, 1fr);
- border-top: 1px solid var(--line);
-}
-.spec-strip > div {
- padding: 26px 28px 30px 0;
- border-right: 1px solid var(--line);
-}
-.spec-strip > div:last-child { border-right: 0; }
-.spec-strip dt {
- font-size: .6875rem;
- font-weight: 600;
- letter-spacing: .13em;
- text-transform: uppercase;
- color: var(--fg-3);
- margin-bottom: 10px;
-}
-.spec-strip dd {
- font-size: clamp(1.5rem, 2.3vw, 2rem);
- font-weight: 600;
- letter-spacing: -.03em;
- line-height: 1.1;
- font-variant-numeric: tabular-nums;
-}
-.spec-strip .sub {
- display: block;
- margin-top: 8px;
- font-size: .75rem;
- font-weight: 400;
- letter-spacing: 0;
- color: var(--fg-3);
- line-height: 1.45;
+ grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
+ gap: 20px;
}
-/* --- Bands ---------------------------------------------------------------- */
-
-.band { padding-block: var(--band); border-bottom: 1px solid var(--line); }
-.band-2 { background: var(--bg-2); }
-
-.section-head { max-width: 68ch; margin-bottom: clamp(44px, 5vw, 72px); }
-.section-head h2 { margin-bottom: 20px; }
+.card {
+ display: flex; flex-direction: column;
+ background: var(--card);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ padding: 26px 24px 22px;
+ transition: border-color .15s, transform .15s, box-shadow .15s;
+}
+.card:hover {
+ border-color: var(--line-2);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 22px -14px rgba(0, 0, 0, .45);
+}
+.band-dark .card { background: color-mix(in srgb, var(--dark-ink) 6%, transparent); border-color: var(--dark-line); }
-/* --- Projects ------------------------------------------------------------- */
+.card-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
+.card h3 { font-family: var(--font-mono); font-weight: 700; font-size: 1.1875rem; }
+.card h3 a { text-decoration: none; }
+.card h3 a:hover { color: var(--accent); }
+.card p { color: var(--ink-2); font-size: .9375rem; margin-bottom: 18px; }
+.band-dark .card p { color: var(--dark-ink-2); }
-.project {
- display: grid;
- grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
- gap: clamp(32px, 5vw, 80px);
- padding-block: clamp(44px, 5vw, 68px);
- border-top: 1px solid var(--line);
-}
-.project:last-child { border-bottom: 1px solid var(--line); }
-
-.project-name {
- display: flex;
- align-items: baseline;
- gap: 14px;
- margin-bottom: 18px;
-}
-.project-name h3 {
- font-family: var(--font-mono);
- font-size: clamp(1.5rem, 2.4vw, 2rem);
- font-weight: 600;
- letter-spacing: -.02em;
-}
-.project-name h3 a { text-decoration: none; }
-.project-name h3 a:hover { color: var(--accent-2); }
-.project-name .licence {
- font-size: .6875rem;
- font-weight: 600;
- letter-spacing: .1em;
- text-transform: uppercase;
- color: var(--fg-3);
- border: 1px solid var(--line-2);
- padding: 3px 8px;
-}
-
-.project .claim {
- font-size: clamp(1.125rem, 1.6vw, 1.375rem);
- line-height: 1.4;
- letter-spacing: -.018em;
- font-weight: 500;
- margin-bottom: 18px;
- text-wrap: balance;
+.tag {
+ font-size: .6875rem; font-weight: 700; letter-spacing: .07em;
+ text-transform: uppercase; color: var(--ink-3);
+ border: 1px solid var(--line-2); border-radius: 999px;
+ padding: 3px 10px; white-space: nowrap;
}
-.project p { color: var(--fg-2); margin-bottom: 24px; max-width: 60ch; }
+.band-dark .tag { color: var(--dark-ink-2); border-color: var(--dark-line); }
+
+.card-foot { margin-top: auto; display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
-.project-links { display: flex; flex-wrap: wrap; gap: 22px; }
-.project-links a {
- font-size: .8125rem;
- font-weight: 600;
- color: var(--fg);
+.more {
+ display: inline-flex; align-items: center; gap: 7px;
+ font-size: .8125rem; font-weight: 700; color: var(--accent);
text-decoration: none;
- border-bottom: 1px solid var(--line-2);
- padding-bottom: 3px;
- transition: border-color .15s, color .15s;
}
-.project-links a:hover { color: var(--accent-2); border-color: var(--accent); }
+.more svg { width: 14px; height: 14px; transition: transform .15s; }
+.more:hover svg { transform: translateX(3px); }
+.band-dark .more { color: var(--accent-2); }
-/* Datasheet block beside each project. */
-.datasheet { border-top: 1px solid var(--line-2); }
-.datasheet > div {
- display: grid;
- grid-template-columns: 1fr auto;
- gap: 20px;
- align-items: baseline;
- padding: 13px 0;
- border-bottom: 1px solid var(--line);
-}
-.datasheet dt {
- font-size: .75rem;
- letter-spacing: .06em;
- text-transform: uppercase;
- color: var(--fg-3);
-}
-.datasheet dd {
- font-size: .9375rem;
- font-weight: 600;
- text-align: right;
- font-variant-numeric: tabular-nums;
-}
-
-.command {
- position: relative;
- margin-top: 24px;
- background: var(--bg);
- border: 1px solid var(--line-2);
- padding: 16px 52px 16px 18px;
- font-family: var(--font-mono);
- font-size: .8125rem;
- line-height: 1.9;
- overflow-x: auto;
-}
-.band-2 .command { background: var(--bg-3); }
-.command .p { color: var(--fg-3); user-select: none; }
-.command .c { color: var(--fg-3); }
-.command .s { color: var(--accent-2); }
-
-.copy-btn {
- position: absolute;
- top: 10px;
- right: 10px;
- width: 30px;
- height: 30px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- background: none;
- border: 1px solid var(--line-2);
- color: var(--fg-3);
- cursor: pointer;
- transition: color .15s, border-color .15s;
-}
-.copy-btn:hover { color: var(--fg); border-color: var(--fg-3); }
-.copy-btn svg { width: 14px; height: 14px; }
-.copy-btn[data-copied] { color: var(--accent-2); border-color: var(--accent); }
-
-/* A measured table inside a project block. The caption carries the conditions,
- so the figures above it can be read without the repository open. */
-.measure {
- margin: 0 0 24px;
- overflow-x: auto;
-}
-.measure table {
- width: 100%;
- border-collapse: collapse;
- font-variant-numeric: tabular-nums;
+.card .command {
+ font-family: var(--font-mono); font-size: .8125rem;
+ background: var(--ground-2); border: 1px solid var(--line);
+ border-radius: var(--radius); padding: 9px 12px; margin-bottom: 18px;
+ overflow-x: auto; white-space: nowrap;
}
+.band-dark .card .command { background: rgba(0, 0, 0, .28); border-color: var(--dark-line); }
+.card .command .p { color: var(--ink-3); user-select: none; }
+
+/* --- Evidence ------------------------------------------------------------- */
+
+.measure { overflow-x: auto; }
+.measure table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.measure caption {
- caption-side: bottom;
- padding-top: 12px;
- text-align: left;
- font-size: .8125rem;
- line-height: 1.5;
- color: var(--fg-3);
+ caption-side: bottom; padding-top: 14px; text-align: left;
+ font-size: .8125rem; line-height: 1.5;
+ color: color-mix(in srgb, var(--bright-ink) 84%, transparent);
}
.measure th {
- text-align: left;
- font-size: .6875rem;
- font-weight: 600;
- letter-spacing: .12em;
- text-transform: uppercase;
- color: var(--fg-3);
+ text-align: left; font-size: .6875rem; font-weight: 700;
+ letter-spacing: .12em; text-transform: uppercase;
padding: 0 20px 10px 0;
- border-bottom: 1px solid var(--line-2);
+ border-bottom: 2px solid color-mix(in srgb, var(--bright-ink) 30%, transparent);
+ color: color-mix(in srgb, var(--bright-ink) 84%, transparent);
}
.measure td {
- padding: 11px 20px 11px 0;
- border-bottom: 1px solid var(--line);
- font-size: .9375rem;
-}
-.measure td:first-child { color: var(--fg-2); }
-.measure td:nth-child(2) { font-weight: 600; }
-.measure td:last-child { color: var(--fg-3); font-size: .8125rem; padding-right: 0; }
-.measure code { color: var(--fg-2); }
-
-/* --- Course --------------------------------------------------------------- */
-
-.course {
- display: grid;
- grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
- gap: clamp(32px, 5vw, 80px);
- align-items: start;
+ padding: 11px 20px 11px 0; font-size: .9375rem;
+ border-bottom: 1px solid color-mix(in srgb, var(--bright-ink) 14%, transparent);
+}
+.measure td:nth-child(2) { font-weight: 800; }
+.measure td:last-child { font-size: .8125rem; padding-right: 0; opacity: .86; }
+.measure code { font-weight: 600; }
+
+.figures { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 20px; margin-top: 44px; }
+.figure dt { font-size: .6875rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; opacity: .86; margin-bottom: 8px; }
+.figure dd { font-size: clamp(1.75rem, 2.6vw, 2.375rem); font-weight: 800; letter-spacing: -.03em; line-height: 1.05; }
+.figure .sub { display: block; margin-top: 8px; font-size: .8125rem; font-weight: 400; letter-spacing: 0; line-height: 1.45; opacity: .86; }
+
+/* --- Notes ---------------------------------------------------------------- */
+
+.feed { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; }
+.feed .card h3 { font-family: var(--font-sans); letter-spacing: -.02em; }
+.meta { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; font-size: .75rem; color: var(--ink-3); margin-bottom: 14px; }
+.meta time { font-variant-numeric: tabular-nums; }
+.meta .sep { opacity: .5; }
+.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
+.chip {
+ font-size: .6875rem; font-weight: 600; color: var(--accent);
+ background: var(--accent-wash); border-radius: 999px; padding: 3px 10px;
}
-.course h2 { margin-bottom: 20px; }
-.course p { color: var(--fg-2); margin-bottom: 28px; max-width: 60ch; }
/* --- Method --------------------------------------------------------------- */
-.rules { border-top: 1px solid var(--line); }
-.rule {
- display: grid;
- grid-template-columns: 64px minmax(0, 26ch) minmax(0, 1fr);
- gap: clamp(20px, 3vw, 48px);
- padding-block: 32px;
- border-bottom: 1px solid var(--line);
- align-items: start;
-}
-.rule .num {
- font-family: var(--font-mono);
- font-size: .8125rem;
- color: var(--fg-3);
- padding-top: 6px;
- font-variant-numeric: tabular-nums;
-}
-.rule h3 { padding-top: 1px; }
-.rule p { color: var(--fg-2); margin: 0; }
-.rule code {
- color: var(--accent-2);
- background: var(--accent-dim);
- padding: 1px 5px;
-}
+.rules { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 22px; }
+.rule { border-top: 3px solid var(--bright); padding-top: 18px; }
+.rule .num { font-family: var(--font-mono); font-size: .75rem; font-weight: 700; color: var(--ink-3); display: block; margin-bottom: 8px; }
+.band-dark .rule .num { color: var(--dark-ink-2); }
+.rule h3 { margin-bottom: 10px; }
+.rule p { color: var(--ink-2); font-size: .9375rem; }
+.rule code { color: var(--accent); background: var(--accent-wash); padding: 1px 5px; border-radius: 3px; }
-/* --- Definition rows (licensing, contributing) ---------------------------- */
+/* `.band-dark p` and `.rule p` have the same specificity, so on the dark band
+ the later rule won and the paragraphs came out at 2.4:1. These overrides are
+ one class deeper on purpose, so the band always beats the component. */
+.band-dark .rule p { color: var(--dark-ink-2); }
+.band-dark .rule code { color: var(--accent-2); background: rgba(255, 255, 255, .07); }
+
+/* --- Definition rows ------------------------------------------------------ */
.rows { border-top: 1px solid var(--line); }
.rows > div {
- display: grid;
- grid-template-columns: minmax(0, 22ch) minmax(0, 1fr);
- gap: clamp(20px, 3vw, 48px);
- padding-block: 24px;
- border-bottom: 1px solid var(--line);
+ display: grid; grid-template-columns: minmax(0, 22ch) minmax(0, 1fr);
+ gap: clamp(16px, 3vw, 44px);
+ padding-block: 22px; border-bottom: 1px solid var(--line);
}
-.rows dt { font-weight: 600; font-size: .9375rem; }
-.rows dd { color: var(--fg-2); }
-.rows dd a { color: var(--fg); text-underline-offset: 3px; }
+.rows dt { font-weight: 700; font-size: .9375rem; }
+.rows dd { color: var(--ink-2); }
+.rows dd a { color: var(--accent); text-underline-offset: 3px; }
+.rows code { background: var(--ground-2); padding: 1px 5px; border-radius: 3px; }
/* --- Footer --------------------------------------------------------------- */
-.site-footer { padding-block: clamp(56px, 6vw, 88px) 40px; }
-
+.site-footer { background: var(--dark); color: var(--dark-ink); padding-block: clamp(48px, 6vw, 76px) 32px; }
+.site-footer .brand { color: var(--dark-ink); }
.footer-grid {
- display: grid;
- grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
- gap: clamp(28px, 4vw, 64px);
- padding-bottom: 48px;
-}
-.footer-brand p { color: var(--fg-3); font-size: .875rem; margin-top: 16px; max-width: 34ch; }
-.footer-col h4 {
- font-size: .6875rem;
- font-weight: 600;
- letter-spacing: .13em;
- text-transform: uppercase;
- color: var(--fg-3);
- margin-bottom: 16px;
-}
-.footer-col li { margin-bottom: 10px; }
-.footer-col a {
- font-size: .875rem;
- color: var(--fg-2);
- text-decoration: none;
- transition: color .15s;
-}
-.footer-col a:hover { color: var(--fg); }
-
-/* A link whose destination is in the other language says so, rather than
- showing the reader a label they cannot read and letting them find out by
- clicking. The switcher itself is exempt: it is written in the language it
- points at on purpose, because that is what a speaker of that language
- recognises. */
-.lang-note { color: var(--fg-3); }
-
+ display: grid; grid-template-columns: minmax(0, 1.7fr) repeat(3, minmax(0, 1fr));
+ gap: clamp(24px, 4vw, 56px); padding-bottom: 40px;
+}
+.footer-brand p { color: var(--dark-ink-2); font-size: .875rem; margin-top: 14px; max-width: 34ch; }
+.footer-col h4 { font-size: .6875rem; font-weight: 700; letter-spacing: .13em; text-transform: uppercase; color: var(--dark-ink-2); margin-bottom: 14px; }
+.footer-col li { margin-bottom: 9px; }
+.footer-col a { font-size: .875rem; color: var(--dark-ink); text-decoration: none; opacity: .82; transition: opacity .15s, color .15s; }
+.footer-col a:hover { opacity: 1; color: var(--bright); }
.footer-bottom {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
- gap: 12px;
- padding-top: 28px;
- border-top: 1px solid var(--line);
- color: var(--fg-3);
- font-size: .8125rem;
+ display: flex; flex-wrap: wrap; justify-content: space-between; gap: 12px;
+ padding-top: 26px; border-top: 1px solid var(--dark-line);
+ color: var(--dark-ink-2); font-size: .8125rem;
}
+.footer-bottom a { color: var(--dark-ink); }
/* --- Legal page and 404 ---------------------------------------------------
- These two pages keep the class names they were written with. The legal text
- is a document with its own edition date, so it is styled here rather than
+ These two keep the class names they were written with. The legal text is a
+ document with its own edition date, so it is styled here rather than
rewritten to match a new set of classes.
--------------------------------------------------------------------------- */
-/* Tokens the two pages reference from inline styles. */
-:root {
- --text-faint: var(--fg-3);
- --text-muted: var(--fg-2);
- --border: var(--line);
-}
-
-.prose,
-.legal { max-width: 78ch; }
-.prose h2,
-.legal h2 { font-size: clamp(1.4rem, 2.2vw, 1.9rem); margin: 56px 0 18px; }
-.prose h3,
-.legal h3 { font-size: 1.0625rem; margin: 30px 0 10px; }
-.prose p, .prose li,
-.legal p, .legal li { color: var(--fg-2); margin-bottom: 13px; }
-.prose ul, .prose ol,
-.legal ul, .legal ol { padding-left: 22px; }
-.prose a,
-.legal a { color: var(--fg); text-underline-offset: 3px; }
-.legal code,
-.prose code { color: var(--accent-2); background: var(--accent-dim); padding: 1px 5px; }
-
-.lm-fine { font-size: .8125rem; color: var(--fg-3); }
-
-/* The eyebrow marker on pages written before the rule replaced the dot. */
-.eyebrow .dot {
- display: inline-block;
- width: 28px;
- height: 1px;
- background: var(--accent);
- vertical-align: middle;
- margin-right: 12px;
- margin-bottom: 2px;
-}
-
-/* Section index on the legal page: sharp bordered links, not pills. */
-.chips { display: flex; flex-wrap: wrap; gap: 10px; }
-.chip {
- font-size: .8125rem;
- color: var(--fg-2);
- text-decoration: none;
- border: 1px solid var(--line-2);
- padding: 8px 14px;
- transition: color .15s, border-color .15s;
-}
-.chip:hover { color: var(--fg); border-color: var(--fg-3); }
-.chip-accent:hover { border-color: var(--accent); color: var(--accent-2); }
+:root { --text-faint: var(--ink-3); --text-muted: var(--ink-2); --border: var(--line); }
+
+.prose, .legal { max-width: 78ch; }
+.prose h2, .legal h2 { font-size: clamp(1.4rem, 2.2vw, 1.85rem); margin: 52px 0 16px; }
+.prose h3, .legal h3 { font-size: 1.0625rem; margin: 28px 0 10px; }
+.prose p, .prose li, .legal p, .legal li { color: var(--ink-2); margin-bottom: 12px; }
+.prose ul, .prose ol, .legal ul, .legal ol { padding-left: 22px; }
+.prose a, .legal a { color: var(--accent); text-underline-offset: 3px; }
+.legal code, .prose code { background: var(--ground-2); padding: 1px 5px; border-radius: 3px; }
+.lm-fine { font-size: .8125rem; color: var(--ink-3); }
+
+.eyebrow .dot { display: none; }
-/* The "what the site actually does" table reuses the datasheet rhythm. */
.facts { border-top: 1px solid var(--line-2); }
.facts > div {
- display: grid;
- grid-template-columns: 1fr auto;
- gap: 20px;
- align-items: baseline;
- padding: 12px 0;
- border-bottom: 1px solid var(--line);
+ display: grid; grid-template-columns: 1fr auto; gap: 20px; align-items: baseline;
+ padding: 12px 0; border-bottom: 1px solid var(--line);
}
-.facts dt { font-size: .875rem; color: var(--fg-3); }
-.facts dd { font-size: .9375rem; font-weight: 600; text-align: right; }
+.facts dt { font-size: .875rem; color: var(--ink-3); }
+.facts dd { font-size: .9375rem; font-weight: 700; text-align: right; }
-/* 404 */
-.notfound { padding-block: clamp(80px, 12vw, 160px); }
+.notfound { padding-block: clamp(72px, 11vw, 140px); }
.notfound .code {
- font-family: var(--font-mono);
- font-size: clamp(3rem, 9vw, 5.5rem);
- font-weight: 600;
- letter-spacing: -.04em;
- color: var(--fg-3);
- margin-bottom: 12px;
+ font-family: var(--font-mono); font-size: clamp(3rem, 9vw, 5rem);
+ font-weight: 800; letter-spacing: -.04em; color: var(--ink-3); margin-bottom: 12px;
}
/* --- Responsive ----------------------------------------------------------- */
@media (max-width: 1000px) {
- .project,
- .course { grid-template-columns: minmax(0, 1fr); }
- .spec-strip { grid-template-columns: repeat(2, 1fr); }
- .spec-strip > div:nth-child(2n) { border-right: 0; }
- .spec-strip > div:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
.footer-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 760px) {
- .icon-btn { display: inline-flex; }
-
+ .nav-toggle { display: inline-flex; }
.nav {
- display: none;
- position: absolute;
- top: 62px;
- left: 0;
- right: 0;
- flex-direction: column;
- align-items: stretch;
- gap: 0;
- background: var(--bg-2);
- border-bottom: 1px solid var(--line);
- padding: 8px var(--gutter) 20px;
+ display: none; position: absolute; top: 66px; left: 0; right: 0;
+ flex-direction: column; align-items: stretch; gap: 0;
+ background: var(--ground); border-bottom: 1px solid var(--line);
+ padding: 6px var(--gutter) 18px;
}
.nav[data-open="true"] { display: flex; }
.nav a { padding: 13px 0; border-bottom: 1px solid var(--line); font-size: .9375rem; }
.nav a::after { display: none; }
- .rule { grid-template-columns: 40px minmax(0, 1fr); }
- .rule p { grid-column: 2; }
-
- .rows > div { grid-template-columns: minmax(0, 1fr); gap: 8px; }
-
- .spec-strip { grid-template-columns: minmax(0, 1fr); }
- .spec-strip > div { border-right: 0; border-bottom: 1px solid var(--line); padding-right: 0; }
- .spec-strip > div:last-child { border-bottom: 0; }
-
+ .rows > div { grid-template-columns: minmax(0, 1fr); gap: 6px; }
.footer-grid { grid-template-columns: minmax(0, 1fr); }
}
diff --git a/assets/site.js b/assets/site.js
index 58d6c51..acd6215 100644
--- a/assets/site.js
+++ b/assets/site.js
@@ -1,11 +1,32 @@
/* DrobyshevDev — site behaviour. Progressive enhancement only:
- every section is readable and every link works with this file removed.
-
- There is no theme code here any more. The site commits to one scheme, so
- there is nothing to toggle and nothing to remember between visits. */
+ every section is readable and every link works with this file removed. */
(function () {
"use strict";
+ /* --- Theme ------------------------------------------------------------ */
+ // The stored value is applied by an inline script in so the page never
+ // paints in the wrong theme; this only handles the toggle itself. With
+ // nothing stored the system preference decides, which is why the toggle
+ // reads the media query rather than assuming light.
+ var root = document.documentElement;
+
+ function currentTheme() {
+ var explicit = root.getAttribute("data-theme");
+ if (explicit === "dark" || explicit === "light") return explicit;
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+ }
+
+ var toggle = document.querySelector(".theme-toggle");
+ if (toggle) {
+ toggle.setAttribute("aria-label", toggle.getAttribute("data-label-" + currentTheme()));
+ toggle.addEventListener("click", function () {
+ var next = currentTheme() === "dark" ? "light" : "dark";
+ root.setAttribute("data-theme", next);
+ try { localStorage.setItem("theme", next); } catch (e) { /* private mode */ }
+ toggle.setAttribute("aria-label", toggle.getAttribute("data-label-" + next));
+ });
+ }
+
/* --- Mobile navigation ------------------------------------------------ */
var navToggle = document.querySelector(".nav-toggle");
var nav = document.getElementById("primary-nav");
@@ -35,34 +56,6 @@
});
}
- /* --- Copy buttons ----------------------------------------------------- */
- // The command text lives in data-copy rather than being scraped from the
- // DOM, so the prompt characters ($, #) are never copied with it.
- document.querySelectorAll("[data-copy]").forEach(function (btn) {
- btn.addEventListener("click", function () {
- var text = btn.getAttribute("data-copy");
- var done = function () {
- btn.setAttribute("data-copied", "true");
- setTimeout(function () { btn.removeAttribute("data-copied"); }, 1600);
- };
- if (navigator.clipboard && window.isSecureContext) {
- navigator.clipboard.writeText(text).then(done, fallback);
- } else {
- fallback();
- }
- function fallback() {
- var ta = document.createElement("textarea");
- ta.value = text;
- ta.setAttribute("readonly", "");
- ta.style.cssText = "position:absolute;left:-9999px";
- document.body.appendChild(ta);
- ta.select();
- try { document.execCommand("copy"); done(); } catch (e) { /* nothing to do */ }
- document.body.removeChild(ta);
- }
- });
- });
-
/* --- Current year ----------------------------------------------------- */
document.querySelectorAll("[data-year]").forEach(function (el) {
el.textContent = String(new Date().getFullYear());
diff --git a/index.html b/index.html
index 67649ff..8f63a62 100644
--- a/index.html
+++ b/index.html
@@ -9,7 +9,8 @@
-
+
+
@@ -29,6 +30,14 @@
+
+
+
Перейти к содержимому
@@ -34,7 +43,7 @@
@@ -191,11 +200,15 @@
6. Защита персональных данных
гарантирующих их сохранность и исключающих несанкционированный доступ.
7. Файлы cookie
-
7.1. Сайт не устанавливает файлы cookie и не сохраняет никаких данных в браузере
- Пользователя: ни в локальном хранилище (localStorage), ни в хранилище
- сессии, ни иным способом.
-
7.2. Запрет локального хранилища настройками браузера не влияет на работу Сайта,
- потому что Сайт им не пользуется.
+
7.1. Сайт не устанавливает файлы cookie. Единственное, что он сохраняет в браузере
+ Пользователя, — выбранная тема оформления, светлая или тёмная, в локальном хранилище
+ (localStorage) под ключом theme. Это значение не передаётся
+ на сервер, не связано с личностью Пользователя и удаляется вместе с данными сайта
+ средствами браузера.
+
7.2. Пока выбор не сделан, тема берётся из системной настройки и не сохраняется
+ никуда. Пользователь может в любое время очистить локальное хранилище или запретить
+ его использование настройками браузера: тогда тема перестанет запоминаться между
+ посещениями, а остальные функции Сайта продолжат работать.
8. Заключительные положения
8.1. Настоящая политика является общедоступным документом и размещена на Сайте в
diff --git a/ru/index.html b/ru/index.html
index 08bd38f..9600185 100644
--- a/ru/index.html
+++ b/ru/index.html
@@ -9,7 +9,8 @@
-
+
+
@@ -29,6 +30,14 @@
+
+