/*
 * MailSieve design system.
 *
 * One hand-written stylesheet, no framework and no build step: the product
 * installs by unzipping it onto a server, and a CSS toolchain would mean
 * shipping either node_modules or a compiled file nobody can regenerate.
 *
 * Written against what browsers ship in 2026 rather than around it:
 *
 *  - light-dark() defines each token once instead of duplicating the whole
 *    palette per theme. The previous version had three copies of every colour,
 *    which is exactly how a dark-mode token drifts out of sync unnoticed.
 *  - Cascade layers give components a defined rank, so a utility cannot
 *    accidentally outrank a component by being written later or more specific.
 *  - Container queries let a card respond to its column instead of the
 *    viewport, which is what actually determines how much room it has.
 *  - :has() replaces several JS class toggles.
 *
 * Accessibility target is WCAG 2.2 AA. The additions over 2.1 that affect this
 * file: 2.5.8 target size (24x24 minimum), 2.4.11 focus not obscured, and
 * 2.4.13 focus appearance.
 */

@layer reset, tokens, base, layout, components, utilities;

/* ---------------------------------------------------------------- tokens - */

@layer tokens {
  :root {
    color-scheme: light dark;

    --accent:      light-dark(#2f6df6, #6d9bff);
    --accent-ink:  light-dark(#ffffff, #0b1020);
    --accent-soft: light-dark(#e8efff, #1a2439);

    --bg:         light-dark(#f6f7fb, #0d1017);
    --surface:    light-dark(#ffffff, #151a24);
    --surface-2:  light-dark(#f1f3f9, #1b2130);
    --border:     light-dark(#dfe3ed, #262d3d);
    --border-strong: light-dark(#c3c9d9, #38415a);

    --ink:   light-dark(#1c2233, #e8ecf5);
    --ink-2: light-dark(#4a5268, #b2bacd);
    --ink-3: light-dark(#79819a, #828da6);

    --green:    light-dark(#16794a, #6ee7a8);
    --green-bg: light-dark(#e3f6ec, #12301f);
    --red:      light-dark(#b3261e, #ff9b93);
    --red-bg:   light-dark(#fdeceb, #3a1613);
    --amber:    light-dark(#8a5a00, #f5c065);
    --amber-bg: light-dark(#fdf1db, #34260c);
    --slate:    light-dark(#4a5268, #aab3c7);
    --slate-bg: light-dark(#eceef4, #222a3a);
    --blue:     light-dark(#1c4fd8, #93b4ff);
    --blue-bg:  light-dark(#e6edfe, #17233f);

    --radius:    10px;
    --radius-sm: 6px;
    --shadow:    0 1px 2px light-dark(rgb(16 24 40 / .06), rgb(0 0 0 / .4)),
                 0 1px 3px light-dark(rgb(16 24 40 / .1), rgb(0 0 0 / .3));
    --shadow-lg: 0 8px 24px light-dark(rgb(16 24 40 / .12), rgb(0 0 0 / .5));

    --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* WCAG 2.2 SC 2.5.8: interactive targets are at least 24x24. */
    --tap: 24px;
  }

  /* Explicit override wins over the system preference. */
  :root[data-theme="light"] { color-scheme: light; }
  :root[data-theme="dark"]  { color-scheme: dark; }
}

/* ----------------------------------------------------------------- reset - */

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

  html { -webkit-text-size-adjust: 100%; }

  body, h1, h2, h3, p, figure, blockquote, dl, dd { margin: 0; }

  /* Focus is restyled, never removed. Losing it makes the app unusable by
     keyboard and fails SC 2.4.7 outright. */
  :focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
  }

  /* SC 2.4.11: a focused control must not be hidden behind a sticky header. */
  :target, :focus-visible { scroll-margin-block: 5rem; }

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

/* ------------------------------------------------------------------ base - */

@layer base {
  body {
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.55;
    color: var(--ink);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
  }

  a { color: var(--accent); text-decoration: none; }
  a:hover { text-decoration: underline; }

  h1, h2, h3 {
    line-height: 1.25;
    font-weight: 650;
    letter-spacing: -.01em;
    /* Stops a two-line heading leaving one orphaned word. */
    text-wrap: balance;
  }

  h1 { font-size: 1.6rem; margin-block-end: .5rem; }
  h2 { font-size: 1.2rem; margin-block-end: .5rem; }
  h3 { font-size: 1rem; margin-block-end: .5rem; }

  p { margin-block-end: 1rem; text-wrap: pretty; }

  code, pre, .mono { font-family: var(--mono); font-size: .875em; }

  img, svg { max-width: 100%; height: auto; }

  /* Hidden from sighted users, still announced. */
  .visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .skip-link {
    position: absolute;
    inset-inline-start: -9999px;
    inset-block-start: 0;
    background: var(--accent);
    color: var(--accent-ink);
    padding: .75rem 1rem;
    z-index: 100;
  }
  .skip-link:focus { inset-inline-start: 0; }
}

/* ---------------------------------------------------------------- layout - */

@layer layout {
  .shell { display: flex; min-height: 100vh; min-height: 100dvh; }

  .sidebar {
    inline-size: 232px;
    flex: 0 0 232px;
    background: var(--surface);
    border-inline-end: 1px solid var(--border);
    padding-block: 1.25rem;
    display: flex;
    flex-direction: column;
  }

  .brand {
    display: flex;
    align-items: center;
    gap: .625rem;
    padding: 0 1.25rem 1.25rem;
    font-weight: 700;
    font-size: 1.05rem;
    color: var(--ink);
  }
  .brand:hover { text-decoration: none; }

  .brand-mark {
    inline-size: 28px; block-size: 28px;
    border-radius: 8px;
    background: var(--accent);
    color: var(--accent-ink);
    display: grid; place-items: center;
    font-weight: 800; font-size: .85rem;
    flex: 0 0 28px;
  }

  .nav { display: flex; flex-direction: column; gap: 1px; padding-inline: .625rem; }

  .nav a {
    display: flex; align-items: center; gap: .625rem;
    padding: .5rem .75rem;
    min-block-size: var(--tap);
    border-radius: var(--radius-sm);
    color: var(--ink-2);
    font-weight: 500;
  }
  .nav a:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
  .nav a[aria-current="page"] { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

  .nav-heading {
    padding: 1rem .75rem .375rem;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--ink-3);
    font-weight: 600;
  }

  .sidebar-foot { margin-block-start: auto; padding: 1rem 1.25rem 0; border-block-start: 1px solid var(--border); }

  .main { flex: 1; min-inline-size: 0; display: flex; flex-direction: column; }

  .topbar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem;
    padding: 1rem 1.5rem;
    border-block-end: 1px solid var(--border);
    background: var(--surface);
    position: sticky;
    inset-block-start: 0;
    z-index: 10;
  }

  .content { padding: 1.5rem; flex: 1; container-type: inline-size; }
  .content > * + * { margin-block-start: 1.25rem; }

  .page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
  .page-head p { color: var(--ink-2); margin: 0; }

  .grid { display: grid; gap: 1rem; }
  .grid-stats { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
  .grid-2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
  .grid-3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

  /* Container query rather than a media query: a card in a narrow column
     should stack even when the window is wide. */
  @container (max-width: 40rem) {
    .page-head { flex-direction: column; align-items: stretch; }
  }
}

/* ------------------------------------------------------------ components - */

@layer components {
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    container-type: inline-size;
  }
  .card-head {
    padding: .875rem 1.125rem;
    border-block-end: 1px solid var(--border);
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    flex-wrap: wrap;
  }
  .card-head :is(h2, h3) { margin: 0; }
  .card-body { padding: 1.125rem; }
  .card-body > * + * { margin-block-start: 1rem; }

  .stat { padding: 1.125rem; }
  .stat-label { font-size: .75rem; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-3); font-weight: 600; }
  .stat-value { font-size: 1.85rem; font-weight: 680; letter-spacing: -.02em; margin-block-start: .25rem; font-variant-numeric: tabular-nums; }
  .stat-note { font-size: .8rem; color: var(--ink-3); margin-block-start: .125rem; }

  .stat-deliverable   .stat-value { color: var(--green); }
  .stat-undeliverable .stat-value { color: var(--red); }
  .stat-risky         .stat-value { color: var(--amber); }
  .stat-unknown       .stat-value { color: var(--slate); }

  a.stat { color: inherit; text-decoration: none; display: block; }
  a.stat:hover { border-color: var(--border-strong); text-decoration: none; }

  .badge {
    display: inline-flex; align-items: center; gap: .3rem;
    padding: .15rem .5rem;
    min-block-size: 20px;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 600;
    white-space: nowrap;
  }
  .badge::before { content: ""; inline-size: 6px; block-size: 6px; border-radius: 50%; background: currentColor; }

  .badge-green { background: var(--green-bg); color: var(--green); }
  .badge-red   { background: var(--red-bg);   color: var(--red); }
  .badge-amber { background: var(--amber-bg); color: var(--amber); }
  .badge-slate { background: var(--slate-bg); color: var(--slate); }
  .badge-blue  { background: var(--blue-bg);  color: var(--blue); }

  .table-wrap { overflow-x: auto; }

  table { inline-size: 100%; border-collapse: collapse; }
  th, td { text-align: start; padding: .625rem .75rem; border-block-end: 1px solid var(--border); }
  th {
    font-size: .72rem; text-transform: uppercase; letter-spacing: .05em;
    color: var(--ink-3); font-weight: 600; white-space: nowrap;
  }
  tbody tr:last-child td { border-block-end: 0; }
  tbody tr:hover { background: var(--surface-2); }
  :is(td, th).num { text-align: end; font-variant-numeric: tabular-nums; }

  label { display: block; font-weight: 600; font-size: .85rem; margin-block-end: .3rem; }

  :is(input:not([type="checkbox"], [type="radio"]), select, textarea) {
    inline-size: 100%;
    padding: .55rem .7rem;
    min-block-size: var(--tap);
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
  }
  :is(input, select, textarea):focus { border-color: var(--accent); }

  /* Grows with its content instead of needing a JS autosize handler. */
  textarea { field-sizing: content; min-block-size: 6lh; }

  :is(input, select, textarea)[aria-invalid="true"] { border-color: var(--red); }

  .field { margin-block-end: 1rem; }
  .field-hint { font-size: .8rem; color: var(--ink-3); margin-block-start: .3rem; }
  .field-error { font-size: .8rem; color: var(--red); margin-block-start: .3rem; font-weight: 500; }

  /* A field whose input is invalid tints its label, with no JS. */
  .field:has([aria-invalid="true"]) label { color: var(--red); }

  .btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .4rem;
    padding: .55rem .9rem;
    min-block-size: var(--tap); min-inline-size: var(--tap);
    font: inherit; font-weight: 600;
    border: 1px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
    background: var(--accent);
    color: var(--accent-ink);
    text-decoration: none;
  }
  .btn:hover { filter: brightness(1.06); text-decoration: none; }
  .btn:disabled, .btn[aria-disabled="true"] { opacity: .55; cursor: not-allowed; filter: none; }
  .btn-secondary { background: var(--surface); color: var(--ink); border-color: var(--border-strong); }
  .btn-secondary:hover { background: var(--surface-2); filter: none; }
  .btn-ghost { background: transparent; color: var(--ink-2); }
  .btn-ghost:hover { background: var(--surface-2); filter: none; }
  .btn-danger { background: var(--red); color: light-dark(#fff, #2a0f0d); }
  .btn-block { inline-size: 100%; }
  .btn-sm { padding: .3rem .6rem; font-size: .85rem; }

  .alert {
    padding: .75rem 1rem;
    border-radius: var(--radius-sm);
    border: 1px solid currentColor;
    font-size: .9rem;
  }
  .alert-error   { background: var(--red-bg);   color: var(--red); }
  .alert-info    { background: var(--blue-bg);  color: var(--blue); }
  .alert-warn    { background: var(--amber-bg); color: var(--amber); }
  .alert-success { background: var(--green-bg); color: var(--green); }

  .progress {
    block-size: 6px;
    background: var(--surface-2);
    border-radius: 999px;
    overflow: hidden;
  }
  .progress-bar { block-size: 100%; background: var(--accent); border-radius: 999px; transition: inline-size .4s ease; }

  /* Native dialog: the browser handles the backdrop, focus trap, inert
     background and Escape. A hand-rolled modal gets at least one of those
     wrong, and it is usually the focus trap. */
  dialog {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    padding: 0;
    max-inline-size: min(34rem, 92vw);
    box-shadow: var(--shadow-lg);
  }
  dialog::backdrop { background: rgb(0 0 0 / .45); }
  dialog .card-body { padding: 1.25rem; }

  .auth {
    min-block-size: 100dvh;
    display: grid;
    place-items: center;
    padding: 2rem 1rem;
  }
  .auth-card { inline-size: 100%; max-inline-size: 25rem; }
  .auth-head { text-align: center; margin-block-end: 1.5rem; }
  .auth-head .brand { justify-content: center; padding: 0 0 1rem; }

  .public {
    min-block-size: 100dvh;
    display: grid;
    place-items: center;
    padding: 2rem 1rem;
    text-align: center;
  }
  .public-card { inline-size: 100%; max-inline-size: 30rem; }

  footer.app-foot {
    padding: 1rem 1.5rem;
    border-block-start: 1px solid var(--border);
    color: var(--ink-3);
    font-size: .8rem;
  }

  .score {
    display: inline-flex; align-items: baseline; gap: .25rem;
    font-variant-numeric: tabular-nums;
  }
  .score-excellent { color: var(--green); }
  .score-good      { color: var(--green); }
  .score-fair      { color: var(--amber); }
  .score-poor      { color: var(--red); }
  .score-none      { color: var(--ink-3); }

  .checklist { list-style: none; padding: 0; margin: 0; display: grid; gap: .75rem; }
  .checklist li { display: grid; grid-template-columns: auto 1fr; gap: .625rem; align-items: start; }
  .checklist .mark { font-weight: 700; font-size: .9rem; line-height: 1.5; }
  .checklist .pass .mark { color: var(--green); }
  .checklist .warn .mark { color: var(--amber); }
  .checklist .fail .mark { color: var(--red); }
  .checklist .info .mark { color: var(--ink-3); }

  /* Definition-style key/value block, used wherever a record or a set of
     properties is displayed rather than a list of rows. */
  .kv { display: grid; grid-template-columns: minmax(8rem, max-content) 1fr; gap: .4rem 1rem; margin: 0; }
  .kv dt { color: var(--ink-3); font-size: .85rem; }
  .kv dd { margin: 0; font-variant-numeric: tabular-nums; }
  @container (max-width: 28rem) {
    .kv { grid-template-columns: 1fr; gap: .1rem; }
    .kv dd { margin-block-end: .5rem; }
  }

  /* A DNS record or an API key: long, unbreakable, and it must be copyable
     verbatim, so it wraps on any character rather than overflowing. */
  .code-block {
    display: block;
    padding: .75rem .875rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .8rem;
    line-height: 1.55;
    overflow-wrap: anywhere;
    white-space: pre-wrap;
    user-select: all;
  }

  /* Horizontal bars for a report. A chart library would be ~70 KB and a build
     step for something a div with a width can express exactly. */
  .bars { display: grid; gap: .5rem; margin: 0; }
  .bar-row { display: grid; grid-template-columns: minmax(6rem, 12rem) 1fr auto; gap: .75rem; align-items: center; }
  /* inline-size: 100% is load-bearing. Without it the track is an auto-sized
     grid item, the fill's percentage width has nothing definite to resolve
     against, and a bar for the value 0 renders full width - which reads as
     "all of them" rather than "none". */
  .bar-track {
    inline-size: 100%;
    block-size: 8px;
    background: var(--surface-2);
    border-radius: 999px;
    overflow: hidden;
  }
  .bar-fill { block-size: 100%; background: var(--accent); border-radius: 999px; }
  .bar-fill.is-green { background: var(--green); }
  .bar-fill.is-red   { background: var(--red); }
  .bar-value { font-variant-numeric: tabular-nums; font-size: .85rem; color: var(--ink-2); }
  @container (max-width: 34rem) {
    .bar-row { grid-template-columns: 1fr auto; }
    .bar-track { grid-column: 1 / -1; }
  }

  /* A day-by-day column chart. Heights are set inline because they are data,
     not style - there is no fixed set of values to enumerate in a stylesheet.
     They are set in px rather than %, because a percentage on a flex item
     resolves against a height the flex layout has not settled yet, and every
     column collapses to its min-block-size. */
  .spark {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    block-size: var(--spark-height, 90px);
  }
  .spark > div {
    flex: 1 1 0;
    min-inline-size: 2px;
    /* Capped, so a chart with two days' data is two columns rather than one
       enormous slab. Without it the flex basis spreads a single bar across the
       whole card and it stops reading as a chart at all. */
    max-inline-size: 2.5rem;
    background: var(--accent);
    border-radius: 2px 2px 0 0;
    min-block-size: 2px;
  }

  .tag {
    display: inline-block;
    padding: .1rem .4rem;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    border: 1px solid var(--border);
    font-size: .75rem;
  }


  /* A segment rule: field, comparison, value, remove. */
  .rule { display: flex; gap: .5rem; align-items: start; flex-wrap: wrap; }
  .rule .field { margin-block-end: 0; }
  @container (max-width: 40rem) {
    .rule { flex-direction: column; align-items: stretch; }
  }

  /* A tag with its colour as a dot, so the text keeps full contrast. A
     coloured background would need a per-colour foreground to stay readable
     in both themes, and getting one of them wrong is invisible until someone
     switches. */
  .tag-dot::before {
    content: "";
    display: inline-block;
    inline-size: 8px; block-size: 8px;
    border-radius: 50%;
    margin-inline-end: .35rem;
    vertical-align: baseline;
    background: var(--ink-3);
  }

  .section + .section { margin-block-start: 1.25rem; }
  /* Anchored sections (/settings#api) must not land under the sticky topbar. */
  .section { scroll-margin-block-start: 5rem; }
}

/* ------------------------------------------------------------- utilities - */

@layer utilities {
  .muted { color: var(--ink-3); }
  .small { font-size: .85rem; }
  .empty { text-align: center; padding: 2.5rem 1rem; color: var(--ink-3); }
  .row { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
  .spread { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
  .stack > * + * { margin-block-start: .75rem; }
  .truncate { max-inline-size: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .nowrap { white-space: nowrap; }
  .hr { border: 0; border-block-start: 1px solid var(--border); margin-block: 1rem; }

  /*
   * These exist because the Content-Security-Policy forbids inline style
   * attributes - style-src 'self' blocks style="" outright, and a nonce cannot
   * apply to an attribute. Every layout hint that would have been written
   * inline lives here instead, which is where it belonged anyway.
   */
  .self-end   { align-self: end; }
  .gap-top    { margin-block-start: 1rem; }
  .gap-top-sm { margin-block-start: .5rem; }
  .normal     { font-weight: 400; }
  .centred    { text-align: center; }
  .start      { text-align: start; }
  .danger     { color: var(--red); }
  .h-lead     { font-size: 1.25rem; }
  .bare       { border: 0; padding: 0; }
  .grow       { flex: 1 1 16rem; margin: 0; }
  .grow-sm    { flex: 1 1 12rem; margin: 0; }
  .fixed-sm   { flex: 0 1 12rem; margin: 0; }
  .plain-link { text-decoration: none; color: inherit; }
  .prose      { max-inline-size: 44rem; inline-size: 100%; }
  .prose-sm   { max-inline-size: 36rem; inline-size: 100%; }
  .wrap-pre   { white-space: pre-wrap; overflow-x: auto; }
}

/* --------------------------------------------------------------- narrow - */

@media (max-width: 860px) {
  @layer layout {
    .shell { flex-direction: column; }
    .sidebar {
      inline-size: 100%; flex: none;
      border-inline-end: 0; border-block-end: 1px solid var(--border);
      padding-block: .75rem;
    }
    .nav { flex-direction: row; overflow-x: auto; padding-inline: .75rem; }
    .nav a { white-space: nowrap; }
    .nav-heading, .sidebar-foot { display: none; }
    .content { padding: 1rem; }
    .topbar { padding: .75rem 1rem; position: static; }
  }
}

/* Navigation transitions, only where the platform supports them and the
   person has not asked for less motion. */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}

@media print {
  .sidebar, .topbar, .btn, footer.app-foot { display: none !important; }
  .card { box-shadow: none; border-color: #999; break-inside: avoid; }
}
