/* Zawrix — the product site.
 *
 * Pure white in light, pure black in dark, neutral borders, and one colour:
 * green, which appears in the connected tiles and the abstract visuals and
 * almost nowhere else. No section is ever green. Buttons are black or white
 * depending on the theme, never the accent - an accent-coloured primary button
 * is what makes a page read as a template.
 *
 * Every colour is a semantic token with a light and a dark value. Nothing below
 * the token block names a raw colour, so the dark theme is a real design rather
 * than an inversion.
 */

/* ---------------------------------------------------------------- type ---- */
/* Self-hosted, one variable file per family, subset to latin. No third-party
   request, nothing about a visitor leaving this origin, and no render-blocking
   stylesheet from a font CDN. size-adjust on the fallback keeps the swap from
   moving the layout - the metric was measured against the real face, not
   guessed. */
@font-face {
  font-family: "Zawrix Sans";
  src: url("/assets/geist-latin.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Zawrix Fallback";
  src: local("Segoe UI"), local("Helvetica Neue"), local("Arial"), local("Roboto");
  size-adjust: 101%;
  ascent-override: 92%;
  descent-override: 24%;
  line-gap-override: 0%;
}

:root {
  --sans: "Zawrix Sans", "Zawrix Fallback", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "Segoe UI Mono", Menlo, Consolas, monospace;

  /* A tuned ramp, not a strict modular scale: the steps a page actually needs,
     with line-height tightening as size grows, which is what stops a big
     headline reading as loose body copy. clamp() carries the two display sizes
     down to phones without a media query. */
  --t-hero: clamp(2.6rem, 6.4vw, 4.6rem);
  --t-h2: clamp(2rem, 4vw, 3.2rem);
  --t-h3: clamp(1.35rem, 2.2vw, 1.75rem);
  --t-lead: clamp(1.0625rem, 1.5vw, 1.25rem);
  --t-body: 1.0625rem;
  --t-small: 0.9375rem;
  --t-label: 0.75rem;

  --lh-hero: 1.02;
  --lh-h2: 1.06;
  --lh-body: 1.6;

  /* 4px mini-unit. Non-linear at the top so section rhythm has real steps
     rather than a smooth ramp nobody can see. */
  --s1: 4px;  --s2: 8px;   --s3: 12px;  --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px;  --s8: 64px;  --s9: 96px; --s10: 140px;

  --measure: 68ch;
  --maxw: 1280px;
  --gutter: clamp(20px, 5vw, 56px);
  --radius: 4px;

  color-scheme: light;
  --bg: #FFFFFF;
  --surface: #FFFFFF;
  --surface-2: #FAFAFA;
  --fg: #0A0A0A;
  --fg-2: #52525B;
  --fg-3: #71717A;
  --border: #E4E4E7;
  --border-strong: #D4D4D8;

  /* THE ONE RULE ABOUT GREEN
     Green is the signature tile material and nothing else. It is not the
     accent colour, not the link colour, not the focus ring and not a chart
     series. Only the --tile-* tokens below are permitted to be green, and
     test/monochrome_test.mjs fails the build if any other token is.

     Why the rule needs enforcing rather than remembering: a brand colour
     defined as "--accent" gets reached for, correctly and thoughtlessly, every
     time something needs to stand out. It spread from the tiles to links,
     focus rings, form success notes, FAQ hover, panel icons and the button
     glow before anybody decided it should. Each step was reasonable. The sum
     was a green website with green tiles on it, which is not a signature.

     --accent is therefore ink: near-black on white, near-white on black. It
     still means "this is emphasised"; it just says so in the monochrome
     system rather than in colour. */
  --accent: #0A0A0A;
  --accent-line: #A1A1AA;
  --tile-face: rgba(47, 169, 95, 0.13);
  --tile-face-2: rgba(47, 169, 95, 0.22);
  --tile-edge: rgba(47, 169, 95, 0.55);
  --tile-ink: #0A0A0A;
  --tile-shadow: 0 24px 48px -28px rgba(11, 110, 51, 0.5);
  --tile-glow: none;

  --btn-bg: #0A0A0A;
  --btn-fg: #FFFFFF;
  --btn-bg-hover: #27272A;
  --focus: #0A0A0A;

  /* Illumination. --btn-spark is the highlight that follows the pointer across
     a button. On a black button it is white light; on the dark theme's white
     button it inverts to a dark wash, which is the monochrome answer to the
     problem the old green tint was solving. --btn-lit is the soft cast
     underneath, and --btn-lit-2 is a hairline that was a green ring and is now
     a neutral one. */
  --btn-spark: rgba(255, 255, 255, 0.26);
  --btn-lit: 0 14px 30px -16px rgba(10, 10, 10, 0.55);
  --btn-lit-2: 0 0 0 1px rgba(10, 10, 10, 0.16);
  --ghost-spark: rgba(10, 10, 10, 0.07);
}

/* The dark palette. Two selectors, because "System" and an explicit dark choice
   are different mechanisms: the media query handles a reader who has chosen
   nothing (and keeps following the OS while the page is open), and the
   attribute handles one who has. An explicit data-theme="light" opts out of the
   media query entirely, which is what the :not() is for. Duplicated across the two selectors rather than generated,
   because a preprocessor is not worth adding to a build that has none. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --bg: #000000;
    --surface: #000000;
    --surface-2: #0B0B0C;
    --fg: #FFFFFF;
    --fg-2: #A1A1AA;
    --fg-3: #8B8B93;
    --border: #26262A;
    --border-strong: #3A3A40;

    --accent: #FAFAFA;
    --accent-line: #52525B;
    --tile-face: rgba(61, 210, 122, 0.10);
    --tile-face-2: rgba(61, 210, 122, 0.20);
    --tile-edge: rgba(74, 222, 128, 0.72);
    --tile-ink: #FFFFFF;
    --tile-shadow: 0 30px 60px -34px rgba(61, 210, 122, 0.55);
    --tile-glow: 0 0 34px -6px rgba(61, 210, 122, 0.35);

    --btn-bg: #FFFFFF;
    --btn-fg: #0A0A0A;
    --btn-bg-hover: #E4E4E7;
    --focus: #FAFAFA;

    --btn-spark: rgba(10, 10, 10, 0.20);
    --btn-lit: 0 16px 36px -18px rgba(255, 255, 255, 0.30);
    --btn-lit-2: 0 0 0 1px rgba(255, 255, 255, 0.22);
    --ghost-spark: rgba(255, 255, 255, 0.10);
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #000000;
  --surface: #000000;
  --surface-2: #0B0B0C;
  --fg: #FFFFFF;
  --fg-2: #A1A1AA;
  --fg-3: #8B8B93;
  --border: #26262A;
  --border-strong: #3A3A40;

  --accent: #FAFAFA;
  --accent-line: #52525B;
  --tile-face: rgba(61, 210, 122, 0.10);
  --tile-face-2: rgba(61, 210, 122, 0.20);
  --tile-edge: rgba(74, 222, 128, 0.72);
  --tile-ink: #FFFFFF;
  --tile-shadow: 0 30px 60px -34px rgba(61, 210, 122, 0.55);
  --tile-glow: 0 0 34px -6px rgba(61, 210, 122, 0.35);

  --btn-bg: #FFFFFF;
  --btn-fg: #0A0A0A;
  --btn-bg-hover: #E4E4E7;
  --focus: #FAFAFA;

  --btn-spark: rgba(10, 10, 10, 0.20);
  --btn-lit: 0 16px 36px -18px rgba(255, 255, 255, 0.30);
  --btn-lit-2: 0 0 0 1px rgba(255, 255, 255, 0.22);
  --ghost-spark: rgba(255, 255, 255, 0.10);
}

/* ---------------------------------------------------------------- base ---- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--sans);
  font-size: var(--t-body);
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  font-synthesis-weight: none;
}
img, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.035em; text-wrap: balance; }
h1 { font-size: var(--t-hero); line-height: var(--lh-hero); letter-spacing: -0.042em; }
h2 { font-size: var(--t-h2); line-height: var(--lh-h2); }
h3 { font-size: var(--t-h3); line-height: 1.2; letter-spacing: -0.025em; }
p { margin: 0; }
ul, ol { margin: 0; padding: 0; }

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}
/* A skip link that is genuinely reachable, not one hidden with display:none
   where a keyboard will never find it. */
.skip {
  position: absolute; left: var(--gutter); top: -100px; z-index: 200;
  background: var(--btn-bg); color: var(--btn-fg);
  padding: 12px 18px; border-radius: var(--radius); font-weight: 600;
  transition: top .15s;
}
.skip:focus { top: 12px; }

.wrap { max-width: var(--maxw); margin: 0 auto; padding-inline: var(--gutter); }
.lead { font-size: var(--t-lead); color: var(--fg-2); max-width: 46ch; line-height: 1.55; }
.prose { max-width: var(--measure); }
.eyebrow {
  font-size: var(--t-label); font-weight: 500; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--fg-2); margin: 0 0 var(--s5);
}
.rule { border: 0; border-top: 1px solid var(--border); margin: 0; }

section { padding-block: clamp(var(--s8), 9vw, var(--s10)); }
section + section { border-top: 1px solid var(--border); }

/* ------------------------------------------------------------- buttons ---- */
/* A button that lights up.
 *
 * Three things happen on hover, and each is doing a different job. The cast
 * underneath (--btn-lit) separates the button from the page so it reads as a
 * thing you can press rather than a rectangle of colour. The lift of one pixel
 * is small on purpose: anything larger reads as a toy, and on a 54px primary
 * button one pixel is still clearly perceptible. And a soft highlight follows
 * the pointer across the face, which is the part that makes it feel lit rather
 * than merely shaded.
 *
 * The highlight is a ::after with a radial gradient positioned from --mx and
 * --my, two custom properties that app.js sets on pointermove. It sits at
 * opacity 0 until hover, so a keyboard user - who has no pointer position -
 * never sees a highlight stuck in the top-left corner; they get the focus ring
 * and the cast, which is the same information without the lie.
 *
 * The easing is a small overshoot on the way in and a plain ease on the way
 * out. Things in the physical world settle; they do not arrive and stop dead.
 */
.btn {
  position: relative; isolation: isolate; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 48px; padding: 0 22px;
  font-size: var(--t-small); font-weight: 600; font-family: inherit;
  border: 1px solid transparent; border-radius: var(--radius);
  background: var(--btn-bg); color: var(--btn-fg);
  cursor: pointer; white-space: nowrap;
  box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
  transition:
    background .18s ease,
    border-color .18s ease,
    color .18s ease,
    box-shadow .26s cubic-bezier(.22, 1, .36, 1),
    transform .26s cubic-bezier(.34, 1.4, .64, 1);
}
.btn::after {
  content: ""; position: absolute; inset: 0; z-index: -1;
  border-radius: inherit; pointer-events: none;
  opacity: 0; transition: opacity .22s ease;
  background: radial-gradient(
    150px circle at var(--mx, 50%) var(--my, 50%),
    var(--btn-spark), transparent 68%);
}
.btn:hover { background: var(--btn-bg-hover); transform: translateY(-1px); box-shadow: var(--btn-lit), var(--btn-lit-2); }
.btn:hover::after { opacity: 1; }
.btn:focus-visible { box-shadow: var(--btn-lit), var(--btn-lit-2); }
/* Pressed goes back down and loses the cast, so the press is felt rather than
   only seen. Faster than the release, the way a real key is. */
.btn:active { transform: translateY(0); box-shadow: none; transition-duration: .06s; }

.btn--ghost {
  background: transparent; color: var(--fg); border-color: var(--border-strong);
  --btn-spark: var(--ghost-spark);
}
.btn--ghost:hover { background: var(--surface-2); border-color: var(--fg-3); }
.btn--plain {
  background: transparent; color: var(--fg); border-color: transparent;
  padding-inline: 4px; min-height: 44px;
  overflow: visible;
}
.btn--plain:hover {
  background: transparent; color: var(--accent);
  transform: none; box-shadow: none;
}
.btn--plain::after { content: none; }
.btn--plain .arrow { transition: transform .18s ease; }
.btn--plain:hover .arrow { transform: translateX(4px); }
.btn--lg { min-height: 54px; padding-inline: 28px; font-size: 1rem; }

/* --------------------------------------------------------------- tiles ---- */
/* The five connected planes.
 *
 * The shape is a PARALLELOGRAM, not a perspective rotation. The first attempt
 * used rotateY on a tall box and the result read as a bar chart - five vertical
 * bars of different heights with a line through them, which is the one thing
 * this must not look like. A skewY gives parallel edges and a consistent
 * angle across all five, which is what makes them read as planes standing in a
 * row rather than as a measurement.
 *
 * Everything is real DOM: labels are text, the lift is a transform on :hover
 * and :focus-visible, and each tile links to the part of the product it names.
 */
.tiles {
  list-style: none;
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: clamp(8px, 1.1vw, 18px);
  align-items: center;
  padding-block: var(--s7) var(--s6);
}
.tiles li { display: flex; }
.tile {
  --lift: 0px;
  --skew: -9deg;
  position: relative;
  flex: 1;
  display: flex; flex-direction: column; gap: var(--s3);
  aspect-ratio: 0.72;
  padding: clamp(11px, 1.2vw, 18px);
  color: var(--tile-ink);
  background: linear-gradient(155deg, var(--tile-face-2), var(--tile-face) 60%);
  border: 1px solid var(--tile-edge);
  box-shadow: var(--tile-shadow), var(--tile-glow);
  transform: skewY(var(--skew)) translateY(var(--lift));
  transition: transform .32s cubic-bezier(.2,.7,.3,1), background .22s ease,
              border-color .22s ease, box-shadow .32s ease;
}
/* The stagger is what turns five identical planes into a group with a shape:
   the middle one stands forward, the outer pair sit back. */
.tiles li:nth-child(1) .tile { --lift: 16px; }
.tiles li:nth-child(2) .tile { --lift: 4px; }
.tiles li:nth-child(3) .tile {
  --lift: -16px;
  background: linear-gradient(155deg, var(--tile-face-2), var(--tile-face-2) 45%, var(--tile-face));
  border-color: var(--accent-line);
}
.tiles li:nth-child(4) .tile { --lift: 4px; }
.tiles li:nth-child(5) .tile { --lift: 16px; }

.tile:hover, .tile:focus-visible {
  --lift: -30px;
  --skew: -5.5deg;
  background: linear-gradient(155deg, var(--tile-face-2), var(--tile-face-2) 62%, var(--tile-face));
  border-color: var(--accent-line);
  z-index: 2;
}

/* The content is counter-skewed so the labels sit level. The plane is angled;
   the words on it are not, because a label you have to tilt your head for is
   not a readable label. */
.tile__in {
  transform: skewY(calc(var(--skew) * -1));
  display: flex; flex-direction: column; height: 100%; gap: var(--s2);
  transition: transform .32s cubic-bezier(.2,.7,.3,1);
}
.tile__n {
  font-family: var(--mono); font-size: var(--t-label); letter-spacing: .1em;
  color: var(--fg-2);
}
/* The icon sits ON the green tile face, so it takes the tile's own ink
   rather than the page accent - otherwise it would track a token that is
   deliberately not part of the tile material. */
.tile__icon { width: 22px; height: 22px; color: var(--tile-ink); margin-top: auto; }
.tile__label {
  font-size: clamp(0.95rem, 1.35vw, 1.15rem); font-weight: 600;
  letter-spacing: -0.02em; line-height: 1.1; margin-top: var(--s2);
}
/* Supplementary, never essential: the label above carries the meaning on its
   own, so nothing a reader needs is hidden behind a hover. It sits below the
   plane rather than inside it, so revealing it cannot reflow the row. */
.tile__note {
  position: absolute; left: 0; right: 0; top: calc(100% + 12px);
  transform: skewY(calc(var(--skew) * -1)) translateY(4px);
  font-size: 0.8125rem; line-height: 1.4; color: var(--fg-2);
  opacity: 0; pointer-events: none;
  transition: opacity .22s ease, transform .22s ease;
}
.tile:hover .tile__note, .tile:focus-visible .tile__note {
  opacity: 1; transform: skewY(calc(var(--skew) * -1)) translateY(0);
}

.tiles-wrap {
  position: relative;
  /* Room for the note that appears under a hovered plane. Reserved rather than
     discovered: without it the note lands on top of the caption below. */
  padding-bottom: 68px;
}
/* The line that joins them. It sits behind the planes and follows the same
   angle, so it reads as one run through all five rather than a stray rule. */
.tiles-line {
  position: absolute; left: 2%; right: 2%; top: 52%; height: 1px;
  background: linear-gradient(90deg, transparent, var(--accent-line) 10%, var(--accent-line) 90%, transparent);
  opacity: .55; transform: skewY(-9deg); pointer-events: none; z-index: 0;
}
.tiles-caption {
  color: var(--fg-2); font-size: var(--t-small); line-height: 1.55;
  border-left: 1px solid var(--border-strong); padding-left: var(--s4);
  margin-top: var(--s8);
}
.tiles-caption strong { color: var(--fg); font-weight: 600; }

@media (hover: none) {
  /* No hover means no way to reveal the note, so on a touch device it is simply
     part of the layout. */
  .tile__note { position: static; opacity: 1; transform: none; margin-top: var(--s2); }
  .tile { aspect-ratio: auto; }
  .tiles-wrap { padding-bottom: 0; }
}
@media (max-width: 760px) {
  /* Flat, level and side by side. A skewed plane 60px wide is a smudge, and the
     label has to stay readable at the narrowest width the site supports. */
  .tiles { gap: 6px; }
  /* aspect-ratio AND min-height together make the box derive its WIDTH from its
     height: 112px / 0.78 is 143px per tile, times five, and the row is 739px
     wide inside a 390px screen. Height only, and the grid keeps control of the
     width. */
  .tile { --skew: 0deg; --lift: 0px; aspect-ratio: auto; min-height: 108px; padding: 10px 8px; }
  .tile__icon { margin-top: var(--s2); }
  .tiles li:nth-child(n) .tile { --lift: 0px; }
  .tile:hover, .tile:focus-visible { --lift: -5px; --skew: 0deg; }
  .tile__label { font-size: 0.8125rem; }
  .tile__icon { width: 18px; height: 18px; }
  .tile__note { display: none; }
  .tiles-line { transform: none; top: 50%; }
  .tiles-caption { margin-top: var(--s5); }
}
@media (prefers-reduced-motion: reduce) {
  .tile, .tile__in, .tile__note, .btn--plain .arrow, .skip { transition: none; }
  .tile:hover, .tile:focus-visible { --lift: 0px; --skew: -9deg; }
}

/* ------------------------------------------------------------- header ----- */
.hdr {
  position: sticky; top: 0; z-index: 90;
  background: var(--bg);
  border-bottom: 1px solid transparent;
  transition: border-color .2s ease;
}
.hdr[data-stuck="true"] { border-bottom-color: var(--border); }
.hdr__bar {
  display: flex; align-items: center; gap: var(--s6);
  height: 72px; max-width: var(--maxw); margin: 0 auto;
  padding-inline: var(--gutter);
}
.brand { display: block; flex: none; color: var(--fg); }
.brand svg { height: 21px; width: auto; }
.hdr__nav { display: flex; gap: var(--s5); }
.hdr__nav a { font-size: var(--t-small); color: var(--fg-2); font-weight: 500; padding: 6px 2px; }
.hdr__nav a:hover, .hdr__nav a[aria-current="page"] { color: var(--fg); }
.hdr__sp { flex: 1; }
.hdr__end { display: flex; align-items: center; gap: var(--s4); }
.hdr__divider { width: 1px; height: 26px; background: var(--border); }

/* --------------------------------------------------------------- theme ---- */
.theme { position: relative; }
.theme__btn {
  display: inline-flex; align-items: center; gap: 7px;
  min-height: 40px; padding: 0 10px;
  background: transparent; border: 1px solid transparent; border-radius: var(--radius);
  color: var(--fg-2); font: inherit; font-size: var(--t-small); cursor: pointer;
}
.theme__btn:hover { color: var(--fg); border-color: var(--border); }
.theme__menu {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 95;
  min-width: 150px; padding: 5px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 18px 40px -24px rgba(0,0,0,.4);
}
.theme__menu[hidden] { display: none; }
.theme__menu button {
  display: flex; width: 100%; align-items: center; gap: 9px;
  padding: 9px 10px; background: transparent; border: 0; border-radius: 3px;
  color: var(--fg); font: inherit; font-size: var(--t-small); text-align: left; cursor: pointer;
}
.theme__menu button:hover { background: var(--surface-2); }
.theme__menu button[aria-checked="true"] { font-weight: 600; }
.theme__tick { width: 15px; color: var(--accent); }
.theme__menu button[aria-checked="false"] .theme__tick { visibility: hidden; }

/* -------------------------------------------------------- mobile menu ----- */
.burger {
  display: none; align-items: center; justify-content: center;
  width: 44px; height: 44px; padding: 0;
  background: transparent; border: 1px solid var(--border); border-radius: var(--radius);
  color: var(--fg); cursor: pointer;
}
.burger span { display: block; width: 18px; height: 1.5px; background: currentColor; position: relative; }
.burger span::before, .burger span::after {
  content: ""; position: absolute; left: 0; width: 18px; height: 1.5px; background: currentColor;
  transition: transform .2s ease;
}
.burger span::before { top: -6px; } .burger span::after { top: 6px; }
.burger[aria-expanded="true"] span { background: transparent; }
.burger[aria-expanded="true"] span::before { transform: translateY(6px) rotate(45deg); }
.burger[aria-expanded="true"] span::after { transform: translateY(-6px) rotate(-45deg); }

.sheet { border-top: 1px solid var(--border); background: var(--bg); }
.sheet__in { padding: var(--s4) var(--gutter) var(--s7); }
.sheet a.sheet__link {
  display: block; padding: 16px 0; font-size: 1.25rem; font-weight: 500;
  border-bottom: 1px solid var(--border);
}
.sheet .btn { width: 100%; margin-top: var(--s5); }

@media (max-width: 960px) {
  .hdr__nav, .hdr__divider { display: none; }
  .burger { display: inline-flex; }
  .hdr__bar { gap: var(--s4); height: 64px; }
}
@media (max-width: 720px) {
  /* Five controls do not fit in a 390px bar - the first attempt pushed the
     demo button off the right edge entirely. Two controls and a menu do. */
  .hdr__end > .theme, .hdr__end > .btn:not(.btn--plain) { display: none; }
  .hdr__bar { gap: var(--s3); }
}
.sheet__theme { display: flex; gap: var(--s2); margin-top: var(--s5); }
.sheet__theme button {
  flex: 1; min-height: 46px; background: transparent; color: var(--fg);
  border: 1px solid var(--border); border-radius: var(--radius);
  font: inherit; font-size: var(--t-small); cursor: pointer;
}
.sheet__theme button[aria-checked="true"] { border-color: var(--fg); font-weight: 600; }
@media (min-width: 961px) { .sheet { display: none; } }
/* With JavaScript off the button cannot work, so the links stand in for it. */
html:not(.js) .burger { display: none; }
html:not(.js) .sheet[hidden] { display: block; }
html.js .sheet[hidden] { display: none; }

/* -------------------------------------------------------------- footer ---- */
.ftr { border-top: 1px solid var(--border); background: var(--surface-2); padding-block: var(--s8) var(--s6); }
.ftr__grid { display: grid; grid-template-columns: 1.7fr repeat(4, 1fr); gap: var(--s6); }
.ftr h2 {
  font-size: var(--t-label); font-weight: 600; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--fg-2); margin-bottom: var(--s4);
}
.ftr ul { list-style: none; }
.ftr li a { display: block; padding: 5px 0; font-size: var(--t-small); color: var(--fg-2); }
.ftr li a:hover { color: var(--fg); }
.ftr__contact { font-size: var(--t-small); color: var(--fg-2); line-height: 1.7; margin-top: var(--s4); }
.ftr__contact a:hover { color: var(--fg); text-decoration: underline; }
.ftr__legal {
  display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); align-items: center;
  margin-top: var(--s8); padding-top: var(--s5);
  border-top: 1px solid var(--border);
  font-size: 0.875rem; color: var(--fg-3);
}
.ftr__legal a:hover { color: var(--fg); }
.ftr__legal .sp { flex: 1; }
@media (max-width: 900px) { .ftr__grid { grid-template-columns: 1fr 1fr; gap: var(--s6) var(--s5); } }
@media (max-width: 560px) { .ftr__grid { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------ layouts ----- */
/* Four rhythms, used in rotation so no two adjacent sections share a shape.
   This is the discipline the audit found missing: the old page ran
   eyebrow -> headline -> lede -> card grid five times. */

/* 1. Split: statement left, supporting prose right. */
.split { display: grid; grid-template-columns: 1.15fr 1fr; gap: clamp(var(--s6), 6vw, var(--s9)); align-items: start; }
.split--top { align-items: start; }
.split p { color: var(--fg-2); }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; gap: var(--s6); } }

/* 2. Ledger: numbered rows divided by rules, no cards. */
.ledger { list-style: none; border-top: 1px solid var(--border); }
.ledger li { border-bottom: 1px solid var(--border); }
.ledger__row {
  display: grid; grid-template-columns: 4rem 1fr 1.4fr; gap: var(--s5);
  padding-block: var(--s6); align-items: baseline;
}
.ledger__n { font-family: var(--mono); font-size: var(--t-label); color: var(--fg-3); letter-spacing: .1em; }
.ledger__t { font-size: 1.125rem; font-weight: 600; letter-spacing: -0.02em; }
.ledger__d { color: var(--fg-2); }
/* Two rows that need their own column tracks - the pricing table and the
   contact details. These were inline style attributes, which beat the media
   query below and left both pages overflowing at 320px: a 10rem first column
   plus two fr columns cannot fit in a 320px screen minus its gutters, and an
   inline style is the one thing a breakpoint cannot argue with. As classes
   they collapse like every other row. */
.ledger__row--plan { grid-template-columns: 10rem 1fr 1.3fr; }
.ledger__row--pair { grid-template-columns: 8rem 1fr; }

@media (max-width: 760px) {
  .ledger__row { grid-template-columns: 2.5rem 1fr; gap: var(--s2) var(--s4); padding-block: var(--s5); }
  .ledger__d { grid-column: 2; }
}

/* AFTER the generic mobile rule, deliberately.
 *
 * These two were written above it and were silently undone: the block above
 * resets grid-template-columns for every .ledger__row at 760px, modifier or
 * not, so the pricing table collapsed to a 2.5rem first column and rendered
 * "Starte" beside "$399". Source order decides this, not intent - both
 * selectors carry the same specificity, so the last one wins and the last one
 * has to be the specific one. */
@media (max-width: 860px) {
  .ledger__row--plan { grid-template-columns: 1fr; gap: var(--s4); }
}
@media (max-width: 560px) {
  .ledger__row--pair { grid-template-columns: 1fr; gap: var(--s2); }
}

/* 3. Evidence: a claim and the exact piece of interface that proves it. */
.evidence { display: grid; grid-template-columns: 1fr 1.25fr; gap: clamp(var(--s6), 5vw, var(--s8)); align-items: center; }
.evidence--flip .evidence__txt { order: 2; }
.evidence__shot { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--surface-2); }
.evidence__cap { font-size: 0.8125rem; color: var(--fg-3); margin-top: var(--s3); }
@media (max-width: 900px) {
  .evidence { grid-template-columns: 1fr; gap: var(--s5); }
  .evidence--flip .evidence__txt { order: 0; }
}

/* 4. Stack: one large statement with generous air, nothing beside it. */
.stack { max-width: 22ch; }
.stack--wide { max-width: 34ch; }

/* two-up panel row, used sparingly and never as a generic card grid */
.duo { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.panel {
  display: flex; align-items: flex-start; gap: var(--s4);
  padding: var(--s6); border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); transition: border-color .18s ease;
}
.panel:hover { border-color: var(--border-strong); }
.panel__icon { width: 24px; height: 24px; color: var(--accent); flex: none; margin-top: 2px; }
.panel__t { font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.02em; }
.panel__d { font-size: var(--t-small); color: var(--fg-2); margin-top: 4px; }
.panel .sp { flex: 1; }
@media (max-width: 760px) { .duo { grid-template-columns: 1fr; } }

/* a full-width invitation band, black on white / white on black */
.band {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s5);
  padding: var(--s7); border: 1px solid var(--border); border-radius: var(--radius);
}
.band__t { font-size: var(--t-h3); font-weight: 600; letter-spacing: -0.025em; }
.band__d { color: var(--fg-2); font-size: var(--t-small); margin-top: 4px; }
.band .sp { flex: 1; }
@media (max-width: 640px) { .band { padding: var(--s6); } .band .btn { width: 100%; } }

.hero { padding-block: clamp(var(--s7), 7vw, var(--s9)) clamp(var(--s8), 8vw, var(--s10)); }
.hero__grid { display: grid; grid-template-columns: 1fr 1.05fr; gap: clamp(var(--s6), 5vw, var(--s8)); align-items: center; }
.hero__cta { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s7); }
@media (max-width: 960px) {
  .hero__grid { grid-template-columns: 1fr; gap: var(--s7); }
  .hero { padding-block: var(--s6) var(--s8); }
}

/* ---------------------------------------------------------- wordmark ------ */
/* Set in the site face, tracked and weighted deliberately. Lowercase, per the
   approved direction. A drawn wordmark is a follow-up; type this considered is
   a defensible interim and it themes and scales for nothing. */
.brand__word {
  display: block;
  font-size: 1.45rem;
  font-weight: 620;
  letter-spacing: -0.045em;
  line-height: 1;
  color: var(--fg);
}
.ftr .brand__word { font-size: 1.3rem; }

/* ---------------------------------------------------------------- form ---- */
.form {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: clamp(var(--s5), 3vw, var(--s7)); background: var(--surface);
}
.form__h { font-size: var(--t-h3); margin-bottom: var(--s6); }
.f { margin-bottom: var(--s5); }
.f__label { display: block; font-size: var(--t-small); font-weight: 600; margin-bottom: 6px; }
.f__opt { font-weight: 400; color: var(--fg-3); }
.f__hint { font-size: 0.8125rem; color: var(--fg-2); margin-bottom: 8px; line-height: 1.45; }
.f__input {
  display: block; width: 100%; min-height: 48px; padding: 12px 14px;
  font: inherit; font-size: var(--t-body); color: var(--fg);
  background: var(--bg); border: 1px solid var(--border-strong); border-radius: var(--radius);
  transition: border-color .15s ease;
}
textarea.f__input { min-height: 104px; resize: vertical; line-height: 1.5; }
.f__input:hover { border-color: var(--fg-3); }
.f__input:focus-visible { border-color: var(--fg); }
.f__input[aria-invalid="true"] { border-color: #C2410C; }
:root[data-theme="dark"] .f__input[aria-invalid="true"] { border-color: #FB923C; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .f__input[aria-invalid="true"] { border-color: #FB923C; } }
.f__err { font-size: 0.8125rem; margin-top: 6px; color: #C2410C; }
:root[data-theme="dark"] .f__err { color: #FB923C; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .f__err { color: #FB923C; } }
/* The honeypot. Off-screen rather than display:none, because some autofill and
   some bots skip hidden fields but not positioned ones - and a real person
   using a screen reader never reaches it, since the wrapper is aria-hidden and
   the input is removed from the tab order. */
.f__trap { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.f__note { font-size: var(--t-small); margin-bottom: var(--s4); line-height: 1.5; }
.f__note:empty { display: none; }
.f__note[data-state="error"] { color: #C2410C; }
.f__note[data-state="ok"] { color: var(--accent); }
:root[data-theme="dark"] .f__note[data-state="error"] { color: #FB923C; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .f__note[data-state="error"] { color: #FB923C; } }
.form .btn { width: 100%; }
.form .btn[aria-busy="true"] { opacity: .7; cursor: progress; }
.f__fine { font-size: 0.8125rem; color: var(--fg-2); margin-top: var(--s4); line-height: 1.5; }
.form__done { text-align: left; }
.form__done h2 { font-size: var(--t-h3); margin-bottom: var(--s4); }
.form__done p { color: var(--fg-2); }

/* --------------------------------------------------------- more layout ---- */
/* Four rhythms were not enough.
 *
 * Every page was split, ledger, split, band. On a page with a lot to say that
 * reads as considered restraint; on a page with a little it reads as a
 * template, because the left column of a split with only a heading in it is a
 * third of the screen holding nothing. The additions below all exist to carry
 * real content, not to fill space - a page with nothing to put in them should
 * still use a split and be short.
 */

/* Questions, answered. Native details/summary: it works with no JavaScript,
   it is keyboard operable and announced correctly for free, and the browser's
   find-in-page opens a closed one to show a match. Every hand-built accordion
   on the web gives up at least two of those. */
.faq { border-top: 1px solid var(--border); }
.faq details { border-bottom: 1px solid var(--border); }
.faq summary {
  display: flex; gap: var(--s4); align-items: baseline;
  padding-block: var(--s5); cursor: pointer;
  font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.015em;
  list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: "+"; margin-left: auto; color: var(--fg-3);
  font-weight: 400; font-size: 1.25rem; line-height: 1;
  transition: transform .2s ease;
}
.faq details[open] summary::after { content: "\2212"; }
.faq summary:hover { color: var(--accent); }
.faq summary:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: 2px; }
.faq__a { color: var(--fg-2); padding-bottom: var(--s6); max-width: 70ch; }
.faq__a p + p { margin-top: var(--s4); }

/* Three short notes across. Not a card grid - no borders, no background, no
   icons. Just three columns of prose with a rule above each, which is the
   lightest way to put three related things beside each other without the page
   turning into the feature-tile layout every competitor uses. */
.cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(var(--s5), 3vw, var(--s7)); }
.cols > * { border-top: 1px solid var(--border); padding-top: var(--s5); }
.cols h3 { font-size: 1.0625rem; font-weight: 600; letter-spacing: -0.02em; }
.cols p { color: var(--fg-2); font-size: var(--t-small); margin-top: var(--s3); }
@media (max-width: 860px) { .cols { grid-template-columns: 1fr; gap: var(--s5); } }
.cols--2 { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 720px) { .cols--2 { grid-template-columns: 1fr; } }

/* Term and meaning. For the places where the honest content is a list of
   specifics - what a plan includes, what a figure counts, what a word means
   here - and prose would be pretending it is an argument. */
.kv { display: grid; grid-template-columns: minmax(12rem, 18rem) 1fr; gap: 0; border-top: 1px solid var(--border); }
.kv dt {
  font-family: var(--mono); font-size: var(--t-label); letter-spacing: .08em;
  text-transform: uppercase; color: var(--fg-3);
  padding-block: var(--s5); border-bottom: 1px solid var(--border);
}
.kv dd {
  margin: 0; color: var(--fg-2);
  padding-block: var(--s5); border-bottom: 1px solid var(--border);
}
@media (max-width: 720px) {
  .kv { grid-template-columns: 1fr; }
  .kv dt { padding-bottom: var(--s2); border-bottom: 0; }
  .kv dd { padding-top: 0; }
}

/* A small bordered note, for the left column of a split that would otherwise
   hold a heading and a great deal of nothing. */
.note {
  border-left: 2px solid var(--accent-line);
  padding: var(--s2) 0 var(--s2) var(--s5);
  margin-top: var(--s6);
  color: var(--fg-2); font-size: var(--t-small); max-width: 42ch;
}
.note b { color: var(--fg); font-weight: 600; }

/* Two lists side by side: what today looks like, and what it looks like here.
   Deliberately not a competitor comparison table - the left column describes a
   way of working, never a named product, because a table with a rival's name
   down one side is marketing rather than explanation. */
.versus { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(var(--s5), 4vw, var(--s8)); }
.versus h3 {
  font-family: var(--mono); font-size: var(--t-label); letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-3); font-weight: 400;
  padding-bottom: var(--s4); border-bottom: 1px solid var(--border);
}
.versus ul { list-style: none; margin-top: var(--s5); display: grid; gap: var(--s4); }
.versus li { color: var(--fg-2); padding-left: var(--s5); position: relative; }
.versus li::before {
  content: ""; position: absolute; left: 0; top: .62em;
  width: 10px; height: 1px; background: var(--border-strong);
}
.versus--to li::before { background: var(--accent-line); }
.versus--to li { color: var(--fg); }
@media (max-width: 760px) { .versus { grid-template-columns: 1fr; gap: var(--s6); } }

/* ------------------------------------------------------- calculators ---- */
/* Inputs on the left, answers on the right, and the answers do not move.
 *
 * The single most important property here is that the output column keeps its
 * position while you type. A layout where the numbers reflow as they change
 * makes a calculator feel broken even when it is correct, because the thing
 * you are reading slides out from under you. So every output row is a fixed
 * two-line block whether it holds a number or a dash.
 */
.calc {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: clamp(var(--s6), 4vw, var(--s8));
  align-items: start;
}
.calc__h {
  font-family: var(--mono); font-size: var(--t-label); letter-spacing: .1em;
  text-transform: uppercase; color: var(--fg-3); font-weight: 400;
  padding-bottom: var(--s4); border-bottom: 1px solid var(--border);
  margin-bottom: var(--s5);
}
.calc__in, .calc__out { min-width: 0; }
.calc__f { display: block; margin-bottom: var(--s5); }
.calc__lbl { display: block; font-size: var(--t-small); font-weight: 600; margin-bottom: var(--s2); }
.calc__hint { display: block; font-size: 0.8125rem; color: var(--fg-3); margin-top: var(--s2); max-width: 46ch; }
.calc__row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }

.calc__grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4) var(--s5); }
.calc__o { border-top: 1px solid var(--border); padding-top: var(--s3); min-height: 62px; }
.calc__olbl { display: block; font-size: 0.8125rem; color: var(--fg-2); }
.calc__oval {
  display: block; margin-top: 2px;
  font-size: 1.25rem; font-weight: 600; letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;   /* so digits do not jitter while typing */
}
.calc__total { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s5); margin-top: var(--s5); }
.calc__total .calc__o { border-top-color: var(--border-strong); }
.calc__total .calc__oval { font-size: 1.5rem; }
/* The two numbers somebody actually came for. Marked by weight and a green
   rule rather than a filled panel - no section on this site is ever green. */
.calc__total--key .calc__o { border-top: 2px solid var(--accent-line); }
.calc__total--key .calc__oval { font-size: clamp(1.6rem, 3vw, 2.1rem); }
.calc__note {
  margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--border);
  font-size: 0.8125rem; color: var(--fg-2); font-variant-numeric: tabular-nums;
}
@media (max-width: 860px) {
  .calc { grid-template-columns: 1fr; gap: var(--s7); }
}
@media (max-width: 420px) {
  .calc__grid, .calc__total, .calc__row { grid-template-columns: 1fr; }
}

/* Columns whose heading is a link - the tools hub. */
.cols h3 a { color: inherit; text-decoration: none; }
.cols h3 a:hover { color: var(--accent); }

/* ------------------------------------------------------------- motion ----- */
/* WHAT THIS IS FOR
 *
 * Every page on this site is a separate HTML document, so moving between them
 * was a white flash and a new page arriving fully formed. That is correct and
 * it is also the thing that makes a static site feel like a stack of documents
 * rather than one product.
 *
 * Cross-document view transitions fix it with no JavaScript at all: the
 * browser holds the old page, paints the new one, and crossfades between them.
 * Where it is not supported the navigation is exactly what it was before, so
 * there is no fallback to maintain and nothing to feature-detect.
 *
 * The header and footer are named, which takes them OUT of the crossfade. They
 * are identical on both pages, so fading one into an identical copy of itself
 * only produces a flicker on the one element the reader is most likely to be
 * looking at. Named elements are matched and left alone instead.
 */
@view-transition { navigation: auto; }

::view-transition-group(root) { animation-duration: .32s; }
::view-transition-old(root) {
  animation: vt-out .18s cubic-bezier(.4, 0, 1, 1) both;
}
::view-transition-new(root) {
  animation: vt-in .34s cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(8px); } }

.hdr { view-transition-name: hdr; }
.ftr { view-transition-name: ftr; }

/* Sections arriving as you reach them.
 *
 * The hidden state is applied ONLY under html.reveal, a class app.js adds
 * before first paint. With JavaScript off, or before the script runs, nothing
 * is hidden and the page is complete - which is the whole requirement. A
 * reveal that starts from opacity:0 in the stylesheet is a page that is blank
 * for anybody whose script did not load, and that is a far worse outcome than
 * having no animation.
 *
 * translateY is 14px, not 40px. A long travel means the reader watches things
 * slide for a third of a second before they can read them; the point is to
 * acknowledge arrival, not to perform. */
.reveal [data-reveal]:not(.is-in) {
  opacity: 0;
  transform: translateY(14px);
}
.reveal [data-reveal] {
  transition: opacity .55s cubic-bezier(.16, 1, .3, 1),
              transform .55s cubic-bezier(.16, 1, .3, 1);
}

/* Printing, and anything else that renders without scrolling.
 *
 * A reveal is driven by IntersectionObserver, and nothing below the fold ever
 * intersects in a context that does not scroll. Press Ctrl+P on a page built
 * this way and the first screen prints followed by several blank pages. The
 * same is true of a screenshot tool, a reader-mode extension and a crawler
 * that renders. All of them want the document, not the choreography. */
@media print {
  .reveal [data-reveal]:not(.is-in) { opacity: 1 !important; transform: none !important; }
}

/* --------------------------------------------------------- less motion ---- */
/* Not "smaller animations" - none. A reader who has asked their operating
   system for reduced motion has usually asked because motion makes them ill,
   and a tasteful 200ms slide is still motion. The only thing kept is the
   colour change on hover, which is not movement. */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  .reveal [data-reveal]:not(.is-in) { opacity: 1; transform: none; }
  .reveal [data-reveal] { transition: none; }
  .btn { transition: background .16s ease, border-color .16s ease, color .16s ease; }
  .btn:hover, .btn:active { transform: none; }
  .btn::after { content: none; }
}

/* ============================================================================
   PRODUCT SCREENS
   ----------------------------------------------------------------------------
   The interface, drawn as markup rather than photographed. See
   site/lib/screens.mjs for why.

   Three rules hold this together and are worth stating because breaking any of
   them quietly turns a product screen back into decoration:

   1. NUMBERS ALIGN. Every figure is tabular-nums and right-set. A column of
      hours where the decimal points wander is the single clearest signal that
      a screenshot is fake, because no real financial interface looks like that.

   2. STATUS IS A WORD. The dot is a second channel for people scanning; the
      word is what carries the meaning. Nothing here is legible only in colour,
      which also means nothing breaks when the palette is monochrome.

   3. NARROW IS A CROP, NOT A SHRINK. Below 640px whole columns leave
      (.col-hide-s) and the remaining ones keep their full type size. Scaling a
      five-column table down to phone width produces six-point text that
      photographs well and cannot be read, which is the opposite of the job.
   ========================================================================== */

.scrwrap { margin: 0; }
.scrwrap__cap {
  margin-top: var(--s4);
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--fg-3);
  max-width: 62ch;
}

.scr {
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
  /* A quiet lift. The screen is an object on the page, not a panel of it. */
  box-shadow: 0 1px 2px rgba(0, 0, 0, .04), 0 18px 40px -28px rgba(0, 0, 0, .28);
  font-size: 13px;
  line-height: 1.45;
}
:root[data-theme="dark"] .scr,
html:not([data-theme="light"]) .scr { box-shadow: 0 1px 2px rgba(0, 0, 0, .5), 0 22px 48px -30px rgba(0, 0, 0, .8); }

/* --- chrome ---------------------------------------------------------------- */
.scr__bar {
  display: flex; align-items: baseline; gap: 10px;
  padding: 11px 14px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.scr__app {
  font-size: 11px; font-weight: 600; letter-spacing: .09em;
  text-transform: uppercase; color: var(--fg);
}
.scr__crumb { font-size: 11.5px; color: var(--fg-3); }

.scr__tabs {
  display: flex; gap: 2px; padding: 0 8px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none;
}
.scr__tabs::-webkit-scrollbar { display: none; }
.scr__tab {
  flex: none; padding: 9px 10px;
  font-size: 11.5px; color: var(--fg-3);
  border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap;
}
.scr__tab.is-on { color: var(--fg); border-bottom-color: var(--fg); font-weight: 500; }

.scr__body { padding: 0; }
.scr__foot {
  display: flex; flex-wrap: wrap; gap: 4px 16px; justify-content: space-between;
  padding: 10px 14px; border-top: 1px solid var(--border);
  background: var(--surface-2); font-size: 11.5px; color: var(--fg-2);
}
.scr__foot strong { color: var(--fg); font-variant-numeric: tabular-nums; }
.scr__foot-r { color: var(--fg-3); }

/* --- tables ---------------------------------------------------------------- */
.scr__t { width: 100%; border-collapse: collapse; }
.scr__t th, .scr__t td {
  text-align: left; padding: 11px 14px;
  border-bottom: 1px solid var(--border); vertical-align: top;
}
.scr__t tbody tr:last-child td { border-bottom: 0; }
.scr__t th {
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--fg-3); padding-top: 9px; padding-bottom: 9px; white-space: nowrap;
}
.scr__t .num { text-align: right; }
.scr__t .tab { font-variant-numeric: tabular-nums; white-space: nowrap; }
.scr__t--tight th, .scr__t--tight td { padding: 8px 14px; }
.scr__t tr.is-total td { border-top: 1px solid var(--border-strong); }

/* A row that needs attention gets a rule, not a wash. A tinted row is a colour
   telling you something; a rule is a mark you can see in greyscale, in high
   contrast mode, and on a projector in a bright room. */
.scr__t tr.is-flagged td:first-child { box-shadow: inset 2px 0 0 var(--fg); }

.scr__sub { display: block; font-size: 11.5px; color: var(--fg-3); margin-top: 2px; }
.scr__sub-i { font-size: 12px; color: var(--fg-3); }
.scr__of { color: var(--fg-3); font-weight: 400; }

/* --- status ---------------------------------------------------------------- */
.pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; white-space: nowrap; color: var(--fg-2);
}
.pill + .pill { margin-left: 10px; }
.pill i {
  width: 6px; height: 6px; border-radius: 50%; flex: none;
  background: var(--fg-3); box-shadow: 0 0 0 1px var(--surface);
}
/* Three marks a reader can tell apart with the colour removed:
     ok    a filled disc      - done, confirmed, verified
     warn  an open ring       - in flight, waiting on somebody
     bad   a filled square    - needs a decision now
   Shape carries it; the word beside it carries it again. */
.pill--ok i   { background: var(--fg); }
.pill--warn i { background: transparent; border: 1.5px solid var(--fg-2); box-shadow: none; width: 7px; height: 7px; }
.pill--bad i  { background: var(--fg); border-radius: 1px; width: 7px; height: 7px; }
.pill--ok, .pill--bad { color: var(--fg); }
.pill--bad { font-weight: 500; }

/* --- worker head ----------------------------------------------------------- */
.scr__head {
  display: flex; flex-wrap: wrap; gap: 10px 16px; justify-content: space-between;
  align-items: baseline; padding: 14px; border-bottom: 1px solid var(--border);
}
.scr__head strong { font-size: 14px; }
.scr__headr { display: flex; align-items: center; }

/* --- pipeline -------------------------------------------------------------- */
.scr__cols {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(148px, 1fr);
  gap: 1px; background: var(--border);
  overflow-x: auto; scrollbar-width: thin;
}
.scr__col { background: var(--surface); padding: 12px; min-height: 176px; }
.scr__colh {
  display: flex; justify-content: space-between; align-items: baseline; gap: 8px;
  font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--fg-3); margin-bottom: 10px;
}
.scr__count { font-variant-numeric: tabular-nums; color: var(--fg); font-size: 12px; letter-spacing: 0; }
.scr__card {
  border: 1px solid var(--border); border-radius: 3px;
  padding: 9px 10px; margin-bottom: 7px; background: var(--surface);
}
.scr__card strong { font-size: 12.5px; display: block; }

/* --- hours to invoice ------------------------------------------------------ */
.scr__split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); }
.scr__split > * + * { border-left: 1px solid var(--border); }
.scr__mini {
  padding: 11px 14px 9px; font-size: 10.5px; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase; color: var(--fg-3);
}
.scr__note {
  margin: 0; padding: 10px 14px 14px; font-size: 11.5px;
  color: var(--fg-3); border-top: 1px solid var(--border);
}
.scr__note strong { color: var(--fg-2); }

/* A table may scroll inside its own screen. The PAGE may never scroll
   sideways, which is why the container is here and not on the section. */
.scr__scroll { overflow-x: auto; scrollbar-width: thin; }
/* Without a floor the table just squeezes: cells narrow until nowrap content
   spills past the screen's clipped edge, and the scroll container never
   engages because nothing is technically overflowing it. The floor is what
   turns "clipped" into "scrollable" on a 320px phone. */
.scr__t--board { min-width: 340px; }
.scr__sub--narrow { display: none; }

/* --- narrow: crop, never shrink --------------------------------------------
   Below 640px the Site column leaves and its content reappears on a second
   line under the client, together with the shift time. The information is not
   dropped and the type does not get smaller - the table is re-cut for the
   width, which is the difference between a mobile layout and a squeezed one. */
@media (max-width: 640px) {
  .scr { font-size: 12.5px; }
  .col-hide-s, .col-hide-xs { display: none; }
  .scr__sub--narrow { display: block; }
  .scr__t th, .scr__t td { padding: 10px 12px; }
  /* The status cell holds a pill and a note. Both were nowrap, which is correct
     at desk width and is what clipped "1 unconfirmed · 1 seat open" against the
     screen's edge on a phone. Here they wrap. */
  .scr__t td:last-child { min-width: 0; }
  .scr__t td:last-child .scr__sub { white-space: normal; }
  .pill { white-space: normal; }
  .scr__split { grid-template-columns: 1fr; }
  .scr__split > * + * { border-left: 0; border-top: 1px solid var(--border-strong); }
  .scr__foot { font-size: 11px; }
  .scr__foot-r { display: none; }
}

/* A quiet alternate ground, so two product sections in a row do not read as one
   very long section. Not a new colour - the same surface-2 already used by the
   screen chrome, which keeps the page to two grounds rather than three. */
.section--tint { background: var(--surface-2); }
