
:root {
  --s1: 2px;
  --s2: 4px;
  --s3: 6px;
  --s4: 8px;
  --s5: 10px;
  --s6: 12px;
  --s7: 14px;
  --s8: 16px;
  --s9: 20px;
  --s10: 24px;
  --s11: 32px;
  --s12: 40px;
  --s13: 48px;

  --t1: 9px;
  --t2: 10px;
  --t3: 11px;
  --t4: 12.5px;
  --t5: 13.5px;
  --t6: 15px;
  --t7: 17px;
  --t8: 20px;
  --t9: 24px;
  --t10: 30px;
  --t11: 40px;

  /* Small text is read in bulk and wants air; a heading is one line and a
     loose one reads as two. */
  --lh1: 1.4;
  --lh2: 1.4;
  --lh3: 1.45;
  --lh4: 1.5;
  --lh5: 1.55;
  --lh6: 1.6;
  --lh7: 1.5;
  --lh8: 1.35;
  --lh9: 1.25;
  --lh10: 1.2;
  --lh11: 1.1;

  --r1: 2px;
  --r2: 4px;
  --r3: 6px;
  --r4: 8px;
  --r5: 10px;
  --r6: 12px;
  --r7: 16px;
  --r-pill: 999px;

  /* How long a state takes to arrive. Fifty-three rules across the sheets
     spelled twenty-two different durations for the one thing a hover does,
     and a screen where two neighbours settle at different moments reads as
     two screens. Long enough to be seen, short enough not to be waited for. */
  --quick: .15s ease;

  /* The three measures every product has and each had named for itself —
     `--k-page`/`--k-prose`/`--k-gutter` in the portal, a literal 1240 in nine
     of the portal's rules and in the front page's wrapper, `container` and
     `container-narrow` in the console's. They are *measures* and not steps
     (UI-42), which is why they are lengths here and not rungs; what they are
     not is one product's, so they are named once and a product that wants a
     different page retunes the name rather than inventing one.

     `--measure` and not `--page`: `--page` is the ground a page is painted
     on, and a width called the same thing as a colour is an hour lost the
     first time somebody reaches for the wrong one. */
  --measure: 1240px;
  --prose: 62ch;
  --gutter: clamp(16px, 2.4vw, 44px);
}

/* comm.ee's own look (SITE-8, UIC-17): the ground, the ink, the gold and the
   two faces the front page is set in — kept in Platform so the front page
   and the assistant's door (`ai.<base>`) read one file and agree.

   Three states, not two: the reader's system setting, and the reader
   overruling it either way (`data-theme="light"` / `"dark"` on the root).
   The tier's own pattern in the tier's own order, so nothing needs
   `!important`: the media block is `:root:not([data-theme="light"])` so a
   reader who chose light on a dark machine keeps light, and the explicit
   dark block follows it so a reader who chose dark on a light machine
   gets dark.

   The palette is written onto the *shared* token names as well as its own:
   every `ui-*` control reads --surface, --ink, --line and their kin, so
   setting those from the `--hp-*` values is what makes the controls wear
   this look without a second stylesheet knowing about it (UIC-10: a look
   is modifications on the one set, not a palette of its own). */

:root {
  --hp-bg: #F1F1F0;
  --hp-surface: #FFFFFF;
  --hp-line: #E3E3E1;
  --hp-line2: #CFCFCB;
  --hp-ink: #111111;
  --hp-ink2: #45443F;
  --hp-mute: #6B6A64;
  --hp-on-ink: #F1F1F0;
  --hp-nav: rgba(241, 241, 240, 0.8);
  --hp-link: #7A5B1C;
  --hp-link-hover: #5E4512;
  --hp-gold: #8A6A2A;
  /* How deep a system's tone sinks into the ground for its band: enough to
     tell the bands apart, not enough to stop the page being the ground. */
  --hp-tint: 8%;
  /* The four tones are the same on either ground. */
  --hp-indigo: #5B5BF0;
  --hp-teal: #10C8B4;
  --hp-coral: #FF6B54;
  --hp-amber: #FFB93D;

  /* The faces: a serif with character for the headlines, a plain sans for
     the rest. Loaded by the page's head; these are the names the tier's
     controls set their type in. */
  --sans: 'Instrument Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --serif: 'Fraunces', 'Lora', Georgia, serif;
}

/* The shared names, at the weight of the tier's own scheme blocks: the
   tier writes its dark under `:root:not([data-theme="light"])`, which
   outweighs a plain `:root` whatever the order, so a mapping written there
   held in light and lost in dark — the assistant's ground came out the
   tier's warm grey on a dark machine, not this black. Every root matches
   one of these two, and both weigh what the tier's blocks weigh. */
:root:not([data-theme="light"]),
:root[data-theme="light"] {
  --bg: var(--hp-bg);
  --page: var(--hp-bg);
  --surface: var(--hp-surface);
  --mid: color-mix(in oklab, var(--hp-bg) 50%, var(--hp-surface));
  --rail: var(--hp-bg);
  --field: var(--hp-surface);
  --hover: color-mix(in oklab, var(--hp-surface) 92%, var(--hp-ink));
  --chip: var(--hp-line);
  --line: var(--hp-line2);
  --line2: var(--hp-line2);
  --hair: var(--hp-line);
  --edge: var(--hp-line2);
  --ring: var(--hp-gold);
  --ink: var(--hp-ink);
  --ink2: var(--hp-ink2);
  --ink3: var(--hp-ink2);
  --mute: var(--hp-mute);
  --faint: var(--hp-mute);
  --rail-ink: var(--hp-mute);
  /* The page's own gold, which is what it spends attention with everywhere
     else — the links, the hover, the mark. */
  --accent: var(--hp-gold);
  --accent-down: var(--hp-link-hover);
  --accent-line: var(--hp-gold);
  --accent-ink: var(--hp-on-ink);
  --accent-deep: var(--hp-link-hover);
  --accent-text: var(--hp-link);
  --chip-ink: var(--hp-link);
  --accent-soft: color-mix(in oklab, var(--hp-gold) 14%, transparent);
  --wash: color-mix(in oklab, var(--hp-gold) 9%, transparent);
  --solid: var(--hp-ink);
  --solid-ink: var(--hp-on-ink);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --hp-bg: #050505;
    --hp-surface: #121212;
    --hp-line: #1E1E1E;
    --hp-line2: #343434;
    --hp-ink: #F3EFE8;
    --hp-ink2: #CFCAC0;
    --hp-mute: #9A958B;
    --hp-on-ink: #050505;
    --hp-nav: rgba(5, 5, 5, 0.72);
    --hp-link: #E2C27A;
    --hp-link-hover: #F1D79A;
    --hp-gold: #D9B36A;
    --hp-tint: 9%;
  }
}
:root[data-theme="dark"] {
  --hp-bg: #050505;
  --hp-surface: #121212;
  --hp-line: #1E1E1E;
  --hp-line2: #343434;
  --hp-ink: #F3EFE8;
  --hp-ink2: #CFCAC0;
  --hp-mute: #9A958B;
  --hp-on-ink: #050505;
  --hp-nav: rgba(5, 5, 5, 0.72);
  --hp-link: #E2C27A;
  --hp-link-hover: #F1D79A;
  --hp-gold: #D9B36A;
  --hp-tint: 9%;
}

/* The front page's dress (SITE-3, 2026-09-02/03): black, quiet, and set in
   a light serif. The page is the black itself — no picture and no gradient
   painted behind anything — with warm paper for the words, pills for the
   buttons, a screen's height for every section where the window has it,
   and each system a band the whole width of the window in a deep tint of
   its own tone. The scheme is the system's unless the switch says
   (UIC-17, 2026-09-08): the palette and the faces are Platform's
   `look.css` (SITE-8), served in front of this sheet and read by the
   assistant's door too, so the front page and its assistant agree. */

:root {
  /* The bar's height, which the hero is a screen less and the anchors
     land under: one number, so the first screen ends where the second
     begins. */
  --hp-bar: 80px;
  --hp-radius: var(--r2);
  /* A picture's corner (2026-09-07, "comm.ee border radiuse piltidel ka
     3px"): the screens, the proof shots and the portrait wear the same
     small corner the portal's own boxes do (KODU-102), not the ladder's
     sixteen — a screenshot in a soft frame read as a card, and it is a
     window. */
  --hp-shot-radius: 3px;
}

/* A tone is one answer here: the colour its band sinks into the ground. */
.tone-indigo {
  --tone: var(--hp-indigo);
}
.tone-teal {
  --tone: var(--hp-teal);
}
.tone-coral {
  --tone: var(--hp-coral);
}
.tone-amber {
  --tone: var(--hp-amber);
}

/* An anchor glides (SITE-4), unless the reader asked for less motion. The
   sticky bar's height is the margin, so a heading lands under the bar and
   not behind it; where a section is a screen the margin is nothing, since
   the bar floats over the screen's own empty top and the screen is meant
   to fill the window whole — the same place the wheel lands it. */
@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}
[id] { scroll-margin-top: var(--hp-bar); }
body {
  margin: 0;
  background: var(--hp-bg);
  color: var(--hp-ink);
  font-family: 'Instrument Sans', sans-serif;
  font-size: 17px;
  line-height: 1.6;
  transition: background 0.3s, color 0.3s;
  -webkit-font-smoothing: antialiased;
}
main { display: block; }
a { color: var(--hp-link); text-decoration: none; }
a:hover { color: var(--hp-link-hover); }
img { max-width: 100%; }
::selection { background: var(--hp-gold); color: #0C0B0A; }

.hp-wrap { max-width: var(--measure); margin: 0 auto; padding-inline: clamp(20px, 4vw, 48px); }

/* ── type ────────────────────────────────────────────────────────────────── */

/* The headlines: a light serif in ordinary sentence case — the one voice,
   from the hero to the numbers. Capitals were tried and were hard to read. */
.hp-h1, .hp-h2, .hp-h3, .hp-stat {
  font-family: 'Fraunces', 'Lora', Georgia, serif;
  font-optical-sizing: auto;
  font-weight: 300;
  letter-spacing: -0.015em;
  margin: 0;
}
.hp-h1 { font-size: clamp(52px, 8.2vw, 132px); line-height: 1; letter-spacing: -0.025em; text-wrap: balance; }
.hp-h2 { font-size: clamp(36px, 5vw, 76px); line-height: 1.04; text-wrap: balance; }
.hp-h3 { font-size: 24px; line-height: 1.25; letter-spacing: 0; font-weight: 400; }
.hp-h3.is-row { font-size: clamp(40px, 5.4vw, 84px); line-height: 1; letter-spacing: -0.02em; }
.hp-accent { color: var(--hp-gold); }
/* The label over a section: small, a little spaced, grey. */
.hp-kicker {
  font-family: 'Instrument Sans', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--hp-mute);
  margin: 0;
}
.hp-kicker.is-amber { color: var(--hp-mute); }
.hp-sub { color: var(--hp-ink2); font-size: clamp(17px, 1.35vw, 20px); margin: 0; max-width: 640px; text-wrap: pretty; }

/* ── the bar across the top ─────────────────────────────────────────────── */

.hp-top {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--hp-nav);
  backdrop-filter: blur(12px);
}
.hp-top-inner { display: flex; align-items: center; gap: var(--s11); height: var(--hp-bar); box-sizing: border-box; }
.hp-logo svg { height: 24px; width: auto; display: block; }
.hp-nav {
  display: flex;
  gap: var(--s11);
  margin-left: auto;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.hp-nav a { color: var(--hp-ink2); }
.hp-nav a:hover { color: var(--hp-ink); }
/* The scheme press is the tier's control now (UIC-18) — the ring, the size
   and the states are `.ui-theme`. What is left here is the one thing the
   tier cannot know: this page is painted in its own poster palette, so the
   control has to be told which ink it is standing on. Adding to a control
   and never redrawing one is the rule the sheet order is for. */
.hp-top .ui-theme { border-color: var(--hp-line2); color: var(--hp-ink); }
.hp-top .ui-theme:hover { border-color: var(--hp-ink); }

/* ── buttons: pills, thin ───────────────────────────────────────────────── */

.hp-btn {
  display: inline-block;
  background: transparent;
  border: 1px solid color-mix(in srgb, var(--hp-ink) 55%, transparent);
  color: var(--hp-ink);
  padding: var(--s6) var(--s10);
  border-radius: var(--r-pill);
  font-weight: 500;
  font-size: 15px;
  letter-spacing: 0.01em;
  transition: transform var(--quick), background var(--quick), color var(--quick), border-color var(--quick);
}
.hp-btn:hover { transform: translateY(-1px); color: var(--hp-ink); border-color: var(--hp-ink); }
.hp-btn-primary { background: var(--hp-ink); border-color: var(--hp-ink); color: var(--hp-on-ink); }
.hp-btn-primary:hover { background: var(--hp-gold); border-color: var(--hp-gold); color: #050505; }
.hp-btn-ink { background: transparent; border-color: color-mix(in srgb, var(--hp-ink) 55%, transparent); color: var(--hp-ink); }
.hp-btn-ink:hover { background: var(--hp-ink); border-color: var(--hp-ink); color: var(--hp-on-ink); }
.hp-top-cta { padding: var(--s4) var(--s8); white-space: nowrap; flex: none; }
.hp-btn-plain {
  color: var(--hp-ink2);
  padding: var(--s6) var(--s4);
  font-weight: 500;
  font-size: 15px;
}
.hp-btn-plain:hover { color: var(--hp-ink); }

/* ── the hero ───────────────────────────────────────────────────────────── */

/* Centred, with the black all around it, a screen tall: the sentence is
   the picture. The top bar's height is taken off, so the hero is the first
   screen exactly and the work begins on the second. */
.hp-hero-say {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: clamp(24px, 3vw, 40px);
  min-height: calc(100svh - var(--hp-bar));
  max-width: 1040px;
  padding-block: clamp(48px, 6vw, 96px);
}
.hp-lede { font-size: clamp(17px, 1.5vw, 21px); max-width: 640px; margin: 0; color: var(--hp-ink2); text-wrap: pretty; }
.hp-facts-row {
  display: flex;
  gap: var(--s9);
  flex-wrap: wrap;
  justify-content: center;
  font-family: 'Instrument Sans', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  color: var(--hp-mute);
  list-style: none;
  margin: 0;
  padding: 0;
}
.hp-facts-row li + li::before { content: "·"; margin-right: var(--s9); }

/* ── sections ───────────────────────────────────────────────────────────── */

/* Every section is a screen of its own where the window is tall enough
   for that: the title and its content stand centred in a screen's height,
   and the reader arrives at each one whole. Below that, generous padding
   does the same work without the empty room. `.hp-screen` in the markup
   names the same sections for the script, which glides the wheel from one
   to the next in the same window (SITE-4). */
.hp-section { padding-block: clamp(96px, 12vw, 176px); }
.hp-lede-block {
  display: flex;
  flex-direction: column;
  gap: var(--s10);
  max-width: 880px;
  padding-block: clamp(96px, 12vw, 176px) clamp(56px, 7vw, 96px);
}
.hp-section .hp-lede-block { padding-block: 0 clamp(56px, 7vw, 96px); }
/* The work's own title stands alone on a screen before the first band. */
.hp-work > .hp-wrap .hp-lede-block { padding-block: clamp(96px, 12vw, 176px); }
@media (min-width: 980px) and (min-height: 640px) {
  .hp-work > .hp-wrap { min-height: 100svh; }
  .hp-band .hp-row { min-height: 100svh; }
  .hp-ways-band .hp-section,
  .hp-proof .hp-section,
  .hp-band-surface .hp-section {
    min-height: 100svh;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  .hp-who { min-height: 100svh; }
  .hp-screen, .hp-work { scroll-margin-top: 0; }
}

/* ── the work: one band per system, the whole width of the window ───────── */

.hp-band {
  background: color-mix(in srgb, var(--tone) var(--hp-tint), var(--hp-bg));
}
.hp-band .hp-kicker { color: color-mix(in srgb, var(--tone) 70%, var(--hp-ink)); }

.hp-row {
  display: grid;
  grid-template-columns: 5fr 7fr;
  gap: clamp(32px, 5vw, 80px);
  align-items: center;
  align-content: center;
  padding-block: clamp(80px, 9vw, 136px);
}
/* Flipped, the picture keeps the wider track: order alone would hand the
   screen the narrow column and call it a checkerboard. The flip is the
   band's (`work.rs`), the grid its inner row's. */
.hp-band.is-flip .hp-row { grid-template-columns: 7fr 5fr; }
.hp-band.is-flip .hp-row-say { order: 2; }
.hp-band.is-flip .hp-frame-mat { order: 1; }
.hp-row-body { margin: 0; font-size: clamp(17px, 1.3vw, 19px); max-width: 520px; color: var(--hp-ink2); }
.hp-points {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  font-size: 15.5px;
  color: var(--hp-ink2);
}
.hp-tick { color: var(--tone, var(--hp-gold)); flex: none; }
.hp-row-acts .hp-btn-ink { padding: var(--s6) var(--s9); }
/* The screen stands on the band with nothing around it: a flat address bar
   over the picture, one hairline, no shadow — and it lifts a little under
   the pointer, the one motion a picture earns. */
.hp-frame {
  margin: 0;
  min-width: 0;
  border-radius: var(--hp-shot-radius);
  overflow: hidden;
  background: var(--hp-surface);
  border: 1px solid var(--hp-line2);
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.hp-frame:hover { transform: translateY(-4px); }
.hp-chrome {
  display: flex;
  align-items: center;
  gap: var(--s4);
  padding: var(--s5) var(--s7);
  background: var(--hp-surface);
  border-bottom: 1px solid var(--hp-line);
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: var(--hp-mute);
}
.hp-chrome-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--hp-line2); flex: none; }
.hp-chrome-at { margin-left: var(--s4); }
.hp-frame > img { width: 100%; height: auto; display: block; }

/* The band's screens turn by themselves (SITE-7, revised 2026-09-07): the
   tier's gallery in the frame, every screen of the product, the next one
   every three seconds. The slide's caption is its address, drawn here as
   the bar over the picture — so the bar follows the turn — with the two
   dots the bar always wore; the dots under the strip are the carousel's,
   and there are no thumbnails. */
.hp-band-album .ui-gal-main { border-radius: 0; max-height: none; aspect-ratio: 8 / 5; background: var(--hp-surface); }
.hp-band-album .ui-gal-captions {
  order: -1;
  display: flex;
  align-items: center;
  min-height: 0;
  padding: var(--s5) var(--s7);
  background: var(--hp-surface);
  border-bottom: 1px solid var(--hp-line);
  font-family: 'JetBrains Mono', monospace;
  font-size: 12px;
  color: var(--hp-mute);
}
/* The bar is one line, and an address longer than the bar is cut with an
   ellipsis rather than wrapped or let out: on a phone the bar widened to
   the address and took the picture past the screen's edge with it. */
.hp-band-album .ui-gal-caption.is-on {
  display: flex; align-items: center; min-width: 0; font: inherit; color: inherit; line-height: 1;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.hp-band-album .ui-gal-caption.is-on::before {
  content: "";
  flex: none;
  width: 8px;
  height: 8px;
  margin-right: var(--s8);
  border-radius: 50%;
  background: var(--hp-line2);
  box-shadow: var(--s8) 0 0 var(--hp-line2);
}
.hp-band-album .ui-gal-dots { padding: var(--s4) 0 var(--s5); background: var(--hp-surface); }
.hp-band-album .ui-gal-dot { background: var(--hp-line2); }
.hp-band-album .ui-gal-dot:hover { background: var(--hp-mute); }
.hp-band-album .ui-gal-dot.is-on { background: var(--hp-ink); }

/* ── the three ways in: columns, a rule over each, no cards ─────────────── */

.hp-ways-band { border-top: 1px solid var(--hp-line); }
.hp-ways { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(28px, 4vw, 64px); }
.hp-way {
  display: flex;
  flex-direction: column;
  gap: var(--s8);
  border-top: 1px solid var(--hp-line2);
  padding-top: var(--s10);
}
.hp-way-no {
  display: flex;
  justify-content: space-between;
  font-family: 'Instrument Sans', sans-serif;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.06em;
  color: var(--hp-mute);
  margin: 0;
}
.hp-way-badge { color: var(--hp-ink); }
.hp-way-body { color: var(--hp-ink2); font-size: 16px; margin: 0; flex: 1; }
.hp-way-act {
  color: var(--hp-ink);
  font-weight: 500;
  font-size: 15px;
}
.hp-way-act:hover { color: var(--hp-gold); }

/* ── the proof: the numbers, large ──────────────────────────────────────── */

.hp-proof { border-top: 1px solid var(--hp-line); }
.hp-proof-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(24px, 3vw, 48px); }
.hp-proof-card {
  display: flex;
  flex-direction: column;
  gap: var(--s5);
  border-top: 1px solid var(--hp-line2);
  padding-top: var(--s10);
}
.hp-stat { font-size: clamp(56px, 6.5vw, 108px); line-height: 1; color: var(--hp-ink); letter-spacing: 0; }
.hp-proof-name { color: var(--hp-ink); font-size: 16px; }
.hp-proof-body { color: var(--hp-mute); font-size: 14.5px; margin: 0; }
.hp-proof-shots { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 3vw, 40px); margin-top: clamp(48px, 5vw, 80px); }
.hp-proof-shot { margin: 0; border-radius: var(--hp-shot-radius); overflow: hidden; border: 1px solid var(--hp-line2); }
.hp-proof-shot img { width: 100%; height: auto; display: block; }
.hp-proof-shot figcaption {
  padding: var(--s7) var(--s8);
  font-size: 14px;
  color: var(--hp-mute);
  background: var(--hp-surface);
}
.hp-proof-shot strong { color: var(--hp-ink); font-weight: 600; }

/* ── who you'd be talking to ────────────────────────────────────────────── */

.hp-who { display: grid; grid-template-columns: 4fr 8fr; gap: clamp(32px, 5vw, 80px); align-items: center; border-top: 1px solid var(--hp-line); }
.hp-portrait {
  width: min(100%, 340px);
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: var(--hp-shot-radius);
  display: block;
}
.hp-who-say .hp-sub { max-width: 640px; }
.hp-promise { margin: 0; font-family: 'Fraunces', 'Lora', Georgia, serif; font-weight: 300; font-size: clamp(22px, 2vw, 30px); line-height: 1.25; }
.hp-who-facts {
  display: flex;
  gap: var(--s10);
  flex-wrap: wrap;
  font-family: 'Instrument Sans', sans-serif;
  font-size: 14px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--hp-mute);
}
.hp-who-facts a { color: var(--hp-link); }

/* ── contact ────────────────────────────────────────────────────────────── */

/* One column down the middle (SITE-5): the words, the two ways to reach us
   on one line, and the form under them at a reading width — no card around
   it, the black is the paper. Padded a little less than the other sections,
   so the whole of it fits the screen it is given. */
.hp-band-surface { border-top: 1px solid var(--hp-line); }
.hp-contact {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--s11);
  padding-block: 96px var(--s12);
}
.hp-contact-say {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--s7);
  max-width: 760px;
}
.hp-contact-say .hp-sub { max-width: 720px; }
.hp-reaches { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--s6) var(--s10); font-size: 17px; }
.hp-reach { color: var(--hp-ink); }
.hp-reach:hover { color: var(--hp-gold); }
.hp-reach + .hp-reach::before { content: "·"; color: var(--hp-mute); margin-right: var(--s10); }
.hp-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--tone, var(--hp-mute)); flex: none; }
.hp-dot.is-big { width: 12px; height: 12px; margin-top: var(--s3); }
.hp-note { display: flex; justify-content: center; max-width: 640px; }
.hp-note .hp-dot { display: none; }
.hp-note p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--hp-mute); }
.hp-note strong { color: var(--hp-ink2); font-weight: 500; }

/* The form: hairline boxes on the black, and over each a label that is
   small, light and a little spaced — the page's label voice, not a form's
   bold one. The float itself is the shared control's (`ui::field`). */
.hp-form {
  width: 100%;
  max-width: 640px;
  display: flex;
  flex-direction: column;
  gap: var(--s6);
  /* The date's calendar and the select's arrow are the browser's, drawn
     for the scheme it is told — black on black otherwise. The system's
     unless the switch said (UIC-17). */
  color-scheme: light dark;
}
:root[data-theme="light"] .hp-form { color-scheme: light; }
:root[data-theme="dark"] .hp-form { color-scheme: dark; }
.hp-pair { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5); }
.hp-form .ui-field > .ui-label {
  top: 11px;
  left: 17px;
  font-size: 11.5px;
  font-weight: 400;
  letter-spacing: 0.06em;
  color: var(--hp-mute);
}
.hp-form .ui-input {
  background: transparent;
  border: 1px solid var(--hp-line2);
  border-radius: var(--r6);
  padding: var(--s10) var(--s8) var(--s4);
  height: 54px;
  font-size: 15.5px;
  font-family: 'Instrument Sans', sans-serif;
  color: var(--hp-ink);
  box-sizing: border-box;
}
.hp-form textarea.ui-input { height: auto; min-height: 112px; padding: var(--s10) var(--s8) var(--s5); line-height: 1.5; }
.hp-form .ui-input:focus { border-color: var(--hp-ink2); }
.hp-form select.ui-input option { background: var(--hp-surface); color: var(--hp-ink); }
.hp-submit { align-self: center; min-width: 200px; margin-top: var(--s4); cursor: pointer; font-family: 'Instrument Sans', sans-serif; }
.hp-fine { color: var(--hp-mute); font-size: 13px; margin: 0; }
.hp-fine.is-center { text-align: center; }
.hp-refused {
  margin: 0;
  padding: var(--s6) var(--s8);
  border-radius: var(--hp-radius);
  background: color-mix(in srgb, var(--hp-coral) 18%, transparent);
  color: var(--hp-ink);
  font-size: 14.5px;
}

/* ── footer ─────────────────────────────────────────────────────────────── */

.hp-footer { border-top: 1px solid var(--hp-line); color: var(--hp-mute); }
.hp-footer-inner {
  padding-block: clamp(48px, 6vw, 80px);
  display: flex;
  gap: var(--s13);
  flex-wrap: wrap;
  justify-content: space-between;
  font-size: 14.5px;
}
.hp-mark svg { width: 36px; height: 36px; display: block; }
.hp-company p { margin: 0; }
.hp-company a { color: var(--hp-mute); }
.hp-company a:hover { color: var(--hp-ink); }
.hp-company-name { color: var(--hp-ink); font-weight: 600; }
.hp-footer-col .hp-kicker { color: var(--hp-ink); }
.hp-footer-col a { color: var(--hp-mute); }
.hp-footer-col a:hover { color: var(--hp-ink); }

/* ── motion ─────────────────────────────────────────────────────────────── */

/* A section arrives as it is scrolled to. Gated on the script having run
   (`html.js`), so a page with no script hides nothing, and off for a reader
   who asked for less motion. */
html.js .hp-reveal {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.8s cubic-bezier(0.2, 0.7, 0.2, 1), transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}
html.js .hp-reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  html.js .hp-reveal { opacity: 1; transform: none; transition: none; }
  .hp-btn, .hp-frame { transition: none; }
  .hp-btn:hover, .hp-frame:hover { transform: none; }
}

/* ── narrower screens ───────────────────────────────────────────────────── */

/* ── the assistant (SITE-7): two screens of it at work, and two lists ────── */

.hp-ai { border-top: 1px solid var(--hp-line); }
.hp-ai-grid { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(20px, 3vw, 40px); margin-top: clamp(40px, 5vw, 72px); }
.hp-ai-frame:hover { transform: none; }
.hp-ai-caption { margin: 0; padding: var(--s5) var(--s6) var(--s6); font-size: 15px; line-height: 1.5; color: var(--hp-ink2); border-top: 1px solid var(--hp-line2); }
.hp-ai-note { margin: var(--s6) 0 0; font-size: 14px; color: var(--hp-mute); }
.hp-ai-lists { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(28px, 4vw, 64px); margin-top: clamp(40px, 5vw, 72px); }
.hp-ai-list { display: flex; flex-direction: column; gap: var(--s7); border-top: 1px solid var(--hp-line2); padding-top: var(--s10); }
.hp-ai-list .hp-points { font-size: 16px; }

/* ── why us (SITE-7): four columns, and the one piece of code on the page ── */

.hp-craft { border-top: 1px solid var(--hp-line); }
.hp-craft-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: clamp(24px, 3vw, 48px); margin-top: clamp(40px, 5vw, 72px); }
.hp-craft-card { display: flex; flex-direction: column; gap: var(--s5); border-top: 1px solid var(--hp-line2); padding-top: var(--s10); }
.hp-craft-name { margin: 0; color: var(--hp-ink); font-size: 18px; font-weight: 500; }
.hp-craft-body { color: var(--hp-ink2); font-size: 15px; margin: 0; }
/* The sample stands in the page's own surface, the mono at a reading size,
   and scrolls sideways on a phone rather than wrapping a line of code. */
.hp-code {
  margin: clamp(40px, 5vw, 72px) 0 0;
  padding: var(--s8) var(--s9);
  border: 1px solid var(--hp-line2);
  border-radius: var(--r7);
  background: var(--hp-surface);
  font-family: 'JetBrains Mono', monospace;
  font-size: 14px;
  line-height: 1.6;
  color: var(--hp-ink);
  overflow-x: auto;
  tab-size: 4;
}
.hp-code code { font: inherit; }
.hp-code-note { margin: var(--s6) 0 0; max-width: 720px; font-size: 15px; color: var(--hp-ink2); }
.hp-code-note code { font-family: 'JetBrains Mono', monospace; font-size: 13px; color: var(--hp-ink); }

/* ── a project's own page (SITE-7): the words, the album, the doors ─────── */

.hp-project-head { padding-block: clamp(64px, 8vw, 120px) clamp(40px, 5vw, 64px); display: flex; flex-direction: column; gap: var(--s9); max-width: 880px; }
.hp-project-back { font-size: 14px; font-weight: 500; letter-spacing: 0.02em; color: var(--hp-ink2); }
.hp-project-back:hover { color: var(--hp-ink); }
.hp-project-album { padding-block: clamp(40px, 5vw, 64px) clamp(64px, 8vw, 120px); }
/* The album is the tier's (UIC-20); what this page adds is its own frame
   around it — the hairline and the corner every picture here wears — and
   the caption at the page's reading size. */
.hp-project-album .ui-gal-main { border: 1px solid var(--hp-line2); background: var(--hp-surface); max-height: none; border-radius: var(--hp-shot-radius); }
.hp-project-album .ui-gal-caption { font-size: 16px; color: var(--hp-ink2); }
.hp-project-album .ui-gal-thumb { width: 120px; border: 1px solid var(--hp-line); }
.hp-project-assistant .hp-points { margin-top: var(--s9); max-width: 820px; font-size: 16px; }

@media (max-width: 980px) {
  .hp-row, .hp-band.is-flip .hp-row { grid-template-columns: 1fr; gap: var(--s11); }
  .hp-band.is-flip .hp-row-say { order: 1; }
  .hp-band.is-flip .hp-frame-mat { order: 2; }
  .hp-ways, .hp-proof-cards, .hp-craft-cards { grid-template-columns: 1fr 1fr; }
  .hp-who { grid-template-columns: 1fr; gap: var(--s11); }
  .hp-nav { display: none; }
}

@media (max-width: 620px) {
  .hp-hero-say { min-height: 0; }
  .hp-ways, .hp-proof-cards, .hp-proof-shots, .hp-pair, .hp-ai-grid, .hp-ai-lists, .hp-craft-cards { grid-template-columns: 1fr; }
  .hp-top-inner { gap: var(--s7); }
}

/* The utility layer (UI-42). Served after every application's own sheet, so a
   class here wins the tie against a component class setting the same property.

   Every value is a step on `theme/scale.rs` or a token on `theme/tokens.rs`.
   Nothing in this file names a number or a colour of its own. */

/* ── layout ──────────────────────────────────────────────────────────────── */

.flex { display: flex; }
.stack { display: flex; flex-direction: column; }
.row { display: flex; flex-direction: row; align-items: center; }
.grid { display: grid; }
.grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.wrap { flex-wrap: wrap; }
.nowrap { flex-wrap: nowrap; }

.start { align-items: flex-start; }
.center { align-items: center; }
.end { align-items: flex-end; }
.baseline { align-items: baseline; }
.stretch { align-items: stretch; }
.between { justify-content: space-between; }
.around { justify-content: space-around; }
.jstart { justify-content: flex-start; }
.jcenter { justify-content: center; }
.jend { justify-content: flex-end; }

/* What one child says about itself, against what its container said about
   all of them. A chip in a column is the usual case: the column stretches
   its children and the chip is as wide as its own word. */
.self-start { align-self: flex-start; }
.self-center { align-self: center; }
.self-end { align-self: flex-end; }

.grow { flex: 1 1 auto; min-width: 0; }
.none { flex: none; }
.shrink { min-width: 0; }

.block { display: block; }
.inline { display: inline; }
.iblock { display: inline-block; }
.iflex { display: inline-flex; align-items: center; }
.relative { position: relative; }
.absolute { position: absolute; }
.fixed { position: fixed; }
.sticky { position: sticky; }
.inset-0 { inset: 0; }
.clip { overflow: hidden; }
.scroll-y { overflow-y: auto; }
.scroll-x { overflow-x: auto; }

/* What a script puts an element behind. The three sheets each carried their
   own copy of this one declaration, so which one a screen got depended on
   which application was serving it. `!important` because it answers a
   `display` the element already has, and it is the only one in the layer. */
.hidden { display: none !important; }

/* A box that fills what holds it is not a measure — it is the absence of one,
   and the same three declarations were written 157 times between the sheets. */
.w-full { width: 100%; }
.h-full { height: 100%; }
.cover { object-fit: cover; }


/* ── space ───────────────────────────────────────────────────────────────── */

.gap-0 { gap: 0; }
.gap-1 { gap: var(--s1); }
.gap-2 { gap: var(--s2); }
.gap-3 { gap: var(--s3); }
.gap-4 { gap: var(--s4); }
.gap-5 { gap: var(--s5); }
.gap-6 { gap: var(--s6); }
.gap-7 { gap: var(--s7); }
.gap-8 { gap: var(--s8); }
.gap-9 { gap: var(--s9); }
.gap-10 { gap: var(--s10); }
.gap-11 { gap: var(--s11); }
.gap-12 { gap: var(--s12); }
.gap-13 { gap: var(--s13); }

.gx-0 { column-gap: 0; }
.gx-1 { column-gap: var(--s1); }
.gx-2 { column-gap: var(--s2); }
.gx-3 { column-gap: var(--s3); }
.gx-4 { column-gap: var(--s4); }
.gx-5 { column-gap: var(--s5); }
.gx-6 { column-gap: var(--s6); }
.gx-7 { column-gap: var(--s7); }
.gx-8 { column-gap: var(--s8); }
.gx-9 { column-gap: var(--s9); }
.gx-10 { column-gap: var(--s10); }
.gx-11 { column-gap: var(--s11); }
.gx-12 { column-gap: var(--s12); }
.gx-13 { column-gap: var(--s13); }

.gy-0 { row-gap: 0; }
.gy-1 { row-gap: var(--s1); }
.gy-2 { row-gap: var(--s2); }
.gy-3 { row-gap: var(--s3); }
.gy-4 { row-gap: var(--s4); }
.gy-5 { row-gap: var(--s5); }
.gy-6 { row-gap: var(--s6); }
.gy-7 { row-gap: var(--s7); }
.gy-8 { row-gap: var(--s8); }
.gy-9 { row-gap: var(--s9); }
.gy-10 { row-gap: var(--s10); }
.gy-11 { row-gap: var(--s11); }
.gy-12 { row-gap: var(--s12); }
.gy-13 { row-gap: var(--s13); }

.p-0 { padding: 0; }
.p-1 { padding: var(--s1); }
.p-2 { padding: var(--s2); }
.p-3 { padding: var(--s3); }
.p-4 { padding: var(--s4); }
.p-5 { padding: var(--s5); }
.p-6 { padding: var(--s6); }
.p-7 { padding: var(--s7); }
.p-8 { padding: var(--s8); }
.p-9 { padding: var(--s9); }
.p-10 { padding: var(--s10); }
.p-11 { padding: var(--s11); }
.p-12 { padding: var(--s12); }
.p-13 { padding: var(--s13); }

.px-0 { padding-inline: 0; }
.px-1 { padding-inline: var(--s1); }
.px-2 { padding-inline: var(--s2); }
.px-3 { padding-inline: var(--s3); }
.px-4 { padding-inline: var(--s4); }
.px-5 { padding-inline: var(--s5); }
.px-6 { padding-inline: var(--s6); }
.px-7 { padding-inline: var(--s7); }
.px-8 { padding-inline: var(--s8); }
.px-9 { padding-inline: var(--s9); }
.px-10 { padding-inline: var(--s10); }
.px-11 { padding-inline: var(--s11); }
.px-12 { padding-inline: var(--s12); }
.px-13 { padding-inline: var(--s13); }

.py-0 { padding-block: 0; }
.py-1 { padding-block: var(--s1); }
.py-2 { padding-block: var(--s2); }
.py-3 { padding-block: var(--s3); }
.py-4 { padding-block: var(--s4); }
.py-5 { padding-block: var(--s5); }
.py-6 { padding-block: var(--s6); }
.py-7 { padding-block: var(--s7); }
.py-8 { padding-block: var(--s8); }
.py-9 { padding-block: var(--s9); }
.py-10 { padding-block: var(--s10); }
.py-11 { padding-block: var(--s11); }
.py-12 { padding-block: var(--s12); }
.py-13 { padding-block: var(--s13); }

.pt-0 { padding-top: 0; }
.pt-1 { padding-top: var(--s1); }
.pt-2 { padding-top: var(--s2); }
.pt-3 { padding-top: var(--s3); }
.pt-4 { padding-top: var(--s4); }
.pt-5 { padding-top: var(--s5); }
.pt-6 { padding-top: var(--s6); }
.pt-7 { padding-top: var(--s7); }
.pt-8 { padding-top: var(--s8); }
.pt-9 { padding-top: var(--s9); }
.pt-10 { padding-top: var(--s10); }
.pt-11 { padding-top: var(--s11); }
.pt-12 { padding-top: var(--s12); }
.pt-13 { padding-top: var(--s13); }

.pb-0 { padding-bottom: 0; }
.pb-1 { padding-bottom: var(--s1); }
.pb-2 { padding-bottom: var(--s2); }
.pb-3 { padding-bottom: var(--s3); }
.pb-4 { padding-bottom: var(--s4); }
.pb-5 { padding-bottom: var(--s5); }
.pb-6 { padding-bottom: var(--s6); }
.pb-7 { padding-bottom: var(--s7); }
.pb-8 { padding-bottom: var(--s8); }
.pb-9 { padding-bottom: var(--s9); }
.pb-10 { padding-bottom: var(--s10); }
.pb-11 { padding-bottom: var(--s11); }
.pb-12 { padding-bottom: var(--s12); }
.pb-13 { padding-bottom: var(--s13); }

/* The only margins there are, and all four are `auto` or nothing. Spacing
   between siblings belongs to the container's gap: a margin that suited one
   parent overlapped the next, which is what this layer exists to stop
   (UI-42). An `auto` is not a length — it is which edge the box is pushed to,
   the one thing a gap cannot say. */
.m-0 { margin: 0; }
.mx-auto { margin-inline: auto; }
.my-auto { margin-block: auto; }
.ml-auto { margin-left: auto; }

/* Where a page stops, and where a reading inside it stops. Every product had
   both and each had named them for itself; the names are the scale's now, so
   a block that lines up with the page says so in the markup.
   **After the margins on purpose**: `m-0 measure` has to mean "no margin of
   its own, and centred", which is what an element wearing both is asking
   for. Written above them it lost the `auto` to the reset, and the foot's
   own hairline was capped at the measure and then left-aligned under bands
   that were centred. */
.measure { width: 100%; max-width: var(--measure); margin-inline: auto; }
.prose { max-width: var(--prose); }
.gutter { padding-inline: var(--gutter); }

/* ── type ────────────────────────────────────────────────────────────────── */

.t-1 { font-size: var(--t1); line-height: var(--lh1); }
.t-2 { font-size: var(--t2); line-height: var(--lh2); }
.t-3 { font-size: var(--t3); line-height: var(--lh3); }
.t-4 { font-size: var(--t4); line-height: var(--lh4); }
.t-5 { font-size: var(--t5); line-height: var(--lh5); }
.t-6 { font-size: var(--t6); line-height: var(--lh6); }
.t-7 { font-size: var(--t7); line-height: var(--lh7); }
.t-8 { font-size: var(--t8); line-height: var(--lh8); }
.t-9 { font-size: var(--t9); line-height: var(--lh9); }
.t-10 { font-size: var(--t10); line-height: var(--lh10); }
.t-11 { font-size: var(--t11); line-height: var(--lh11); }

.serif { font-family: var(--serif); }
.sans { font-family: var(--sans); }
.mono { font-family: var(--mono); }

.fw-4 { font-weight: 400; }
.fw-5 { font-weight: 500; }
.fw-6 { font-weight: 600; }
.fw-7 { font-weight: 700; }

.upper { text-transform: uppercase; }
.track { letter-spacing: 0.08em; }
.tabular { font-variant-numeric: tabular-nums; }
.tleft { text-align: left; }
.tcenter { text-align: center; }
.tright { text-align: right; }
.pretty { text-wrap: pretty; }
.balance { text-wrap: balance; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* `.nowrap` above is the flex one. These two are the text's: a line that may
   not break, and a word — a key, an address, a token — that may break
   anywhere rather than push its column wider. */
.nobr { white-space: nowrap; }
.anywhere { overflow-wrap: anywhere; }

/* ── ink ─────────────────────────────────────────────────────────────────── */

.text-ink { color: var(--ink); }
.text-ink2 { color: var(--ink2); }
.text-ink3 { color: var(--ink3); }
.text-mute { color: var(--mute); }
.text-faint { color: var(--faint); }
.text-accent { color: var(--accent); }
.text-accent-ink { color: var(--accent-ink); }
.text-solid-ink { color: var(--solid-ink); }
.text-danger { color: var(--danger); }
.text-ok { color: var(--ok-ink); }
.text-warn { color: var(--warn-ink); }
.text-info { color: var(--info); }
.text-star { color: var(--star); }

/* ── ground ──────────────────────────────────────────────────────────────── */

.bg-none { background: none; }
.bg-page { background: var(--page); }
.bg-surface { background: var(--surface); }
.bg-rail { background: var(--rail); }
.bg-mid { background: var(--mid); }
.bg-chip { background: var(--chip); }
.bg-field { background: var(--field); }
.bg-accent { background: var(--accent); }
.bg-accent-soft { background: var(--accent-soft); }
.bg-solid { background: var(--solid); }
.bg-danger-soft { background: var(--danger-soft); }
.bg-ok { background: var(--ok-bg); }
.bg-warn { background: var(--warn-bg); }
.bg-info-soft { background: var(--info-soft); }
.bg-star-soft { background: var(--star-soft); }

/* ── edges ───────────────────────────────────────────────────────────────── */

.line { border: 1px solid var(--line); }
.line-t { border-top: 1px solid var(--line); }
.line-b { border-bottom: 1px solid var(--line); }
.line-l { border-left: 1px solid var(--line); }
.line-r { border-right: 1px solid var(--line); }
.line2 { border: 1px solid var(--line2); }
.hair { border: 1px solid var(--hair); }
.hair-t { border-top: 1px solid var(--hair); }
.hair-b { border-bottom: 1px solid var(--hair); }
.line-accent { border: 1px solid var(--accent-line); }
.line-danger { border: 1px solid var(--danger-line); }
.line-warn { border: 1px solid var(--warn-line); }
.line-none { border: 0; }

.r-0 { border-radius: 0; }
.r-1 { border-radius: var(--r1); }
.r-2 { border-radius: var(--r2); }
.r-3 { border-radius: var(--r3); }
.r-4 { border-radius: var(--r4); }
.r-5 { border-radius: var(--r5); }
.r-6 { border-radius: var(--r6); }
.r-7 { border-radius: var(--r7); }
.r-pill { border-radius: var(--r-pill); }

.shadow { box-shadow: var(--shadow); }
.shadow-pop { box-shadow: var(--pop); }
.shadow-none { box-shadow: none; }

/* ── what answers a pointer ──────────────────────────────────────────────── */

/* The sheets spelled twenty-two different transitions between them, and
   fifty-three rules wanted the same one: the colours move, nothing else, and
   the eye reads the change as a state rather than as an animation. */
.transition-colors { transition: background var(--quick), border-color var(--quick), color var(--quick); }
.transition-transform { transition: transform var(--quick); }

.cursor-pointer { cursor: pointer; }
.cursor-default { cursor: default; }
.pointer-none { pointer-events: none; }
.pointer-auto { pointer-events: auto; }

.hover-text-ink:hover { color: var(--ink); }
.hover-text-accent:hover { color: var(--accent); }
.hover-text-danger:hover { color: var(--danger); }
.hover-underline:hover { text-decoration: underline; text-underline-offset: 2px; }

.hover-bg-hover:hover { background: var(--hover); }
.hover-bg-chip:hover { background: var(--chip); }
.hover-bg-surface:hover { background: var(--surface); }
.hover-bg-accent-soft:hover { background: var(--accent-soft); }

.hover-line-accent:hover { border-color: var(--accent); }
.hover-line-ink:hover { border-color: var(--ink); }
.hover-line-danger:hover { border-color: var(--danger); }

.active-bg-down:active { background: var(--accent-down); }

/* A ring the keyboard draws and the mouse does not, taking the corner of
   whatever it is put on rather than deciding one of its own. */
.focus-ring:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: inherit; }
.focus-none:focus { outline: none; }

/* Not `[disabled]`: a link and a label can be past answering too, and neither
   takes the attribute. */
.disabled { opacity: 0.5; cursor: default; pointer-events: none; }

/* ── the desk (UIC-23) ───────────────────────────────────────────────────── */

/* Mobile is the ground. A utility above is every width, and its `d-` twin
   is the desk — 641px and up, measured on the page's own width (the body is
   the container, `layouts.css`), so a page narrowed by the dock is a phone
   to it. `row d-stack` is a row on a phone and a column on a desk; the twin
   comes after the base in this file, which is what lets it win the tie.
   Only the arrangement has twins: what changes between a phone and a desk
   is where things stand, not what colour they are. */
@container (min-width: 641px) {
  .d-flex { display: flex; }
  .d-stack { display: flex; flex-direction: column; }
  .d-row { display: flex; flex-direction: row; align-items: center; }
  .d-grid { display: grid; }
  .d-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .d-grid-3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .d-grid-4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .d-block { display: block; }
  .d-iflex { display: inline-flex; align-items: center; }
  .d-hidden { display: none; }
  .d-wrap { flex-wrap: wrap; }
  .d-nowrap { flex-wrap: nowrap; }

  .d-start { align-items: flex-start; }
  .d-center { align-items: center; }
  .d-end { align-items: flex-end; }
  .d-stretch { align-items: stretch; }
  .d-between { justify-content: space-between; }
  .d-jstart { justify-content: flex-start; }
  .d-jcenter { justify-content: center; }
  .d-jend { justify-content: flex-end; }

  .d-grow { flex: 1 1 auto; min-width: 0; }
  .d-none { flex: none; }
  .d-w-full { width: 100%; }

  .d-gap-0 { gap: 0; }
  .d-gap-1 { gap: var(--s1); }
  .d-gap-2 { gap: var(--s2); }
  .d-gap-3 { gap: var(--s3); }
  .d-gap-4 { gap: var(--s4); }
  .d-gap-5 { gap: var(--s5); }
  .d-gap-6 { gap: var(--s6); }
  .d-gap-7 { gap: var(--s7); }
  .d-gap-8 { gap: var(--s8); }
  .d-gap-9 { gap: var(--s9); }
  .d-gap-10 { gap: var(--s10); }
  .d-gap-11 { gap: var(--s11); }
  .d-gap-12 { gap: var(--s12); }
  .d-gap-13 { gap: var(--s13); }
}
