/* Spare Cycles Software — one stylesheet, no build step, no external requests.
   Mobile-first; one breakpoint. Mono for chrome and headings, sans for body.
   Dark always: no prefers-color-scheme query, because browsers report `light`
   for users who have expressed no preference, so a light fallback would be
   what most visitors got. color-scheme: dark also darkens scrollbars. */

:root {
  color-scheme: dark;
  --bg: #101114;
  --fg: #ecece9;
  --muted: #a6adb8;
  --accent: #f58d46;
  --rule: #303743;
  --tag-bg: #39271d;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

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

/* Decorative circuit band, full width. The tile repeats horizontally and is
   scaled to the band's height, so it fits any viewport without stretching.
   On html::before, not body::before: the root box is already the full visible
   width minus the scrollbar, so this bleeds edge to edge without the 100vw
   trick, which adds a phantom horizontal scrollbar whenever a vertical one is
   present. A pseudo-element because it is pure decoration — nothing added to
   four hand-written pages, and nothing for a screen reader to announce. */
html::before {
  content: "";
  display: block;
  height: clamp(56px, 13vw, 104px);
  background: var(--bg) url("/assets/circuit-header.svg") repeat-x center / auto 100%;
  border-bottom: 1px solid var(--rule);
}

body {
  margin: 0 auto;
  padding: 0 1.25rem;
  max-width: 44rem;
  background: var(--bg);
  color: var(--fg);
  font: 1rem/1.6 var(--sans);
  /* The email address is one unbreakable token: at large text sizes
     it runs past a phone-width viewport. Found by the smoke test's 200% case. */
  overflow-wrap: break-word;
}

a { color: var(--accent); text-underline-offset: 0.15em; }
a:focus-visible,
summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Chrome: header and footer share one voice. */
.site-header,
.site-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem 1.5rem;
  font-family: var(--mono);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
}

/* The mark is taller than the text, so the header centres its two sides; the
   footer, all text, keeps the shared baseline alignment. */
.site-header { padding: 1rem 0; border-bottom: 1px solid var(--rule); align-items: center; }
.site-footer { padding: 1.5rem 0 2.5rem; border-top: 1px solid var(--rule); color: var(--muted); }
.site-footer p { margin: 0; }

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--fg);
  font-weight: 600;
  text-decoration: none;
}
/* logo.svg has a viewBox and no size, so width follows from the height. */
.brand img { height: 1.5rem; width: auto; }

.site-header nav a { color: var(--muted); text-decoration: none; }
.site-header nav a + a { margin-left: 1rem; }
.site-header nav a:hover,
nav a[aria-current] { color: var(--accent); }

main { padding: 2rem 0 1.5rem; }
/* Whatever ends a page (paragraph, app card) carries its own bottom margin;
   dropping it makes the gap above the footer rule main's padding alone. */
main > :last-child { margin-bottom: 0; }

/* Type */
h1, h2, h3 { font-family: var(--mono); line-height: 1.25; }
h1 { font-size: 1.6rem; margin: 0 0 1rem; }
h2 {
  /* The rule sits midway in the gap between sections: 24px above it (this
     margin absorbs the paragraph's 1rem) and 24px below it. The footer rule
     gets the same 24/24 from main's bottom padding and its own top padding. */
  margin: 1.5rem 0 0.75rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  font-size: 1.15rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--fg);
}
h3 {
  font-size: 1rem;
  font-weight: 600;
  margin: 1.5rem 0 0.5rem;
  color: var(--muted);
}
p, ul, ol, dl { margin: 0 0 1rem; }
li { margin-bottom: 0.25rem; }
.meta { font-family: var(--mono); font-size: 0.8rem; color: var(--muted); }
.lead { font-size: 1.1rem; }

/* Key / value blocks */
.kv {
  display: grid;
  /* minmax(0,…) lets both tracks shrink below their content instead of blowing
     out of the viewport at large text sizes. Found by the smoke test. */
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  gap: 0.25rem 1rem;
}
.kv dt {
  padding-top: 0.15em;
  font-family: var(--mono);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.kv dd { margin: 0; }

/* App entries */
.app { margin: 0 0 1.5rem; padding: 1.25rem; border: 1px solid var(--rule); }
.app h2 {
  margin: 0 0 0.75rem;
  padding: 0;
  border: 0;
  font-size: 1.15rem;
  color: var(--fg);
}
.tag {
  display: block;
  width: fit-content;
  margin-top: 0.4rem;
  padding: 0.15em 0.5em;
  border-radius: 2px;
  background: var(--tag-bg);
  color: var(--accent);
  font-size: 0.75rem;
  font-weight: 400;
  letter-spacing: 0.01em;
}

/* Section navigation, privacy policy only — the one page long enough to need it
   (13 sections, ~5 screens). Collapsed by default: measured at 390px, an open
   13-item list is 468px of a 844px screen, and 833px at 200% text. Collapsed it
   is 53px. <details> is native, so the disclosure needs no JavaScript, which
   check.sh forbids outright. */
.toc { margin: 0 0 2rem; padding: 1rem 1.25rem; border: 1px solid var(--rule); }
.toc summary {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--muted);
  cursor: pointer;
}
.toc ol {
  /* A column WIDTH, not a count: two columns at normal text, and one once the
     text is large enough that two would be a few characters wide. */
  columns: 9rem;
  column-gap: 1.25rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}
.toc li { margin: 0 0 0.35rem; break-inside: avoid; }
.toc a { font-size: 0.85rem; line-height: 1.35; text-decoration: none; }
.toc a:hover { text-decoration: underline; }

/* An anchor jump should not land the heading flush against the viewport edge. */
.has-toc h2 { scroll-margin-top: 1rem; }

@media (min-width: 40rem) {
  body { padding: 0 2rem; }
  h1 { font-size: 2rem; }
}

/* Wide enough for a rail beside the 44rem column: the policy's sections move
   into the left margin and follow the reader down the page. */
@media (min-width: 68rem) {
  .has-toc { max-width: 60rem; }
  .has-toc main {
    display: grid;
    grid-template-columns: 12rem minmax(0, 1fr);
    column-gap: 3rem;
  }
  .has-toc main > * { grid-column: 2; }
  /* Grid items do not margin-collapse, so h2's 2.25rem top margin stopped
     absorbing the 1rem bottom margin of the block above it and desktop
     silently gained 1rem per section. Pre-subtract it to keep the rhythm the
     same as every other page. */
  .has-toc main > h2 { margin-top: 0.5rem; }
  .has-toc main > .toc {
    grid-column: 1;
    /* A sticky grid item can only travel inside its own grid area, so the rail
       must span the rows the content occupies. `1 / -1` will not do it: -1 is
       the last line of the EXPLICIT grid, and only columns are declared here,
       so it would resolve to a single row. 99 comfortably exceeds <main>'s 44
       children; if the page ever outgrows that the rail stops following, which
       is visible rather than silent. */
    grid-row: 1 / span 99;
    align-self: start;
    position: sticky;
    top: 1.5rem;
    /* 13 entries at a large minimum-font setting are taller than the viewport;
       without this the last sections are pinned out of reach. Measured 925px
       against an 800px viewport at a 32px root. */
    max-height: calc(100vh - 3rem);
    overflow-y: auto;
    margin: 0;
    padding: 0;
    border: 0;
  }
  .has-toc .toc ol { columns: 1; }
  /* Show the list outright in the rail. A closed <details> hides its contents
     with content-visibility on ::details-content, NOT by setting display on
     the children, so overriding the child is useless — measured: the <ol>
     computes display:block and still renders nothing. This pseudo-element is
     the only CSS hook, and it is Baseline only since Sept 2025, hence the
     @supports: an older Safari keeps a real click-to-open disclosure in the
     rail instead of an empty one. The summary stays visible in both cases. */
  @supports selector(::details-content) {
    .has-toc .toc details::details-content { content-visibility: visible; }
    /* The rail shows its links outright while the <details> itself stays
       closed, so a visible <summary> would announce aria-expanded="false" over
       13 links a screen-reader user can already tab into, and clicking it would
       change nothing on screen. Hiding it removes the control whose state would
       be a lie; aria-labelledby still names the landmark from it. Both rules
       sit inside this @supports, so a browser without ::details-content skips
       the pair together and keeps a real click-to-open disclosure. */
    .has-toc .toc summary { display: none; }
    /* Decoration only — the landmark's name comes from aria-labelledby. */
    .has-toc .toc::before {
      content: "On this page";
      display: block;
      margin-bottom: 0.6rem;
      font-family: var(--mono);
      font-size: 0.75rem;
      letter-spacing: 0.04em;
      color: var(--muted);
    }
  }
}

/* The privacy policy is the page most likely to be printed or saved as a PDF —
   it is the URL on file with App Store Connect. Browsers drop backgrounds when
   printing but keep the text colour, which would put #e3e5e8 on white paper at
   1.26:1. Chrome, nav and decoration carry nothing on paper. */
@media print {
  html::before { display: none; }
  body {
    max-width: none;
    background: #fff;
    color: #000;
    font-size: 11pt;
  }
  .site-header,
  .site-footer,
  .toc { display: none; }
  main { padding: 0; display: block; }
  h1, h2, h3 { color: #000; }
  .meta { color: #333; }
  a { color: #000; }
  /* A link's destination is invisible on paper unless it is written out.
     A prefilled mailto (one with a ?query) is not: its href is encoded noise. */
  main a[href^="https://"]::after,
  main a[href^="mailto:"]:not([href*="?"])::after { content: " <" attr(href) ">"; font-size: 9pt; }
  h2, h3 { break-after: avoid; }
}
