/* Deliberately small and hand-written: the build is `go build`, so a CSS
   toolchain would be the one thing dragging a Node install back in. */

/* --- palette -------------------------------------------------------------- */

/* Every colour in this file comes from a variable, and no rule names a colour
   of its own. That is not tidiness: the header used to be `background:
   var(--fg)`, which is fine while --fg is near-black and turns the bar white on
   white the moment a dark palette redefines it. A role that is a *background*
   somewhere needs its own name, or the second palette silently breaks it. */

:root {
  color-scheme: light;

  --fg: #1a1a1a;
  --muted: #666;
  --bg: #fff;
  --bg-alt: #f6f6f6;
  --field-bg: #fff;
  --border: #d8d8d8;
  --border-soft: #eee;
  --accent: #2b5797;
  --accent-bg: #2b5797;
  --accent-fg: #fff;

  --header-bg: #1a1a1a;
  --header-fg: #fff;
  --header-nav: #ddd;
  --header-muted: #bbb;
  --header-hover: #2e2e2e;

  --pill-ok: #2e7d32;
  --pill-off: #9e9e9e;
  --pill-warn: #b3261e;
  --pill-fg: #fff;

  --danger: #b3261e;
  --danger-border: #e8c4c1;

  --error-bg: #fdecea;
  --error-border: #f5c6c2;
  --error-fg: #7f1d1a;
  --notice-bg: #e8f4ea;
  --notice-border: #b7dcbd;
  --notice-fg: #1e4620;

  --radius: 4px;

  /* Opacity of the login page decoration, see the login section. */
  --login-emblem: .16;
  --login-skyline: .09;
}

/* The dark palette applies in two situations, and they are not the same one:
   the operating system asks for dark and nobody has overridden it, or somebody
   picked dark here. Hence two selectors with the same declarations — CSS cannot
   share a block between a media query and a plain rule, and a preprocessor
   would be the build step this project does not have. `TestTheDarkPaletteMatches`
   compares them, so a colour added to one and forgotten in the other fails.

   `:not([data-theme="light"])` is the half that matters for the switch: without
   it, choosing light on a machine set to dark would do nothing. */

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;

    --fg: #e4e6e8;
    --muted: #9aa1a8;
    --bg: #23272e;
    --bg-alt: #1a1d22;
    --field-bg: #1a1d22;
    --border: #3a4049;
    --border-soft: #2e333a;
    --accent: #79b0f2;
    --accent-bg: #3b6fb5;
    --accent-fg: #fff;

    --header-bg: #0f1216;
    --header-fg: #f0f2f4;
    --header-nav: #b5bcc4;
    --header-muted: #8b939b;
    --header-hover: #1c2128;

    --pill-ok: #2f8a34;
    --pill-off: #5d646c;
    --pill-warn: #c23a30;
    --pill-fg: #fff;

    --danger: #f08a82;
    --danger-border: #6b2f2a;

    --error-bg: #3a1f1c;
    --error-border: #6b302a;
    --error-fg: #f3b7b1;
    --notice-bg: #17301b;
    --notice-border: #2e5c34;
    --notice-fg: #a8d7ad;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;

  --fg: #e4e6e8;
  --muted: #9aa1a8;
  --bg: #23272e;
  --bg-alt: #1a1d22;
  --field-bg: #1a1d22;
  --border: #3a4049;
  --border-soft: #2e333a;
  --accent: #79b0f2;
  --accent-bg: #3b6fb5;
  --accent-fg: #fff;

  --header-bg: #0f1216;
  --header-fg: #f0f2f4;
  --header-nav: #b5bcc4;
  --header-muted: #8b939b;
  --header-hover: #1c2128;

  --pill-ok: #2f8a34;
  --pill-off: #5d646c;
  --pill-warn: #c23a30;
  --pill-fg: #fff;

  --danger: #f08a82;
  --danger-border: #6b2f2a;

  --error-bg: #3a1f1c;
  --error-border: #6b302a;
  --error-fg: #f3b7b1;
  --notice-bg: #17301b;
  --notice-border: #2e5c34;
  --notice-fg: #a8d7ad;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--fg);
  background: var(--bg-alt);
}

a { color: var(--accent); }

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

header.top {
  background: var(--header-bg);
  color: var(--header-fg);
  padding: 0 16px;
  display: flex;
  align-items: center;
  gap: 24px;
  flex-wrap: wrap;
}
header.top .brand { font-weight: 600; padding: 12px 0; }
header.top nav { display: flex; gap: 20px; flex: 1; }
header.top nav a { color: var(--header-nav); text-decoration: none; padding: 12px 0; }
header.top nav a:hover, header.top nav a[aria-current] {
  color: var(--header-fg);
  box-shadow: inset 0 -2px 0 var(--header-fg);
}
header.top .who { color: var(--header-muted); font-size: 13px; }
header.top .who a { color: var(--header-nav); }

main { max-width: 1000px; margin: 24px auto; padding: 0 16px; }

h1 { font-size: 20px; margin: 0 0 4px; }
h2 { font-size: 16px; margin: 24px 0 8px; }
.sub { color: var(--muted); font-size: 13px; margin: 0 0 20px; }

/* Title on the left, the page's own navigation on the right. The links to DKIM
   and DNS used to sit inside the grey subtitle, between a description and the
   word "aktiv" — and a link inside a sentence is read as part of the sentence,
   not as somewhere to go. As buttons on their own they match the row actions in
   every table below them, which is where a reader already looks for "open
   this". */
.pagehead {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-bottom: 20px;
}
.pagehead > .titles { flex: 1 1 260px; }
.pagehead .sub { margin-bottom: 0; }
.pagenav { display: flex; flex-wrap: wrap; gap: 8px; }

/* --- panels and tables ---------------------------------------------------- */

.panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: 20px;
  overflow: hidden;
}
.panel > header {
  padding: 10px 14px;
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
  display: flex;
  align-items: baseline;
  gap: 12px;
}
.panel > header h2 { margin: 0; font-size: 14px; }
.panel > header .count { color: var(--muted); font-size: 13px; margin-left: auto; }
.panel > footer { padding: 10px 14px; border-top: 1px solid var(--border); }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 8px 14px; border-bottom: 1px solid var(--border-soft); }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); font-weight: 600; }
tr:last-child td { border-bottom: none; }
td.actions { text-align: right; white-space: nowrap; }
td.num { text-align: right; font-variant-numeric: tabular-nums; }
.empty { padding: 16px 14px; color: var(--muted); }

/* --- controls ------------------------------------------------------------- */

.btn {
  display: inline-block;
  font: inherit;
  font-size: 13px;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--fg);
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--bg-alt); }
.btn-primary { background: var(--accent-bg); border-color: var(--accent-bg); color: var(--accent-fg); }
.btn-danger { color: var(--danger); border-color: var(--danger-border); }

/* The theme switch sits on the dark bar, so it takes the bar's colours rather
   than the page's. It is hidden until the script unhides it: a button that does
   nothing without JavaScript teaches that this program is unreliable, and
   without the script the operating system's setting still decides. */
.theme-toggle {
  background: none;
  border-color: var(--header-muted);
  color: var(--header-nav);
  padding: 2px 8px;
}
.theme-toggle:hover { background: var(--header-hover); color: var(--header-fg); }

/* The active toggle is a form button so it works without JavaScript; HTMX
   upgrades it to a partial swap when it is available. */
.toggle { border: none; background: none; font: inherit; cursor: pointer; padding: 0; }
/* The DNS page: one record per <details>, sixteen of them.
   Tight rows, because the value of the page is scanning the state column —
   the detail is what you open when a row says something you did not expect. */
.dnsrow { margin-bottom: 8px; }

.dnsrow > summary {
  /* 19px on the left, not 14: with list-style-position:inside the marker sits
     flush at the content edge, and at 14px it crowds the panel border. The
     body below matches, so the name and the detail under it stay on one line
     when the row is open. */
  padding: 9px 14px 9px 19px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 10px;
  /* The marker stays: it is the only thing that says the row opens, and a
     reader who cannot see a hover state has nothing else to go by. */
  list-style-position: inside;
}
.dnsrow > summary:hover { background: var(--bg-alt); }
.dnsrow[open] > summary {
  background: var(--bg-alt);
  border-bottom: 1px solid var(--border);
  margin-bottom: 12px;
}

/* The type sits between the name and the state, and the state goes right so
   that sixteen rows form one column to run an eye down. */
.dnsrow > summary .count { color: var(--muted); font-size: 13px; }
.dnsrow > summary .pill { margin-left: auto; }

/* Keyboard focus has to be visible: the summary is the control. */
.dnsrow > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.pill {
  display: inline-block; font-size: 12px; padding: 2px 8px; border-radius: 10px;
  background: var(--pill-ok); color: var(--pill-fg);
}
.pill.off { background: var(--pill-off); }
.pill.warn { background: var(--pill-warn); }

/* The DKIM mirror badge: what the signer has, beside what the database says.
   One line, pill first, because the pill is the answer and the sentence is
   only there for the states where the answer needs one. */
.mirror { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.mirror .hint { margin-top: 0; }

label { display: block; font-size: 13px; margin-bottom: 4px; }
input[type=text], input[type=password], input[type=email], input[type=number], select, textarea {
  width: 100%; font: inherit; padding: 6px 8px;
  color: var(--fg); background: var(--field-bg);
  border: 1px solid var(--border); border-radius: var(--radius);
}
.field { margin-bottom: 14px; }
.hint { color: var(--muted); font-size: 12px; margin-top: 3px; }

/* The password policy under a password field. A list rather than a
   sentence: the rules are read while typing, and a paragraph of four
   of them is read by nobody. */
.hint-rules { margin: 3px 0 0; padding-left: 16px; }
.hint-rules li { margin: 1px 0; }

.error {
  background: var(--error-bg); border: 1px solid var(--error-border); color: var(--error-fg);
  padding: 10px 14px; border-radius: var(--radius); margin-bottom: 16px;
}
.notice {
  background: var(--notice-bg); border: 1px solid var(--notice-border); color: var(--notice-fg);
  padding: 10px 14px; border-radius: var(--radius); margin-bottom: 16px;
}

/* --- login ---------------------------------------------------------------- */

/* Every page reached without a session — login, second factor, the password
   and recovery forms — wraps its panel in .login, so all of them get the same
   frame: the panel centred, an emblem above it, a Hamburg panorama along the
   bottom edge. The same decoration as the webmailer's login, on purpose: the
   two are the front doors of one installation. */
.login {
  min-height: 100vh; min-height: 100dvh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 24px 16px;
  position: relative; overflow: hidden;
}
.login > .panel { width: 100%; max-width: 360px; margin: 0; padding: 24px; position: relative; z-index: 1; }

/* Both are drawn as masks, not as pictures. The file contributes only its
   shape, the colour is var(--fg), so one emblem and one skyline work on the
   light and on the dark palette instead of needing a second file each. A
   browser without mask support gets neither, rather than two grey blocks —
   hence the @supports around the whole thing.

   --login-emblem is the larger of the two numbers because watermark.png is
   only about two thirds opaque where it is drawn at all; the skyline is
   solid. Both numbers are declared with the palette; set either to 0 to drop
   it. */

@supports (mask-image: url(watermark.png)) or (-webkit-mask-image: url(watermark.png)) {
  .login::before, .login::after {
    content: ""; pointer-events: none;
    background-color: var(--fg);
  }
  /* The emblem is a flex item above the panel, not positioned against the
     middle of the page: the panels here range from two fields to a password
     form with its rules, and a fixed offset would run into the taller ones.
     On short windows, where it would push the form down, it keeps no size
     and no mask, which leaves an empty box of zero height. */
  @media (min-height: 640px) {
    .login::before {
      flex: none;
      width: min(520px, 72vw); aspect-ratio: 1145 / 380;
      margin-bottom: 24px;
      opacity: var(--login-emblem);
      -webkit-mask: url(watermark.png) center / contain no-repeat;
      mask: url(watermark.png) center / contain no-repeat;
    }
  }
  /* The quay strip of the SVG spans its full width, so repeat-x has no seam. */
  .login::after {
    position: absolute; left: 0; right: 0; bottom: 0; height: min(190px, 28vh);
    opacity: var(--login-skyline);
    -webkit-mask: url(skyline-hamburg.svg) bottom left / auto 100% repeat-x;
    mask: url(skyline-hamburg.svg) bottom left / auto 100% repeat-x;
  }
}
.login h1 { text-align: center; margin-bottom: 20px; }

/* --- misc ----------------------------------------------------------------- */

.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }
.muted { color: var(--muted); }
footer.page { color: var(--muted); font-size: 12px; text-align: center; padding: 20px 0; }
