/*
 * Simplest1 CSS: the Ink & Paper design system.
 * Tonal monochrome, one sans (IBM Plex Sans), IBM Plex Mono for data.
 * See docs/design/DESIGN.md (doctrine) and docs/design/CSS.md (components).
 */

@view-transition {
  navigation: auto;
}

/* The cascade layers. `marketing` holds what web/static/css/marketing.css
   shares with this sheet (the button, the wordmark, the skip link, the
   language switcher) on the pages that load both, so `components` here
   outranks it: an unlayered sheet beats every layered rule whatever its
   specificity, so marketing.css's .btn would override .btn--outline on
   /sign-in (CSS.md "Cascade layers"). */
@layer reset, tokens, base, layout, marketing, components, utilities;

@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/IBM_Plex_Sans/IBMPlexSans-Variable.ttf") format("truetype-variations");
  font-weight: 100 700;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: "IBM Plex Sans";
  src: url("/static/fonts/IBM_Plex_Sans/IBMPlexSans-Italic-Variable.ttf") format("truetype-variations");
  font-weight: 100 700;
  font-style: italic;
  font-display: swap;
}

/* Arabic and Devanagari are their own families, listed right after Plex
   Sans in every stack, and load only for their own characters
   (unicode-range). Sharing Plex Sans's family name does not work: Chrome
   matches the variable Latin face and never consults them (DESIGN.md
   "Font Stack"). */
@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/static/fonts/IBM_Plex_Sans_Arabic/IBMPlexSansArabic-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}

@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/static/fonts/IBM_Plex_Sans_Arabic/IBMPlexSansArabic-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}

@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/static/fonts/IBM_Plex_Sans_Arabic/IBMPlexSansArabic-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}

@font-face {
  font-family: "IBM Plex Sans Arabic";
  src: url("/static/fonts/IBM_Plex_Sans_Arabic/IBMPlexSansArabic-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0600-06FF, U+0750-077F, U+0870-08FF, U+200C-200E, U+FB50-FDFF, U+FE70-FEFF;
}

@font-face {
  font-family: "IBM Plex Sans Devanagari";
  src: url("/static/fonts/IBM_Plex_Sans_Devanagari/IBMPlexSansDevanagari-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

@font-face {
  font-family: "IBM Plex Sans Devanagari";
  src: url("/static/fonts/IBM_Plex_Sans_Devanagari/IBMPlexSansDevanagari-Medium.ttf") format("truetype");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

@font-face {
  font-family: "IBM Plex Sans Devanagari";
  src: url("/static/fonts/IBM_Plex_Sans_Devanagari/IBMPlexSansDevanagari-SemiBold.ttf") format("truetype");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

@font-face {
  font-family: "IBM Plex Sans Devanagari";
  src: url("/static/fonts/IBM_Plex_Sans_Devanagari/IBMPlexSansDevanagari-Bold.ttf") format("truetype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20B9, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

@font-face {
  font-family: "IBM Plex Mono";
  src: url("/static/fonts/IBM_Plex_Mono/IBMPlexMono-Regular.ttf") format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* ==========================================================================
   Reset
   ========================================================================== */

@layer reset {
  *, *::before, *::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }

  /* The hidden attribute must win over component display rules (.btn etc.),
     so `el.hidden = true` from behaviors actually hides. */
  [hidden] {
    display: none !important;
  }

  html {
    font-size: 16px;
    -webkit-text-size-adjust: none;
    text-size-adjust: none;
    scroll-behavior: smooth;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
  }

  body {
    min-height: 100dvh;
    line-height: 1.6;
  }

  img, picture, video, canvas, svg {
    display: block;
    max-width: 100%;
    height: auto;
  }

  input, button, textarea, select {
    font: inherit;
    color: inherit;
  }

  button, summary { cursor: pointer; }

  h1, h2, h3, h4, h5, h6 {
    overflow-wrap: break-word;
    text-wrap: balance;
  }

  p { text-wrap: pretty; }
  ul, ol { list-style: none; }
  table { border-collapse: collapse; }

  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* ==========================================================================
   Design Tokens (Ink & Paper)
   ========================================================================== */

@layer tokens {
  :root {
    /* === Color: tonal monochrome === */
    --ink: #141413;
    --ink-2: #5f5e5a;
    --ink-3: #8a8985;
    --paper: #ffffff;
    --paper-2: #f7f6f4;
    --line: rgba(20, 20, 19, 0.14);
    --line-soft: rgba(20, 20, 19, 0.07);
    /* Brick red: a refused form only (DESIGN.md "The error rule") */
    --red: #9f3a2c;
    --red-tint: #fcf4f2;
    /* A code block is ink on paper reversed, so its tokens are set for
       the other ground: light hues on the light page's dark block, dark
       hues on the dark page's light one (CSS.md "Prose"). */
    --code-keyword: #f0a092;
    --code-string: #a9d3a0;
    --code-comment: #8a8983;
    --code-number: #e5c07b;
    --code-function: #93bdf2;
    --code-type: #cfaef0;
    --accent: #e8604c; /* the wordmark "1" only; never UI states */

    /* Semantic aliases (canonical names for component CSS) */
    --color-fg: var(--ink);
    --color-fg-muted: var(--ink-2);
    --color-fg-subtle: var(--ink-3);
    --color-bg: var(--paper);
    --color-bg-secondary: var(--paper-2);
    --color-bg-hover: var(--paper-2);
    --color-border: var(--line);
    --color-border-light: var(--line-soft);
    /* The site's accent: links in running text and the primary button.
       Ink by default, so it follows the theme; a public page whose owner
       picked a colour overrides both from the "Accent color" setting
       (SETTINGS.md "What the public pages wear"). --accent above is the
       wordmark's and never this. */
    --site-accent: var(--ink);
    --site-accent-ink: var(--paper);
    --color-link: var(--site-accent);
    --color-link-hover: var(--site-accent);

    /* Status colors are ink: state is carried by copy, not hue. The one
       exception is a refused form, below. */
    --color-success: var(--ink);
    --color-success-bg: var(--paper);
    --color-success-fg: var(--ink);
    --color-danger: var(--ink);
    --color-danger-bg: var(--paper);
    --color-danger-fg: var(--ink);
    --color-warning: var(--ink);
    --color-warning-bg: var(--paper);
    --color-warning-fg: var(--ink);
    /* A refused form: the message, its field's border, the error box */
    --color-error: var(--red);
    --color-error-bg: var(--red-tint);
    --color-error-border: var(--red);

    /* === Typography === */
    --font-sans: "IBM Plex Sans", "IBM Plex Sans Arabic", "IBM Plex Sans Devanagari", -apple-system, BlinkMacSystemFont,
                 "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: "IBM Plex Mono", "IBM Plex Sans Arabic", "IBM Plex Sans Devanagari", ui-monospace, "SF Mono", Menlo, monospace;

    --text-xs: 0.75rem;     /* 12px */
    --text-sm: 0.875rem;    /* 14px */
    --text-base: 1rem;      /* 16px */
    --text-md: 1.125rem;    /* 18px */
    --text-lg: 1.375rem;    /* 22px */
    --text-xl: 1.75rem;     /* 28px */
    --text-2xl: 2.125rem;   /* 34px */
    --text-3xl: 2.625rem;   /* 42px */
    --text-4xl: 3.25rem;    /* 52px */
    --text-5xl: 4rem;       /* 64px */
    --text-hero: clamp(2.375rem, 5.2vw, 3.5rem);

    --leading-tight: 1.15;
    --leading-normal: 1.5;
    --leading-relaxed: 1.7;

    --weight-light: 400;    /* light retired; alias to regular */
    --weight-regular: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;

    --tracking-tight: -0.022em;
    --tracking-normal: -0.011em;

    /* === Spacing (4px grid) === */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.25rem;
    --space-6: 1.5rem;
    --space-8: 2rem;
    --space-10: 2.5rem;
    --space-12: 3rem;
    --space-16: 4rem;
    --space-24: 6rem;
    --space-32: 8rem;

    /* === Layout === */
    --max-width-narrow: 38rem;
    --max-width-default: 56rem;
    --max-width-wide: 72rem;
    --max-width-prose: 38rem;

    /* === Depth: hairlines + the fixed radius scale. No shadows. === */
    --radius-sm: 8px;
    --radius-md: 12px;
    --radius-pill: 999px;
    --radius: var(--radius-sm); /* the default radius */
    --border: 1px solid var(--line);
    --underline-offset: 0.2em;

    /* === Motion === */
    --duration: 150ms;
    --ease: ease;

    /* === A signature: drawn on paper in either theme, so the drawing a
       member later sees reads the same as when it was drawn === */
    --signature-paper: #ffffff;
    --signature-ink: #141413;
  }

  /* === Dark mode: tonal, not inversion === */
  /* color-scheme gives the browser's own parts (a time picker's clock, a
     select's list, the scrollbars) the theme's colors too. */
  @media (prefers-color-scheme: dark) {
    :root {
      color-scheme: dark;
      --ink: #f2f1ee;
      --ink-2: #a3a29d;
      --ink-3: #7c7b76;
      --paper: #121211;
      --paper-2: #1b1b1a;
      --line: rgba(242, 241, 238, 0.16);
      --line-soft: rgba(242, 241, 238, 0.08);
      --red: #e58a79;
      --red-tint: #251614;
      --code-keyword: #a8382a;
      --code-string: #2b6b35;
      --code-comment: #6f6e69;
      --code-number: #85600d;
      --code-function: #245ea8;
      --code-type: #7141a0;
    }
  }

  /* === Chosen theme ===
     The admin theme toggle sets data-theme on <html>, and the server sets it
     on the error pages under /admin from the toggle's cookie; it always
     beats the OS preference. Both themes are the tonal Ink & Paper palettes (DESIGN.md);
     dark is not an inversion. */
  [data-theme="dark"] {
    color-scheme: dark;
    --ink: #f2f1ee;
    --ink-2: #a3a29d;
    --ink-3: #7c7b76;
    --paper: #121211;
    --paper-2: #1b1b1a;
    --line: rgba(242, 241, 238, 0.16);
    --line-soft: rgba(242, 241, 238, 0.08);
    --red: #e58a79;
    --red-tint: #251614;
    --code-keyword: #a8382a;
    --code-string: #2b6b35;
    --code-comment: #6f6e69;
    --code-number: #85600d;
    --code-function: #245ea8;
    --code-type: #7141a0;
    --icon-filter: invert(1);
  }

  [data-theme="light"] {
    color-scheme: light;
    --ink: #141413;
    --ink-2: #5f5e5a;
    --ink-3: #8a8985;
    --paper: #ffffff;
    --paper-2: #f7f6f4;
    --line: rgba(20, 20, 19, 0.14);
    --line-soft: rgba(20, 20, 19, 0.07);
    --red: #9f3a2c;
    --red-tint: #fcf4f2;
    --code-keyword: #f0a092;
    --code-string: #a9d3a0;
    --code-comment: #8a8983;
    --code-number: #e5c07b;
    --code-function: #93bdf2;
    --code-type: #cfaef0;
    --icon-filter: none;
  }
}

/* ==========================================================================
   Base Styles
   ========================================================================== */

@layer base {
  body {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    font-weight: var(--weight-regular);
    line-height: var(--leading-relaxed);
    letter-spacing: var(--tracking-normal);
    color: var(--color-fg);
    background-color: var(--color-bg);
  }

  /* One family everywhere. Hierarchy = weight + size, never a second face
     and never ALL CAPS. */
  h1, h2, h3, h4, h5, h6 {
    font-weight: var(--weight-medium);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    color: var(--color-fg);
  }

  h1 { font-size: var(--text-3xl); }
  h2 { font-size: var(--text-2xl); }

  /* Small headings: 600 and neutral tracking. Bold plus negative tracking
     chokes at these sizes. */
  h3 { font-size: var(--text-lg); font-weight: var(--weight-semibold); letter-spacing: normal; }
  h4 { font-size: var(--text-md); font-weight: var(--weight-semibold); letter-spacing: normal; }
  h5 { font-size: var(--text-base); font-weight: var(--weight-semibold); letter-spacing: normal; }
  h6 { font-size: var(--text-sm); font-weight: var(--weight-semibold); letter-spacing: normal; }

  p { color: var(--color-fg); }

  a {
    color: inherit;
    text-decoration: underline;
    text-decoration-skip-ink: auto;
    text-underline-offset: var(--underline-offset);
    text-decoration-color: var(--line);
    transition: text-decoration-color var(--duration) var(--ease);

    &:hover {
      text-decoration-color: var(--color-fg);
    }
  }

  strong {
    font-weight: var(--weight-bold);
  }

  code {
    font-family: var(--font-mono);
    font-size: 0.875em;
    background-color: var(--paper-2);
    border-radius: 4px;
    padding: 0.125em 0.375em;
  }

  pre {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    line-height: var(--leading-relaxed);
    background-color: var(--paper-2);
    border-radius: var(--radius-sm);
    padding: var(--space-6);
    overflow-x: auto;

    & code {
      background: none;
      border-radius: 0;
      padding: 0;
    }
  }

  /* Dates are data: mono, tabular */
  time {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
  }

  blockquote {
    font-size: var(--text-lg);
    color: var(--color-fg-muted);
    padding-inline-start: var(--space-6);
    border-inline-start: 2px solid var(--line);
  }

  hr {
    border: none;
    border-top: 1px solid var(--line-soft);
    margin: var(--space-12) 0;
  }

  ul, ol {
    padding-inline-start: var(--space-6);
  }

  fieldset {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
  }

  ul { list-style-type: disc; }
  ol { list-style-type: decimal; }

  li {
    padding-inline-start: var(--space-2);
    margin-bottom: var(--space-2);

    &::marker {
      color: var(--color-fg-subtle);
    }
  }

  ::selection {
    background: var(--color-fg);
    color: var(--color-bg);
  }

  :focus-visible {
    outline: 2px solid var(--color-fg);
    outline-offset: 3px;
    border-radius: 2px;
  }
}

/* ==========================================================================
   Layout
   ========================================================================== */

@layer layout {
  .site {
    display: flex;
    flex-direction: column;
    min-height: 100dvh;
  }

  /* A word longer than the line (a pasted address, a long name) breaks
     where it must instead of pushing the page sideways; min-width lets the
     column be narrower than its longest word (CSS.md "Prose"). */
  .site__content {
    flex: 1;
    min-width: 0;
    overflow-wrap: break-word;
  }

  .container {
    width: 100%;
    max-width: var(--max-width-default);
    margin-inline-start: auto;
    margin-inline-end: auto;
    padding-inline-start: var(--space-6);
    padding-inline-end: var(--space-6);

    @media (min-width: 768px) {
      padding-inline-start: var(--space-8);
      padding-inline-end: var(--space-8);
    }
  }

  .container--wide {
    max-width: var(--max-width-wide);
  }

  .container--narrow {
    max-width: var(--max-width-narrow);
  }

  /* Sections separate with whitespace: no rules, no tinted bands */
  .section {
    padding-top: var(--space-16);
    padding-bottom: var(--space-16);
  }

  /* A page another site shows in a frame: no chrome, a little room at the
     edges. It is the box embed-height measures, so it holds its children's
     margins (flow-root) and the frame shrinks as well as grows. */
  .embed {
    display: flow-root;
    min-width: 0;
    padding: var(--space-4);
    overflow-wrap: break-word;
  }
}

/* ==========================================================================
   Components
   ========================================================================== */

@layer components {
  /* --------------------------------------------------------------------------
     Header
     -------------------------------------------------------------------------- */

  .site-header {
    padding: var(--space-5) 0;
  }

  .header {
    display: flex;
    justify-content: space-between;
    align-items: center;
  }

  .header__logo {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: var(--color-fg);
    text-decoration: none;

    &:hover {
      color: var(--color-fg);
    }

    & img {
      height: 1.5rem;
      width: auto;
    }
  }

  /* The brand wordmark: text, not an image. The "1" is the one place
     --accent may appear. */
  /* The wordmark is an inline SVG (shared/wordmark.html) with outlined
     glyphs - no font loads for it. font-size still drives the scale via
     the em height, so wm--lg keeps working. */
  .wm {
    display: inline-flex;
    align-items: center;
    font-size: var(--text-md);
    color: var(--color-fg);
    text-decoration: none;
  }

  .wm svg {
    display: block;
    height: 0.92em;
    width: auto;
  }

  .wm--lg {
    font-size: var(--text-lg);
  }

  .header__logo-suffix {
    font-family: var(--font-mono);
    font-size: var(--text-xs);
    color: var(--color-fg-subtle);
  }

  /* The language switcher: a button and a native popover
     (shared/lang-switcher.html; I18N.md "Templates"). One word closed, a
     list open, any number of languages; a click elsewhere or Escape closes
     it without a script. */
  .lang-switcher {
    position: relative;
    anchor-scope: --lang-switcher;
    font-size: var(--text-xs);

    & .lang-switcher__button {
      anchor-name: --lang-switcher;
      display: inline-flex;
      align-items: center;
      gap: 0.45em;
      padding: 0;
      border: 0;
      background: none;
      font: inherit;
      color: var(--color-fg-muted);
      white-space: nowrap;
      cursor: pointer;
    }

    /* A box with two borders turned 45 degrees; the [dir="rtl"] rule at the
       end of the sheet turns it the other way (CSS.md "Logical directions"). */
    & .lang-switcher__button::after {
      content: "";
      width: 0.45em;
      height: 0.45em;
      border-inline-end: 1px solid currentColor;
      border-bottom: 1px solid currentColor;
      transform: rotate(45deg) translateY(-0.15em);
    }

    &:has(:popover-open) .lang-switcher__button {
      color: var(--color-fg);
    }

    /* The compact control of a header row: a globe and the language's code,
       its name kept for a screen reader. marketing.css carries the same
       rules in its own tokens for the homepage, which loads it alone; this
       copy serves every page that loads global.css, /launch first. */
    &.lang-switcher--compact .lang-switcher__button {
      gap: 0.35em;
      /* The homepage's own size, off the token scale on purpose: one control
         reads the same on both pages. */
      font-size: 0.8125rem;
      color: var(--color-fg-muted);
    }

    &.lang-switcher--compact .lang-switcher__button::after {
      content: none;
    }

    & .lang-switcher__icon {
      display: block;
      width: 1.05rem;
      height: 1.05rem;
    }

    & .lang-switcher__code {
      font-weight: var(--weight-medium);
    }

    & .lang-switcher__name {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
      border: 0;
    }

    /* Anchored under the button, above it when there is no room below; a
       browser without anchor positioning shows the list centered. */
    & .lang-switcher__menu {
      padding: 0.35rem 0;
      list-style: none;
      min-width: 11rem;
      max-height: 60vh;
      overflow-y: auto;
      color: var(--color-fg);
      background: var(--color-bg);
      border: 1px solid var(--line);
      border-radius: var(--radius-sm);
      box-shadow: 0 8px 24px rgba(20, 20, 19, 0.1);

      @supports (anchor-name: --a) {
        position-anchor: --lang-switcher;
        inset: auto;
        top: anchor(bottom);
        inset-inline-end: anchor(end);
        margin: 0.5rem 0 0;
        position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;
      }
    }

    & li a {
      display: block;
      padding: 0.45rem 1rem;
      color: var(--color-fg-muted);
      text-decoration: none;
      white-space: nowrap;
    }

    & li a:hover {
      background: var(--color-bg-hover);
    }

    & li a[aria-current="true"] {
      color: var(--color-fg);
      font-weight: var(--weight-medium);
    }
  }

  /* A browser without the popover would show every language inline; it
     gets no switcher, and the prefixed links still carry the language. */
  @supports not selector(:popover-open) {
    .lang-switcher {
      display: none;
    }
  }

  .header__nav {
    display: flex;
    align-items: center;
    gap: var(--space-6);

    & a:not(.btn) {
      font-size: var(--text-sm);
      color: var(--color-fg-muted);
      text-decoration: none;
      transition: color var(--duration) var(--ease);

      &:hover {
        color: var(--color-fg);
      }

      &[aria-current="page"],
      &.active {
        color: var(--color-fg);
        font-weight: var(--weight-medium);
      }
    }
  }

  /* --------------------------------------------------------------------------
     Footer
     -------------------------------------------------------------------------- */

  .footer {
    padding: var(--space-8) 0;
    margin-top: var(--space-8);
    border-top: 1px solid var(--line-soft);
  }

  /* The site's profiles and contact address, as quiet icons on the
     copyright's line, at its end, and under it when the line is too narrow
     (SETTINGS.md "What the public pages wear"). Each is a 44px target; the
     icon inside stays small, and the row's outer targets hang past the
     column so the icons line up with its edge. */
  .footer:has(.footer__links) {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    column-gap: var(--space-4);
  }

  /* The copyright's line box is the icons' 44px, so the words and the
     icons share one centre. */
  .footer:has(.footer__links) > p {
    margin: 0;
    line-height: 2.75rem;
  }

  .footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-1);
    margin: 0 calc(-1 * var(--space-3));
    padding: 0;
    list-style: none;

    & > li {
      margin: 0;
    }
  }

  .footer__link {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    color: var(--color-fg-muted);
    transition: color var(--duration) var(--ease);

    &:hover {
      color: var(--color-fg);
    }

    & svg {
      width: 1.25rem;
      height: 1.25rem;
    }
  }

  /* A brand logo is full-bleed; size it down optically against the inset
     line icons (ICONS.md). */
  .footer__link--brand svg {
    width: 1.05rem;
    height: 1.05rem;
  }

  /* --------------------------------------------------------------------------
     Navigation
     -------------------------------------------------------------------------- */

  /* --------------------------------------------------------------------------
     Dropdown (click-based; behavior: dropdown)
     -------------------------------------------------------------------------- */

  .dropdown {
    position: relative;
    display: inline-block;
  }

  .dropdown__trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: 0;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    background: none;
    border: none;
    cursor: pointer;
    color: inherit;
    text-decoration: none;

    &::after {
      content: "▾";
      font-size: 0.75em;
      opacity: 0.6;
    }

    &:has(.dropdown__icon)::after {
      display: none;
    }
  }

  .dropdown__icon {
    width: 24px;
    height: 24px;
  }

  /* The "more options" kebab: one Lucide ellipsis-vertical, never a text
     glyph (ICONS.md "The rule"), so nothing here sizes type. */
  .dropdown__trigger--icon {
    padding: var(--space-1) var(--space-2);
    color: var(--color-fg-muted);

    /* The icon keeps its size: in a table row squeezed by a phone's width
       it shrank to a 2px sliver, and the row's menu could not be seen. */
    & svg {
      flex-shrink: 0;
    }

    &::after {
      display: none;
    }

    &:hover {
      color: var(--color-fg);
    }
  }

  .dropdown__menu {
    position: absolute;
    top: calc(100% + var(--space-2));
    inset-inline-end: 0;
    min-width: 10rem;
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: var(--space-1);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-0.25rem);
    transition: all var(--duration) var(--ease);
    z-index: 100;
  }

  .dropdown.open .dropdown__menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }

  .dropdown--up .dropdown__menu {
    top: auto;
    bottom: calc(100% + var(--space-2));
    transform: translateY(0.25rem);
  }

  .dropdown--up.open .dropdown__menu {
    transform: translateY(0);
  }

  .dropdown__item {
    display: block;
    width: 100%;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-sm);
    color: var(--color-fg);
    text-align: start;
    text-decoration: none;
    background: none;
    border: none;
    border-radius: 4px;
    cursor: pointer;
  }

  .dropdown__item:hover {
    background-color: var(--paper-2);
    text-decoration: none;
  }

  .dropdown__item--danger {
    color: var(--color-fg);

    &:hover {
      background-color: var(--ink);
      color: var(--paper);
    }
  }

  .dropdown__label {
    display: block;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-xs);
    color: var(--color-fg-subtle);
  }

  .dropdown__divider {
    border-top: 1px solid var(--line-soft);
    margin: var(--space-1) 0;
  }

  /* Ink chrome dropdown variant */
  /* --------------------------------------------------------------------------
     Buttons: sentence case, pill, tonal hover. One size system.
     -------------------------------------------------------------------------- */

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    white-space: nowrap;
    padding: 0.5625rem 1.375rem;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    text-decoration: none;
    color: var(--site-accent-ink);
    background-color: var(--site-accent);
    border: 1px solid var(--site-accent);
    border-radius: var(--radius-pill);
    cursor: pointer;
    transition: opacity var(--duration) var(--ease),
                border-color var(--duration) var(--ease);

    &:hover {
      opacity: 0.85;
      text-decoration: none;
    }
  }

  .btn--secondary,
  .btn--outline {
    color: var(--color-fg);
    background-color: transparent;
    border: 1px solid var(--line);

    &:hover {
      opacity: 1;
      border-color: var(--color-fg);
    }
  }

  .btn--ghost {
    color: var(--color-fg);
    background-color: transparent;
    border-color: transparent;

    &:hover {
      opacity: 1;
      border-color: var(--line);
    }
  }

  .btn--danger {
    color: var(--paper);
    background-color: var(--ink);
    border-color: var(--ink);
  }

  /* One compact size. */
  .btn--sm {
    padding: 0.375rem 1rem;
    font-size: var(--text-xs);
  }

  .btn--large {
    padding: 0.8125rem 1.875rem;
    font-size: var(--text-base);
  }

  .btn--block {
    width: 100%;
  }

  .btn--link {
    background: none;
    color: var(--color-fg-muted);
    padding: 0;
    border: none;
    border-radius: 0;
    font-weight: var(--weight-regular);
    text-decoration: underline;
    text-underline-offset: var(--underline-offset);
    text-decoration-color: var(--line);

    &:hover {
      opacity: 1;
      background: none;
      color: var(--color-fg);
      text-decoration-color: var(--color-fg);
    }
  }

  /* --------------------------------------------------------------------------
     Forms: hairlines, radius-sm, sentence-case labels
     -------------------------------------------------------------------------- */

  .form-group {
    margin-bottom: var(--space-6);
  }

  /* A choice group among the fields is a field, not a box: the base
     fieldset rule draws a border around every fieldset, and a group of
     radios or checkboxes read as heavy as a card. Its legend is its label
     (CSS.md "Form rhythm"). A section of a page keeps its own look
     (form-fieldset, settings-group). */
  fieldset.form-group {
    border: 0;
    padding: 0;
    margin-inline: 0;
    min-inline-size: 0;

    /* As wide as a label, so a legend in its own direction starts where
       a label in that direction does. */
    & > legend {
      padding: 0;
      inline-size: 100%;
    }
  }

  .form-label {
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-fg);
    margin-bottom: var(--space-2);
  }

  .form-input,
  .form-textarea,
  .form-select {
    width: 100%;
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-base);
    color: var(--color-fg);
    background-color: var(--color-bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    transition: border-color var(--duration) var(--ease);

    &::placeholder {
      color: var(--color-fg-subtle);
    }

    &:focus {
      outline: 2px solid var(--color-fg);
      outline-offset: 2px;
      border-color: var(--line);
    }
  }

  /* A search field, wherever it appears. Chrome draws its own clear control
     inside a focused one, in its system blue, which is outside the palette
     entirely (DESIGN.md "Color"). Hide it once here rather than per field:
     the field clears the way every other field does. */
  input[type="search"]::-webkit-search-cancel-button {
    appearance: none;
    display: none;
  }

  .form-select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238a8985' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--space-4) center;
    padding-inline-end: var(--space-10);
    cursor: pointer;
  }

  /* Filterable select (searchable combobox), behavior: filter-select. The
     server sends a native select, which the behavior turns into the text
     input; appearance: none lets a select carrying this class (the launch
     form without JavaScript) draw the same chevron as the input. */
  .filter-select {
    position: relative;
  }

  .filter-select__input {
    appearance: none;
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%238a8985' d='M2 4l4 4 4-4'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--space-4) center;
    padding-inline-end: var(--space-10);
  }

  .filter-select__list {
    position: absolute;
    top: 100%;
    inset-inline-start: 0;
    inset-inline-end: 0;
    margin: var(--space-1) 0 0 0;
    padding: var(--space-1);
    background-color: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    list-style: none;
    z-index: 100;
    max-height: 240px;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity var(--duration) var(--ease),
                transform var(--duration) var(--ease),
                visibility var(--duration);
  }

  .filter-select--open .filter-select__list {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
  }

  .filter-select__item {
    margin: 0;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-base);
    color: var(--color-fg);
    border-radius: 4px;
    cursor: pointer;
    transition: background-color var(--duration) var(--ease);

    &:hover,
    &.filter-select__item--hover {
      background-color: var(--paper-2);
    }

    &.filter-select__item--selected {
      font-weight: var(--weight-medium);
    }

    &[hidden] {
      display: none;
    }
  }

  .filter-select__empty {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-fg-subtle);
    font-style: italic;
    cursor: default;
    pointer-events: none;
  }

  /* A block, so no line box: an inline textarea sits on the text baseline
     and leaves a descender's gap under itself, 8px more below a textarea
     than below an input. */
  .form-textarea {
    display: block;
    min-height: 8rem;
    resize: vertical;
  }

  /* A field that holds the site's own text (CSS.md "Text in its own
     direction"): each line runs in the direction of its own first letter,
     the field's dir the fallback for a line with none. The site's language
     is the text's only by assumption, so Arabic written on an English site
     runs right to left with its full stops at the right end, and a code
     block or a Latin name in an Arabic post stays left to right on its own
     line without turning the whole field. */
  .form-site-text {
    unicode-bidi: plaintext;
  }

  .form-hint {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-fg-muted);
    margin-top: var(--space-2);
  }

  .form-error {
    display: block;
    font-size: var(--text-sm);
    color: var(--color-error);
    font-weight: var(--weight-medium);
    margin-top: var(--space-2);
  }

  /* The summary above a form, only when more than one field is wrong
     (CSS.md "Several errors at once"). It takes the width of the form it
     belongs to, never the page. */
  .form-errors {
    margin-bottom: var(--space-6);
    padding: var(--space-4) var(--space-5);
    color: var(--color-error);
    background-color: var(--color-error-bg);
    border: 1px solid var(--color-error-border);
    border-radius: var(--radius-sm);
  }

  .form-errors a {
    color: inherit;
  }

  .form-errors__title {
    margin: 0 0 var(--space-2);
    font-weight: var(--weight-medium);
    color: inherit;
  }

  .form-errors__list ::marker {
    color: var(--color-error);
  }

  .form-errors__list {
    margin: 0;
    padding-inline-start: var(--space-5);
  }

  .form-input--error,
  .form-textarea--error,
  .form-select--error {
    border-color: var(--color-error-border);
  }

  /* The box sits beside the label's first line, not the middle of the
     label, so a label that wraps on a phone reads from its box. The
     offset centres the box on one line of the
     label, from the same tokens the label uses. */
  .form-check {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin-bottom: var(--space-3);
  }

  /* A field group that ends in a checkbox row ends where the row does. */
  .form-group > .form-check:last-child {
    margin-bottom: 0;
  }

  .form-check-input {
    flex-shrink: 0;
    width: 1.125rem;
    height: 1.125rem;
    margin: 0;
    margin-block-start: calc((var(--text-base) * var(--leading-relaxed) - 1.125rem) / 2);
    accent-color: var(--ink);
    cursor: pointer;
  }

  .form-check-label {
    font-size: var(--text-base);
    line-height: var(--leading-relaxed);
    color: var(--color-fg);
    cursor: pointer;
  }

  /* A checkbox with a hint: the hint sits in the label's column, under its
     words. Its margin takes back the label's half-leading, so the hint reads
     the same 8px under the words as a hint under an input. */
  .form-check-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }

  .form-check-text .form-hint {
    margin-top: calc(var(--space-2) - var(--text-base) * (var(--leading-relaxed) - 1) / 2);
  }

  /* Input group (subdomain inputs) */
  .input-group {
    display: flex;
  }

  .input-group .form-input {
    border-inline-end: none;
    border-start-start-radius: var(--radius-sm);
    border-end-start-radius: var(--radius-sm);
    border-start-end-radius: 0;
    border-end-end-radius: 0;
  }

  .input-group__addon {
    display: flex;
    align-items: center;
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-base);
    color: var(--color-fg-muted);
    background-color: var(--paper-2);
    border: 1px solid var(--line);
    border-inline-start: none;
    border-start-start-radius: 0;
    border-end-start-radius: 0;
    border-start-end-radius: var(--radius-sm);
    border-end-end-radius: var(--radius-sm);
  }

  /* A refused field reddens the whole control: the addon shares the border,
     so half of it grey reads as a drawing mistake (DESIGN.md "The error
     rule"). */
  .input-group:has(.form-input--error) .input-group__addon {
    border-color: var(--color-error-border);
  }

  /* One address, one ring: the focus ring goes round the input and the
     addon together (CSS.md "Input Group"). */
  .input-group {
    border-radius: var(--radius-sm);

    &:focus-within {
      outline: 2px solid var(--color-fg);
      outline-offset: 2px;
    }

    & .form-input:focus,
    & .form-input:focus-visible {
      outline: none;
    }
  }

  .form--narrow {
    max-width: 31rem;
  }

  .form--editor {
    max-width: var(--max-width-default);
  }

  /* --------------------------------------------------------------------------
     Cards: hairline + radius, no shadows
     -------------------------------------------------------------------------- */

  .card {
    background-color: var(--color-bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    padding: var(--space-6);
  }

  .card--compact {
    padding: var(--space-4);

    & .owner-row {
      padding: var(--space-2) 0;

      &:first-child {
        padding-top: 0;
      }

      &:last-child {
        padding-bottom: 0;
      }
    }
  }

  /* --------------------------------------------------------------------------
     Alerts & Badges
     -------------------------------------------------------------------------- */

  .alert {
    padding: var(--space-4) var(--space-6);
    background-color: var(--paper-2);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm);
    font-size: var(--text-base);
  }

  .alert--success,
  .alert--warning {
    background-color: var(--paper-2);
    color: var(--color-fg);
  }

  .alert--error,
  .alert--danger {
    background-color: var(--color-error-bg);
    color: var(--color-error);
    border: 1px solid var(--color-error-border);
  }

  /* A pill is one line: wrapped, the pill radius draws an ellipse around
     several lines of text. */
  .badge {
    display: inline-flex;
    align-items: center;
    padding: 0.0625rem 0.625rem;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    color: var(--color-fg-muted);
    background-color: var(--color-bg);
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    white-space: nowrap;
  }

  .badge--success,
  .badge--danger,
  .badge--warning {
    color: var(--color-fg);
    border-color: var(--color-fg);
  }

  .badge--muted {
    color: var(--color-fg-subtle);
    border-color: var(--line-soft);
  }

  .dev-notice {
    overflow-wrap: anywhere;
    background-color: var(--paper-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    color: var(--color-fg);
    padding: var(--space-4);
    text-align: start;

    & p {
      margin: 0 0 var(--space-2) 0;
      color: var(--color-fg);

      &:last-child {
        margin-bottom: 0;
      }
    }

    & a {
      color: var(--color-fg);
      word-break: break-all;
    }
  }

  /* --------------------------------------------------------------------------
     Tables: hairline dividers, sentence-case headers
     -------------------------------------------------------------------------- */

  .table {
    width: 100%;
  }

  .table th,
  .table td {
    padding: var(--space-3) var(--space-4);
    text-align: start;
  }

  .table th {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-fg-muted);
    border-bottom: 1px solid var(--line);
  }

  .table td {
    border-bottom: 1px solid var(--line-soft);
  }

  /* A cell holding only a row's menu gives its padding to the columns
     beside it on a phone, so the menu's full-size icon fits and the date
     keeps its width. */
  @media (max-width: 640px) {
    .table td:has(> .dropdown:only-child) {
      padding-inline: var(--space-1);
    }
  }

  .table tr:last-child td {
    border-bottom: none;
  }

  /* A column a phone folds away (CSS.md "A column a phone folds away"):
     at 640px and below its cells hide, and the row's first cell says the
     same in a muted line of its own, so the date and the row's menu stay
     on the screen instead of past its edge. */
  .table__fold {
    display: none;
  }

  @media (max-width: 640px) {
    .table .table__wide {
      display: none;
    }

    .table__fold {
      display: block;
      margin-top: var(--space-1);
      font-size: var(--text-sm);
      color: var(--color-fg-muted);
    }
  }

  .table tbody tr {
    transition: background-color var(--duration) var(--ease);
  }

  .table tbody tr:hover {
    background-color: var(--paper-2);
  }

  .table tbody a {
    text-decoration: none;
  }

  .table tbody a:hover:not(.dropdown__item) {
    text-decoration: underline;
  }

  @keyframes flash-row {
    from { background-color: var(--paper-2); }
    to { background-color: transparent; }
  }

  .table tbody tr.flash {
    animation: flash-row 1.5s ease-out;
  }

  .data-table {
    width: 100%;
    border-collapse: collapse;

    & th,
    & td {
      padding: var(--space-3) var(--space-4);
      text-align: start;
      border-bottom: 1px solid var(--line-soft);
    }

    /* A row's label (<th scope="row">) reads like a heading but draws its
       row's line: only the head row carries the stronger one. */
    & th {
      font-size: var(--text-sm);
      font-weight: var(--weight-medium);
      color: var(--color-fg-muted);
    }

    & thead th {
      border-bottom: 1px solid var(--line);
    }

    & tbody tr:last-child > * {
      border-bottom: none;
    }
  }

  /* Detail list: responsive key-value metadata grid */
  .detail-list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border-top: 1px solid var(--line-soft);
  }

  .detail-list > div {
    padding: var(--space-4);
    border-bottom: 1px solid var(--line-soft);
  }

  .detail-list > div:nth-last-child(-n+2):not(.detail-full) {
    border-bottom: none;
  }

  .detail-list > div.detail-full:last-child {
    border-bottom: none;
  }

  /* A wide cell last is the last row alone: the cell before it keeps its
     line. */
  .detail-list > div:nth-last-child(2):not(.detail-full):has(+ .detail-full) {
    border-bottom: 1px solid var(--line-soft);
  }

  .detail-list > .detail-full {
    grid-column: 1 / -1;
  }

  .detail-list__label {
    display: block;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    color: var(--color-fg-subtle);
    margin-bottom: var(--space-1);
  }

  @media (max-width: 640px) {
    .detail-list {
      grid-template-columns: 1fr;
    }

    .detail-list > div:nth-last-child(-n+2):not(.detail-full) {
      border-bottom: 1px solid var(--line-soft);
    }

    .detail-list > div:last-child {
      border-bottom: none;
    }
  }

  /* --------------------------------------------------------------------------
     Page Layouts
     -------------------------------------------------------------------------- */

  .auth-page {
    max-width: 25rem;
    margin: var(--space-16) auto;
    padding: 0 var(--space-6);
  }

  .auth-page--wide {
    max-width: 31rem;
  }

  /* A page whose whole content is an auth card centres the header over it,
     so the wordmark sits over the card it belongs above rather than off
     to its start. */
  .container--narrow:has(> .auth-page) > .header {
    justify-content: center;
  }

  /* The wordmark above an auth page's title (backstage sign-in), drawn the
     way shared/login.html draws it. */
  .auth-page__logo {
    display: block;
    width: fit-content;
    margin-inline: auto;
    margin-bottom: var(--space-8);
    font-size: var(--text-lg);
    text-decoration: none;
  }

  .auth-page__title {
    font-size: var(--text-2xl);
    text-align: center;
    margin-bottom: var(--space-8);
  }

  .auth-page__message {
    text-align: center;
    color: var(--color-fg-muted);
    margin-bottom: var(--space-6);
  }

  .auth-page__footer {
    text-align: center;
    margin-top: var(--space-8);
    color: var(--color-fg-muted);
    font-size: var(--text-sm);
  }

  .maintenance-bar {
    background-color: var(--ink);
    color: var(--paper);
    text-align: center;
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-sm);

    & a {
      color: var(--paper);
      font-weight: var(--weight-medium);
    }
  }

  .form-textarea--code {
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    white-space: pre;
    overflow-wrap: normal;
  }

  .form-input--color {
    height: 3rem;
    padding: var(--space-2);
    cursor: pointer;
  }

  .form-input--short {
    width: 140px;
  }

  /* A column: several templates put a sentence, a hint, a list or a button in
     here, and a row would run them together. */
  .empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    flex: 1;
    min-height: 40vh;
    text-align: center;
    color: var(--color-fg-muted);

    /* A list of tips reads from its markers, not centred on them. */
    & ul,
    & ol {
      text-align: start;
    }
  }

  /* --------------------------------------------------------------------------
     Article Content
     -------------------------------------------------------------------------- */

  .article {
    font-size: var(--text-md);
    line-height: var(--leading-relaxed);

    & > * + * {
      margin-top: var(--space-6);
    }

    & h2 {
      font-size: var(--text-xl);
      margin-top: var(--space-12);
      margin-bottom: var(--space-4);
    }

    & h3 {
      font-size: var(--text-lg);
      margin-top: var(--space-8);
      margin-bottom: var(--space-3);
    }

    & p {
      max-width: var(--max-width-prose);
    }

    & a:not(.btn):not(.badge) {
      text-decoration: underline;
    }
  }

  /* --------------------------------------------------------------------------
     Pagination
     -------------------------------------------------------------------------- */

  .pagination {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-2);
    padding: var(--space-8) 0;
  }

  .pagination__item {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    padding: 0 var(--space-3);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-fg-muted);
    background-color: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    text-decoration: none;
    transition: all var(--duration) var(--ease);

    &:hover {
      color: var(--color-fg);
      border-color: var(--line);
      text-decoration: none;
    }

    &[aria-current="page"] {
      color: var(--paper);
      background-color: var(--ink);
      border-color: var(--ink);
    }
  }

  .pagination__ellipsis {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 2.25rem;
    height: 2.25rem;
    font-size: var(--text-sm);
    color: var(--color-fg-subtle);
  }

  /* On a phone every page is a 44px target (PAGINATION.md "CSS Classes");
     the gap closes a little so a full pager still fits a narrow screen,
     and a longer one wraps rather than scrolling the page sideways. */
  @media (max-width: 640px) {
    .pagination {
      gap: var(--space-1);
    }

    .pagination__item,
    .pagination__ellipsis {
      min-width: 2.75rem;
      height: 2.75rem;
    }
  }

  /* --------------------------------------------------------------------------
     Contents: the shared "toc" partial above a long text (CSS.md "Contents")
     -------------------------------------------------------------------------- */

  .toc {
    max-width: var(--max-width-prose);
    margin-block: 0 var(--space-10);
    padding-inline-start: var(--space-4);
    border-inline-start: var(--border);
  }

  .toc__title {
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--color-fg-muted);
    margin-block-end: var(--space-2);
  }

  .toc__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    font-size: var(--text-sm);
  }

  .toc__item {
    line-height: 1.4;

    & a {
      color: var(--color-fg-muted);
      text-decoration: none;

      &:hover {
        color: var(--color-fg);
        text-decoration: underline;
      }
    }
  }

  .toc__item--sub {
    padding-inline-start: var(--space-4);
  }

  /* --------------------------------------------------------------------------
     Comments: the comments module's public partial "comments-section", in
     the text column of whatever page draws it (CSS.md "Comments")
     -------------------------------------------------------------------------- */

  .comments {
    max-width: var(--max-width-narrow);
    margin-block-start: var(--space-12);
    padding-block-start: var(--space-8);
    border-block-start: var(--border);
  }

  .comments__title {
    font-size: var(--text-lg);
    margin-block-end: var(--space-4);
  }

  .comment {
    padding-block: var(--space-4);
    border-block-end: var(--border);
  }

  .comment__meta {
    display: flex;
    gap: var(--space-3);
    align-items: baseline;
    font-size: var(--text-sm);
    margin-block-end: var(--space-1);

    & time {
      color: var(--color-fg-muted);
    }
  }

  .comment__body {
    white-space: pre-line;
    line-height: var(--leading-relaxed);
  }

  /* The banner belongs to the form it is about and keeps its distance from
     the list above. */
  .comments .alert {
    margin-block-start: var(--space-6);
  }

  /* A column of fields a fixed step apart: a group's own margin and the
     textarea's baseline gap stacked into a hole above the button. */
  .comment-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    margin-block-start: var(--space-6);

    & .form-group,
    & .form-actions,
    & .form-hint {
      margin: 0;
    }

    & .form-actions {
      align-items: center;
    }
  }

  /* --------------------------------------------------------------------------
     "Was this helpful?": the feedback module's partial "feedback-form" under
     whatever page draws it (FEEDBACK.md "The form"). One row, the question,
     Yes and No, the quiet bot check after them taking no room until
     Cloudflare asks the visitor something; after an answer the thanks,
     after a No the contact line on a line of its own.
     -------------------------------------------------------------------------- */

  .feedback {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3) var(--space-4);
    margin-block-start: var(--space-12);
    padding-block: var(--space-6);
    border-block: 1px solid var(--line-soft);

    & .cf-turnstile:not([data-appearance="interaction-only"]) {
      flex-basis: 100%;
    }
  }

  .feedback__question {
    margin: 0;
    font-weight: var(--weight-medium);
  }

  .feedback__actions {
    display: flex;
    gap: var(--space-2);
  }

  .feedback__note {
    flex-basis: 100%;
    margin: 0;
    font-size: var(--text-sm);
    color: var(--color-fg-muted);

    & a {
      color: var(--color-fg);
      text-decoration: underline;
      text-underline-offset: 0.2em;
    }
  }

  .feedback__error {
    color: var(--color-error);
    font-weight: var(--weight-medium);
  }

  /* --------------------------------------------------------------------------
     Search: the search module's "search-form" partial, its suggestions and
     the results list, drawn by every app's search page and the help widget's
     frame, which loads this file only (SEARCH.md "Suggestions while typing").
     -------------------------------------------------------------------------- */

  .search-form {
    display: flex;
    gap: var(--space-2);
    align-items: stretch;
    margin-block: var(--space-6) var(--space-8);

    & .form-input {
      flex: 1;
    }

    & .btn {
      flex-shrink: 0;
    }
  }

  .search-form__field {
    position: relative;
    flex: 1;
    min-width: 0;
    display: flex;

    & .form-input {
      padding-inline-start: 2.75rem;
      border-radius: var(--radius-pill);
      background-color: var(--paper-2);
    }
  }

  .search-form__icon {
    position: absolute;
    inset-inline-start: var(--space-4);
    top: 50%;
    translate: 0 -50%;
    display: flex;
    color: var(--color-fg-subtle);
    pointer-events: none;

    & svg {
      width: 1.125rem;
      height: 1.125rem;
    }
  }

  /* At most five suggestions and "See every result", hung under the field
     over what follows, like the tag input's list. */
  .search-suggest {
    position: absolute;
    inset-inline: 0;
    top: calc(100% + var(--space-1));
    z-index: 20;
    list-style: none;
    margin: 0;
    padding: var(--space-1);
    background: var(--paper);
    border: 1px solid var(--line);
    border-radius: var(--radius-md);
    text-align: start;
  }

  .search-suggest__option {
    margin: 0;
    padding: 0;

    & a {
      display: block;
      padding: var(--space-2) var(--space-3);
      border-radius: var(--radius-sm);
      color: var(--color-fg);
      text-decoration: none;
    }

    & a:hover,
    &[aria-selected="true"] a {
      background: var(--paper-2);
    }
  }

  .search-suggest__title {
    display: block;
    font-weight: var(--weight-medium);
  }

  .search-suggest__context {
    display: block;
    font-size: var(--text-xs);
    color: var(--color-fg-muted);
  }

  .search-suggest__all {
    border-block-start: 1px solid var(--line-soft);
    margin-block-start: var(--space-1);
    padding-block-start: var(--space-1);

    & a {
      font-size: var(--text-sm);
      color: var(--color-fg-muted);
    }
  }

  @media (max-width: 640px) {
    .search-suggest__option a {
      min-height: 44px;
    }
  }

  .search-meta {
    margin: 0 0 var(--space-4);
    color: var(--color-fg-muted);
    font-size: var(--text-sm);
  }

  .search-results__item {
    padding-block: var(--space-6);
    border-block-end: var(--border);

    &:last-child {
      border-block-end: none;
    }
  }

  .search-results__context {
    margin: 0 0 var(--space-1);
    font-size: var(--text-xs);
    color: var(--color-fg-muted);
  }

  .search-results__title {
    margin: 0 0 var(--space-2);
    font-size: var(--text-lg);

    & a {
      text-decoration: none;
    }

    & a:hover {
      text-decoration: underline;
      text-underline-offset: 0.2em;
    }
  }

  .search-results__snippet {
    margin: 0 0 var(--space-2);
    color: var(--color-fg-muted);

    & mark {
      background: none;
      color: var(--color-fg);
      font-weight: var(--weight-semibold);
    }
  }

  .search-results__type {
    font-size: var(--text-xs);
    color: var(--color-fg-muted);
  }

  /* A compact list for a frame (the help widget): tighter rows, the title
     at the body's size. */
  .search-results--compact {
    & .search-results__item {
      padding-block: var(--space-4);
    }

    & .search-results__title {
      font-size: var(--text-base);
      margin-block-end: var(--space-1);
    }

    & .search-results__snippet {
      font-size: var(--text-sm);
      margin-block-end: 0;
    }
  }

  /* The subscribe box an app puts under its content, quiet and never a
     popup, and the subscribers module's form in it: the field and the
     button on one line, the bot check on a line of its own under them. */
  .subscribe-inline {
    border: var(--border);
    padding: var(--space-8);
    margin-block-start: var(--space-16);
    text-align: center;

    & h2 {
      font-size: var(--text-xl);
      margin-block-end: var(--space-2);
    }

    & p {
      color: var(--color-fg-muted);
      max-width: 26rem;
      margin-block: 0 var(--space-6);
      margin-inline: auto;
    }
  }

  .subscribe-inline__form {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    max-width: 24rem;
    margin-inline: auto;

    & .form-input {
      flex: 1;
    }

    & .cf-turnstile {
      flex-basis: 100%;
      display: flex;
      justify-content: center;
    }
  }

  /* A subscriber's choice of tags, under the confirmation or on their own
     page, and the unsubscribe that ends their page: sections of their own,
     set apart by a rule. The choice's form keeps the form rhythm (CSS.md
     "Form rhythm"): the line of what they receive, then the tags, then
     Save 40px under them (16px gap and the group's 24px). */
  .subscribe-follow,
  .subscribe-leave {
    border-block-start: var(--border);
    padding-block-start: var(--space-8);
    margin-block-start: var(--space-8);
  }

  .subscribe-follow {
    & h2 {
      font-size: var(--text-xl);
      margin-block-end: var(--space-2);
    }

    & > p {
      margin-block-end: var(--space-6);
    }

    & form {
      display: flex;
      flex-direction: column;
      gap: var(--space-4);
    }

    & .btn {
      align-self: flex-start;
    }
  }

  .subscribe-leave > p {
    margin-block-end: var(--space-4);
  }

  /* The way back, a step under the form or the section above it. */
  .page-narrow > :is(.form, .subscribe-follow, .subscribe-leave) + p {
    margin-block-start: var(--space-8);
  }

  /* The widget is 300px wide: on a phone the box gives up some of its
     inline padding so the widget fits inside it. */
  @media (max-width: 640px) {
    .subscribe-inline {
      padding-inline: var(--space-4);
    }
  }

  /* --------------------------------------------------------------------------
     Misc Components
     -------------------------------------------------------------------------- */

  .skip-link {
    position: absolute;
    top: var(--space-4);
    inset-inline-start: var(--space-4);
    padding: var(--space-3) var(--space-6);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--paper);
    background-color: var(--ink);
    border-radius: var(--radius-pill);
    text-decoration: none;
    transform: translateY(-200%);
    transition: transform var(--duration) var(--ease);
    z-index: 9999;

    &:focus {
      transform: translateY(0);
    }
  }

  /* A body a writer wrote in Markdown, wherever it shows: a post, a page,
     the editor's preview, every app to come (CSS.md "Prose"). */
  .prose > * + * {
    margin-block-start: var(--space-6);
  }

  /* A long link or a long inline code breaks anywhere rather than widen
     the page: on a phone one pasted address would scroll the whole post
     sideways. A code block keeps its lines and scrolls inside itself
     (CSS.md "Prose"). */
  .prose {
    overflow-wrap: anywhere;
  }

  .prose p {
    line-height: var(--leading-relaxed);
    max-width: var(--max-width-prose);
  }

  .prose h2 {
    font-size: var(--text-xl);
    margin-block: var(--space-12) var(--space-4);
  }

  .prose h3 {
    font-size: var(--text-lg);
    margin-block: var(--space-8) var(--space-3);
  }

  .prose > :first-child {
    margin-block-start: 0;
  }

  /* Links wear the site's accent (SETTINGS.md "What the public pages
     wear"); the ink until the owner picks one. */
  .prose a {
    color: var(--color-link);
    text-decoration: underline;

    &:hover {
      text-decoration-thickness: 2px;
    }
  }

  .prose blockquote {
    border-inline-start: 2px solid var(--color-fg);
    padding-inline-start: var(--space-6);
    margin-inline: 0;
    font-style: italic;
  }

  .prose pre {
    background: var(--color-fg);
    color: var(--color-bg);
    padding: var(--space-6);
    overflow-x: auto;
    font-size: var(--text-sm);
    tab-size: 4;
  }

  .prose code {
    font-family: var(--font-mono);
    font-size: 0.9em;
  }

  /* Highlighted code: chroma's short class names (SECURITY.md "Markdown
     content is sanitized"), one quiet hue per role. */
  .prose pre :is(.k, .kc, .kd, .kn, .kp, .kr, .ow) { color: var(--code-keyword); }
  .prose pre :is(.s, .sa, .sb, .sc, .dl, .sd, .s1, .s2, .se, .sh, .si, .sx, .sr, .ss) { color: var(--code-string); }
  .prose pre :is(.c, .ch, .cm, .c1, .cs, .cp, .cpf) { color: var(--code-comment); font-style: italic; }
  .prose pre :is(.m, .mb, .mf, .mh, .mi, .il, .mo) { color: var(--code-number); }
  .prose pre :is(.nf, .fm, .nb, .bp) { color: var(--code-function); }
  .prose pre :is(.kt, .nc, .nn, .nt, .na) { color: var(--code-type); }
  .prose pre .err { color: inherit; background: none; }

  .prose ul,
  .prose ol {
    padding-inline-start: var(--space-6);
  }

  .prose li + li {
    margin-block-start: var(--space-2);
  }

  .prose img,
  .prose video,
  .prose iframe {
    max-width: 100%;
    height: auto;
  }

  .prose iframe {
    aspect-ratio: 16 / 9;
    width: 100%;
    border: 0;
  }

  /* A page of the pages module wears a post's frame (CSS.md "Prose"). */
  .page {
    max-width: var(--max-width-narrow);
    padding-block-start: var(--space-8);
  }

  .page__title {
    font-size: clamp(var(--text-2xl), 5vw, var(--text-3xl));
    line-height: 1.15;
    margin: 0 0 var(--space-8);
    text-wrap: balance;
  }

  .meta-text {
    font-size: var(--text-sm);
    color: var(--color-fg-muted);
  }

  /* A quiet small label (uppercase is retired, DESIGN.md). */
  .caps {
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    color: var(--color-fg-subtle);
  }

  .dim {
    color: var(--color-fg-muted);
  }

  .lead {
    font-size: var(--text-md);
    color: var(--color-fg-muted);
    line-height: 1.6;
  }

  .small {
    font-size: var(--text-sm);
  }
}

/* ==========================================================================
   Utilities
   ========================================================================== */

@layer utilities {
  .visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }

  /* Text */
  .text-center { text-align: center; }
  .text-muted { color: var(--color-fg-muted); }
  .text-danger { color: var(--color-fg); font-weight: var(--weight-medium); }
  .text-xs { font-size: var(--text-xs); }
  .text-sm,
  .text-small { font-size: var(--text-sm); }
  .text-lg { font-size: var(--text-lg); }
  /* A heading's level says where it sits in the page's outline, this says
     how big it is. A section under a page title is an h2 even where it has
     to look like an h3, rather than skipping a level to borrow the size
     (CSS.md "Headings"). */
  .text-h3 {
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    letter-spacing: normal;
  }
  .text-h4 {
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    letter-spacing: normal;
  }
  .text-nowrap { white-space: nowrap; }
  .invisible { visibility: hidden; }
  .text-strong { font-weight: var(--weight-medium); }
  .text-mono { font-family: var(--font-mono); }
  .tracking-wide { letter-spacing: 0.01em; }

  /* Truncation (works in table cells via max-width: 0) */
  .truncate {
    max-width: 0;
    width: 100%;
  }
  .truncate > * {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  /* Font weight */
  .font-medium { font-weight: var(--weight-medium); }
  .font-semibold { font-weight: var(--weight-medium); }
  .fw-normal { font-weight: var(--weight-regular); }

  /* Spacing - margin */
  .mb-0 { margin-bottom: 0; }
  .mb-2 { margin-bottom: var(--space-2); }
  .mb-3 { margin-bottom: var(--space-3); }
  .mb-4 { margin-bottom: var(--space-4); }
  .mb-6 { margin-bottom: var(--space-6); }
  .mb-8 { margin-bottom: var(--space-8); }
  .mt-2 { margin-top: var(--space-2); }
  .mt-4 { margin-top: var(--space-4); }
  .mt-6 { margin-top: var(--space-6); }
  .mt-8 { margin-top: var(--space-8); }

  /* Spacing - padding */
  .py-4 { padding-top: var(--space-4); padding-bottom: var(--space-4); }
  .py-8 { padding-top: var(--space-8); padding-bottom: var(--space-8); }
  .pt-8 { padding-top: var(--space-8); }

  /* Borders */
  .border-t { border-top: 1px solid var(--line-soft); }
  .border-b { border-bottom: 1px solid var(--line-soft); }

  /* Flexbox */
  .flex { display: flex; }
  .flex-1 { flex: 1; }
  .items-center { align-items: center; }
  .justify-between { justify-content: space-between; }
  .gap-2 { gap: var(--space-2); }
  .gap-4 { gap: var(--space-4); }
  .gap-8 { gap: var(--space-8); }

  /* Grid */
  .grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-8);
  }

  .grid-2--divided {
    gap: var(--space-12);

    & > *:first-child {
      padding-inline-end: var(--space-6);
      border-inline-end: 1px solid var(--line-soft);
    }
  }

  @media (max-width: 768px) {
    .grid-2 {
      grid-template-columns: 1fr;
    }

    .grid-2--divided > *:first-child {
      padding-inline-end: 0;
      border-inline-end: none;
    }
  }

  /* Responsive visibility */
  .hide-desktop { display: none; }
  .hide-mobile { display: inline; }

  @media (max-width: 639px) {
    .hide-desktop { display: inline; }
    .hide-mobile { display: none; }
  }
}

/* ==========================================================================
   Print
   ========================================================================== */

@media print {
  *, *::before, *::after {
    background: transparent !important;
    color: #000 !important;
    box-shadow: none !important;
  }

  body {
    font-size: 12pt;
    line-height: 1.5;
  }

  a { text-decoration: underline; }

  a[href]::after {
    content: " (" attr(href) ")";
  }

  h2, h3 {
    page-break-after: avoid;
  }

  img, pre, blockquote {
    page-break-inside: avoid;
  }
}

/* ==========================================================================
   Confirm modal (JS.md "Confirm modal"): our own <dialog> in place of the
   native confirm(). Ink & Paper: hairline border, one radius, paper ground,
   a tinted backdrop, one filled primary (danger when the action is
   destructive). data-behavior is none; behaviors/confirm.js drives it.
   ========================================================================== */

.modal {
  width: min(28rem, calc(100vw - 2rem));
  margin: auto;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper);
  color: var(--color-fg);
  /* Animate open AND close: fade and rise the box, fade the backdrop, and
     let display/overlay transition as discrete so the dialog stays through
     the exit. @starting-style gives the entrance its from-state. The global
     reduced-motion rule cuts every duration to nothing. */
  opacity: 0;
  transform: translateY(-0.5rem);
  transition:
    opacity var(--duration) var(--ease),
    transform var(--duration) var(--ease),
    overlay var(--duration) allow-discrete,
    display var(--duration) allow-discrete;
}

.modal[open] {
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  .modal[open] {
    opacity: 0;
    transform: translateY(-0.5rem);
  }
}

.modal::backdrop {
  background: rgba(20, 20, 19, 0.35);
  opacity: 0;
  transition:
    opacity var(--duration) var(--ease),
    overlay var(--duration) allow-discrete,
    display var(--duration) allow-discrete;
}

.modal[open]::backdrop {
  opacity: 1;
}

@starting-style {
  .modal[open]::backdrop {
    opacity: 0;
  }
}

.modal__panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-5);
  margin: 0;
}

.modal__title {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 600;
}

.modal__message {
  margin: 0;
  color: var(--color-fg-muted);
  line-height: 1.5;
}

.modal__actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

/* ==========================================================================
   Command palette (ADMIN.md "Command palette"): a native dialog on every
   admin page and, for a signed-in user, every public page. Ink & Paper:
   hairlines, the two radii, tonal hover, no shadow; the backdrop is a tint.
   ========================================================================== */

.palette {
  width: min(40rem, calc(100vw - 2rem));
  margin: clamp(4rem, 12vh, 8rem) auto 0;
  padding: 0;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--paper);
  color: var(--color-fg);
}

.palette::backdrop {
  background: rgba(20, 20, 19, 0.35);
}

/* The entrance: a short fade and rise, like the field it replaced. The
   global reduced-motion rule cuts both to nothing. */
.palette[open] {
  animation: palette-in var(--duration) var(--ease);
}

.palette[open]::backdrop {
  animation: palette-backdrop-in var(--duration) var(--ease);
}

@keyframes palette-in {
  from {
    opacity: 0;
    transform: translateY(-0.5rem);
  }
}

@keyframes palette-backdrop-in {
  from {
    opacity: 0;
  }
}

.palette__input {
  width: 100%;
  padding: var(--space-4) var(--space-5);
  font: inherit;
  font-size: var(--text-md);
  color: inherit;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line-soft);
  outline: none;
}

.palette__list {
  list-style: none;
  margin: 0;
  padding: var(--space-2);
  max-height: 50vh;
  overflow-y: auto;
}

.palette__group {
  padding: var(--space-3) var(--space-3) var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-fg-subtle);
}

.palette__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.palette__item[aria-selected="true"],
.palette__item:hover {
  background: var(--paper-2);
}

.palette__hint {
  margin-inline-start: auto;
  font-size: var(--text-xs);
  color: var(--color-fg-subtle);
  white-space: nowrap;
}

.palette__empty {
  padding: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-fg-muted);
}

.palette__footer {
  display: flex;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--line-soft);
  font-size: var(--text-xs);
  color: var(--color-fg-subtle);
}

kbd {
  font-family: var(--font-mono);
  font-size: 0.8em;
  padding: 0.1em 0.4em;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
}

/* Small shared controls: an inline form, compact selects, the preferences
   grid, a fieldset with a real legend, a narrow public page, the small
   badges, the progress bar. */
@layer components {
  .form--inline {
    display: inline-flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .form-select--sm,
  .form-select--small {
    width: auto;
    min-width: 7.5rem;
    padding-block: var(--space-2);
    padding-inline: var(--space-3) var(--space-8); /* room for the arrow on the inline-end side */
    font-size: var(--text-sm);
  }

  .form-input--color {
    width: 3.5rem;
    height: 2.5rem;
    padding: var(--space-1);
    cursor: pointer;
  }

  .form-fieldset {
    border: var(--border);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-6) var(--space-2);
    margin: 0 0 var(--space-6);
  }

  .form-fieldset > legend {
    padding: 0 var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
  }

  .form-group--inline {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto;
    align-items: center;
    gap: var(--space-3);
  }

  .form-group--inline .form-label {
    margin: 0;
  }

  .form-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-top: var(--space-6);
  }

  .button-group {
    display: inline-flex;
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .badge--sm {
    padding: 0 var(--space-2);
    font-size: 0.6875rem;
  }

  .badge--subtle {
    border-color: transparent;
    background-color: var(--paper-2);
  }

  .code-block {
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-mono);
    font-size: var(--text-sm);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    background-color: var(--paper-2);
    border: var(--border);
    border-radius: var(--radius-sm);
  }

  .table--compact td,
  .table--compact th {
    padding-top: var(--space-2);
    padding-bottom: var(--space-2);
  }

  .page-narrow {
    max-width: 36rem;
    margin-inline: auto;
    padding-block: var(--space-10);
  }

  .page-narrow > h1 + p {
    margin-bottom: var(--space-6);
    color: var(--color-fg-muted);
  }

  @media (max-width: 640px) {
    .form-group--inline {
      grid-template-columns: 1fr;
    }
  }

  /* A thin progress bar: a file's upload, a form's steps, a share in a
     summary. The bar's width is the share. */
  .progress {
    width: 100%;
    height: 4px;
    background: var(--color-bg-hover);
    flex-basis: 100%;
  }

  .progress__bar {
    height: 100%;
    width: 0;
    background: var(--color-fg);
    transition: width 150ms ease;
  }
}

/* Phone: the platform header (account, backstage) wraps its nav under the
   wordmark instead of colliding with it. */
@layer components {
  @media (max-width: 640px) {
    .header {
      flex-wrap: wrap;
      row-gap: var(--space-3);
    }

    .header__nav {
      flex-wrap: wrap;
      row-gap: var(--space-2);
    }

    /* The account menu stays on the wordmark's row, at its far end, and
       the links take the row below. */
    .header--menu > .header__nav {
      order: 3;
      flex-basis: 100%;
    }
  }
}

/* A header with a menu beside its nav (backstage): wordmark, links pushed
   to the far end, menu last. */
@layer components {
  .header--menu {
    column-gap: var(--space-6);
  }

  .header--menu > .header__nav {
    margin-inline-start: auto;
  }
}

/* Phone: a wide table scrolls inside itself; the page never does. A short
   one still spans the width: as a block, the table's rows sit in a box the
   browser sizes to their content, and an empty caption as wide as the
   table (100cqi, the table its own container) makes that box at least the
   width, the spare room shared among the columns as a desktop table shares
   it. */
@layer components {
  @media (max-width: 640px) {
    .table,
    .data-table {
      display: block;
      max-width: 100%;
      overflow-x: auto;
      container-type: inline-size;
      /* A soft edge where more columns wait: the two paper gradients scroll
         with the content and hide the shadows at either end. */
      background:
        linear-gradient(to right, var(--color-bg) 30%, transparent) left / 2rem 100% no-repeat local,
        linear-gradient(to left, var(--color-bg) 30%, transparent) right / 2rem 100% no-repeat local,
        radial-gradient(farthest-side at 0 50%, rgba(20, 20, 19, 0.14), transparent) left / 0.75rem 100% no-repeat scroll,
        radial-gradient(farthest-side at 100% 50%, rgba(20, 20, 19, 0.14), transparent) right / 0.75rem 100% no-repeat scroll;
    }

    .table::before,
    .data-table::before {
      content: "";
      display: table-caption;
      inline-size: 100cqi;
    }
  }
}

/* The fields' own types (FIELDS.md "The types"; CSS.md "Fields"). */
@layer components {
  /* A Linear scale: its numbers as one row of equal boxes at every width,
     sharing the row (never wrapping, so the last number stays over its
     word), each at least 44px tall; the radio inside each out of sight but
     focusable, the words at each end under the first and the last box. */
  .scale__options {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: var(--space-2);
  }

  .scale__option {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 2.75rem;
    border: var(--border);
    border-radius: var(--radius-sm);
    min-width: 0;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    transition: border-color var(--duration) var(--ease);

    &:hover {
      border-color: var(--color-fg-muted);
    }

    &:has(:checked) {
      background: var(--color-fg);
      border-color: var(--color-fg);
      color: var(--color-bg);
    }

    &:has(:focus-visible) {
      outline: 2px solid var(--color-fg);
      outline-offset: 3px;
    }
  }

  .scale__input,
  .rating__input {
    position: absolute;
    inset: 0;
    opacity: 0;
    margin: 0;
    cursor: pointer;
  }

  .scale__ends {
    display: flex;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-fg-muted);

    > :last-child {
      text-align: end;
    }
  }

  /* On a phone eleven boxes share about 290px: the gap narrows so each
     number keeps room. */
  @media (max-width: 640px) {
    .scale__options {
      gap: var(--space-1);
    }
  }

  .scale__clear,
  .rating__clear {
    margin-top: var(--space-2);
  }

  /* A Rating: stars in a row, each a 44px target; the chosen star and every
     one before it filled. */
  .rating__options {
    display: flex;
    flex-wrap: wrap;
  }

  .rating__option {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    color: var(--color-fg);
    cursor: pointer;

    &:has(:focus-visible) {
      outline: 2px solid var(--color-fg);
      outline-offset: -2px;
      border-radius: var(--radius-sm);
    }
  }

  .rating__star svg {
    display: block;
    fill: none;
    transition: fill var(--duration) var(--ease);
  }

  .rating__option:has(:checked) .rating__star svg,
  .rating__option:has(~ .rating__option :checked) .rating__star svg {
    fill: currentColor;
  }

  /* A File upload: the files a refused send kept above the input. */
  .file-field__kept {
    margin-bottom: var(--space-2);
  }

  .file-field__kept .form-check:last-child {
    margin-bottom: 0;
  }

  .file-field__input {
    padding-block: var(--space-2);
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--color-fg-muted);
  }

  /* The browser's own button, drawn as the site's small secondary button
     in the site's type, not the system's. */
  .file-field__input::file-selector-button {
    margin-inline-end: var(--space-3);
    padding: var(--space-1) var(--space-4);
    font: inherit;
    font-weight: var(--weight-medium);
    color: var(--color-fg);
    background: transparent;
    border: 1px solid var(--line);
    border-radius: var(--radius-pill);
    cursor: pointer;
  }

  .file-field__input:hover::file-selector-button {
    border-color: var(--color-fg);
  }

  /* A Signature: the typed name first, then the pad, paper in either
     theme with a hairline edge; the Clear button beside the hint. */
  .signature__label {
    display: block;
    margin-bottom: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-fg-muted);
  }

  .signature__pad {
    margin-top: var(--space-4);
  }

  .signature__canvas {
    display: block;
    width: 100%;
    height: 10rem;
    border: var(--border);
    border-radius: var(--radius-sm);
    background: var(--signature-paper);
    color: var(--signature-ink);
    touch-action: none;
    cursor: crosshair;
  }

  .signature__actions {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-2);

    & .form-hint {
      margin-top: 0;
    }
  }

  /* A text block between the questions: a Heading, or a Paragraph. */
  .text-block__heading {
    margin: 0 0 var(--space-2);
    font-size: var(--text-lg);
  }

  /* A heading's line runs the column's width like every hint: the pretty
     wrap of a paragraph pulled a two-line hint in short. */
  .text-block__hint {
    margin: 0;
    text-wrap: wrap;
  }

  .text-block__body > :last-child {
    margin-bottom: 0;
  }

  /* An answer as a member reads it. */
  .answer-files {
    margin: 0;
    padding: 0;
    list-style: none;
  }

  .answer-files__item + .answer-files__item {
    margin-top: var(--space-2);
  }

  .answer-files__link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    overflow-wrap: anywhere;

    & svg {
      flex-shrink: 0;
    }
  }

  .answer-text {
    white-space: pre-line;
    overflow-wrap: anywhere;
  }

  /* A Date or a Time answer is what a person wrote, so it reads in the
     page's type like the answers beside it, not in the mono of a date the
     site stamps (DESIGN.md "Roles and weights"). */
  .answer-time {
    font-family: inherit;
  }

  .answer-signature {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
  }

  .answer-signature__drawing {
    max-width: min(100%, 24rem);
    height: auto;
    border: var(--border);
    border-radius: var(--radius-sm);
    background: var(--signature-paper);
  }

  .answer-rating {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
  }

  .answer-rating__stars {
    display: inline-flex;
  }

  .answer-rating__star--filled svg {
    fill: currentColor;
  }

  @media (prefers-reduced-motion: reduce) {
    .scale__option,
    .rating__star svg {
      transition: none;
    }
  }
}

/* Chinese, Japanese and Korean read in the system's own fonts: a shipped
   CJK face is 2.4 to 18 MB and the systems' are excellent. Plex keeps the
   Latin letters inside the text (DESIGN.md "Typography"). */
:root:lang(ja) {
  --font-sans: "IBM Plex Sans", "IBM Plex Sans Arabic", "IBM Plex Sans Devanagari", "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Yu Gothic UI", "Yu Gothic", Meiryo, "Noto Sans JP", sans-serif;
}

:root:lang(ko) {
  --font-sans: "IBM Plex Sans", "IBM Plex Sans Arabic", "IBM Plex Sans Devanagari", "Apple SD Gothic Neo", "Malgun Gothic", "Noto Sans KR", sans-serif;
}

:root:lang(zh) {
  --font-sans: "IBM Plex Sans", "IBM Plex Sans Arabic", "IBM Plex Sans Devanagari", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans SC", sans-serif;
}

/* Right to left (I18N.md "Right to left"). Logical
   properties mirror the layout; these are what they cannot express. */
[dir="rtl"] .form-select,
[dir="rtl"] .filter-select__input {
  background-position: left var(--space-4) center;
}

/* An address, a phone number, a key or a code sample reads left to right
   inside a right-to-left page. */
[dir="rtl"] input[type="email"],
[dir="rtl"] input[type="tel"],
[dir="rtl"] input[type="url"] {
  direction: ltr;
  text-align: end;
}

[dir="rtl"] code,
[dir="rtl"] kbd,
[dir="rtl"] .text-mono {
  direction: ltr;
  unicode-bidi: isolate;
}

/* The switcher's chevron: its inline border swaps sides, so the turn
   swaps too, or it points sideways. */
[dir="rtl"] .lang-switcher .lang-switcher__button::after {
  transform: rotate(-45deg) translateY(-0.15em);
}
