/* SolidRT site stylesheet: classless, over what the markdown converter and
 * the landing fragment emit (see okf/plans/website.md, styling track).
 *
 * The look is that of an app built with @solidrt/components: filled,
 * borderless surfaces (card fill on window fill; no rules, no underlines),
 * radius 12 / padding 20 / gap 16, one text color with muted for secondary
 * text, headings separated by size and weight rather than tone, flat accent
 * buttons. Every value reads a --sol-* token from tokens.css, which the site
 * build generates from the framework theme. Sits on modern-normalize
 * (vendored, MIT) for the cross-browser baseline; this file is look only.
 *
 * Prose scale: the framework's type scale is sized for app UI (14px body).
 * Long-form reading gets a 16px body with the same ratios, applied through
 * one factor over the emitted sizes.
 */

:root {
  --sol-prose-scale: calc(16 / 14);
  --sol-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Noto Sans", sans-serif;
  --sol-font-mono: ui-monospace, "SFMono-Regular", Menlo, Consolas, "Noto Sans Mono", monospace;
  --sol-content-width: 46rem;
  --sol-sidebar-width: 14rem;
  --sol-column-gap: calc(var(--sol-spacing-xl) * 2);
  /* The page is one centered block: sidebar, gutter, content column. */
  --sol-page-width: calc(
    var(--sol-content-width) + var(--sol-sidebar-width) + var(--sol-column-gap)
  );
  /* The sticky header's height: the nav's padding and the brand link (its
   * 40px mark plus padding). What stays clear below it: the sidebar and
   * anchor targets. */
  --sol-header-height: 5.5rem;
  /* The landing's eyebrow mint, for the dash that marks the current page in
   * the sidebar; a deep teal in the light scheme, where mint is too faint. */
  --sol-accent-line: light-dark(#0f766e, #5eead4);
  /* The bar at rest, sunk toward the page (darker on dark, lighter on
   * light); it brightens to the full accent while hovered. The percentage
   * is how much accent stays. */
  --sol-accent-line-rest: color-mix(in srgb, var(--sol-accent-line) 35%, var(--sol-background));
  /* The dash's width; it sits that far left of the current link, just
   * outside the link's hover fill. */
  --sol-accent-line-width: 3px;
  --sol-page-gradient-end: color-mix(in srgb, var(--sol-background) 75%, var(--sol-primary));
  /* Filled boxes carry the page's diagonal wash, run the other way: the accent
   * end sits top-left where the page fill is plainest, so a card reads as a
   * surface and not as a flat patch. */
  --sol-surface-gradient: linear-gradient(
    315deg,
    var(--sol-surface),
    color-mix(in srgb, var(--sol-surface) 82%, var(--sol-primary))
  );
  /* Code, quotes and tables sit on a flat panel with a thin outline: in the dark
   * scheme the landing cards' navy, which the page's wash shows through. */
  --sol-panel: light-dark(var(--sol-surface), rgb(10 17 34 / 0.8));
  --sol-panel-stroke: light-dark(var(--sol-surface-alt), #1e2a4a);
  color-scheme: light dark;
}

/* A forced theme forces the scheme too, so light-dark() follows it. */
[data-theme="light"] {
  color-scheme: light;
}

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

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

html {
  font-family: var(--sol-font-sans);
  font-size: calc(var(--sol-body-size) * var(--sol-prose-scale));
  line-height: var(--sol-body-line-height);
  font-weight: var(--sol-body-weight);
  color: var(--sol-text);
  /* Diagonal wash from the page fill toward the accent, as on the changelog
   * card; fixed so it spans the viewport rather than the document. */
  background: linear-gradient(135deg, var(--sol-background), var(--sol-page-gradient-end)) fixed;
}

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

main {
  width: 100%;
  max-width: var(--sol-page-width);
  margin: 0 auto;
  padding: 0 var(--sol-spacing-xl);
}

/* The header stays in view while the page scrolls. It has no fill of its
   own, so it reads as the page, and blurs whatever passes under it. It spans
   the viewport so the blur covers the whole width, and its padding keeps the
   content on the page block, aligned with main. */
header {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 0 max(var(--sol-spacing-xl), calc((100% - var(--sol-page-width)) / 2 + var(--sol-spacing-xl)));
}

/* The blur sits on its own layer behind the nav so a mask can fade it out
   toward the header's lower edge, leaving no line where blurred content
   meets sharp; the nav itself stays unmasked. */
header::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  backdrop-filter: blur(12px);
  /* Full blur down to here, then fading to none at the bottom edge. */
  mask-image: linear-gradient(to bottom, black 50%, transparent);
}

main {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sol-column-gap);
  padding-top: var(--sol-spacing-xl);
  padding-bottom: calc(var(--sol-spacing-xl) * 3);
}

/* The content column keeps its reading width whatever the page block is, and
   centers in the space it is given: with a sidebar that space is exactly one
   content width, so the margins only do anything on a page without one, where
   they keep the text off the block's left edge. */
article {
  flex: 1;
  /* Fills main on a narrow screen, where main stacks: the auto margins below
     would otherwise shrink it to its widest content (a long code line) and
     push the page sideways. */
  width: 100%;
  min-width: 0;
  /* A long identifier (a heading named after a type) breaks rather than
     running off a narrow screen. */
  overflow-wrap: break-word;
  max-width: var(--sol-content-width);
  margin-inline: auto;
}

/* -- Sidebar ------------------------------------------------------------ */

/* Section pages with more than one page get an in-section sidebar. It is part
   of the page block, not a gutter hanger: the block (aside plus article)
   centers as a whole, so the aside survives down to where the two columns
   stop fitting. Below that it stacks above the article, where it stays in
   view without scrolling. Source order is aside then article, which is also
   the stacked order, so neither case sets `order`. */
aside {
  width: 100%;
}

@media (min-width: 64rem) {
  main {
    flex-direction: row;
    align-items: flex-start;
  }

  aside {
    flex: 0 0 var(--sol-sidebar-width);
    width: auto;
    position: sticky;
    top: calc(var(--sol-header-height) + var(--sol-spacing-xl));
    max-height: calc(100vh - var(--sol-header-height) - var(--sol-spacing-xl) * 2);
    overflow-y: auto;
  }
}

aside ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Room for the current page's dash left of the top-level links; the
   scrolling aside would clip it otherwise. */
aside nav > ul {
  padding-left: var(--sol-accent-line-width);
}

aside li {
  margin: 0;
}

/* A group: its heading row, then its members indented one step. */
aside li > ul,
aside details > ul {
  margin: var(--sol-spacing-sm) 0 var(--sol-spacing-md);
  padding-left: var(--sol-spacing-lg);
}

/* A group heading is a disclosure row: the title still navigates to the
   group's own page, the rest of the row opens and closes it. The row carries
   the hover fill so it reads as one target either way; the current page's
   dash sits on the title link, as on any other item. */
aside summary {
  display: flex;
  align-items: center;
  list-style: none;
  cursor: pointer;
}

aside summary::-webkit-details-marker {
  display: none;
}

aside summary > a {
  flex: 1;
}

/* A hovered or current group heading: the row carries the fill. */
aside summary:hover,
aside summary:has(> a[aria-current="page"]) {
  background: var(--sol-surface-gradient);
}

aside summary > a:hover,
aside summary > a[aria-current="page"] {
  background: transparent;
}

/* The chevron: pointing right when closed, down when open. */
aside summary::after {
  content: "";
  flex: none;
  width: 0.4em;
  height: 0.4em;
  margin-right: var(--sol-spacing-md);
  border-right: var(--sol-border-width-sm) solid var(--sol-text-muted);
  border-bottom: var(--sol-border-width-sm) solid var(--sol-text-muted);
  transform: translateY(-0.1em) rotate(-45deg);
  transition: transform 120ms ease-out;
}

aside details[open] > summary::after {
  transform: translateY(-0.1em) rotate(45deg);
}

aside a {
  position: relative;
  display: block;
  padding: var(--sol-spacing-sm) var(--sol-spacing-md);
  color: var(--sol-text);
  font-size: calc(var(--sol-label-size) * var(--sol-prose-scale));
  font-weight: var(--sol-body-weight);
  line-height: var(--sol-body-line-height);
}

/* A hovered link and the current page look alike: the fill quotes and code
   blocks share, which opens with the landing eyebrow's dash stood upright
   (as they do). The current page is also bold. */
aside a:hover,
aside a[aria-current="page"] {
  background: var(--sol-surface-gradient);
}

aside a[aria-current="page"] {
  font-weight: var(--sol-label-weight);
}

aside a::before {
  content: "";
  position: absolute;
  /* The link's full height, so it meets the fill edge to edge, and just
     left of the link. */
  inset: 0 auto 0 calc(var(--sol-accent-line-width) * -1);
  width: var(--sol-accent-line-width);
}

/* Hovered, the dash keeps its resting color; only the current page's is
   bright. */
aside a:hover::before,
aside summary:hover > a::before {
  background: var(--sol-accent-line-rest);
}

aside a[aria-current="page"]::before,
aside summary:hover > a[aria-current="page"]::before {
  background: var(--sol-accent-line);
}

/* -- Header nav --------------------------------------------------------- */

header nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sol-spacing-lg);
  padding: var(--sol-spacing-lg) 0;
}

header nav ul {
  display: flex;
  align-items: center;
  gap: var(--sol-spacing-sm);
  margin: 0;
  padding: 0;
  list-style: none;
}

header nav li {
  margin: 0;
}

/* Section links: no fill, an underline as wide as the text on hover, at the
   sidebar bar's resting color. The current section's underline is bright. */
header nav a {
  position: relative;
  display: inline-block;
  padding: var(--sol-spacing-md) var(--sol-spacing-lg);
  color: var(--sol-text);
  font-size: calc(var(--sol-label-size) * var(--sol-prose-scale));
  font-weight: var(--sol-label-weight);
  line-height: var(--sol-label-line-height);
  text-decoration: none;
  text-transform: capitalize;
}

#site-sections a:hover::after,
#site-sections a[aria-current]::after {
  content: "";
  position: absolute;
  inset: auto var(--sol-spacing-lg) var(--sol-spacing-sm);
  height: var(--sol-accent-line-width);
  background: var(--sol-accent-line-rest);
}

#site-sections a[aria-current]::after {
  background: var(--sol-accent-line);
}

/* The brand link: title-sized wordmark, no button treatment. */
header nav > ul:first-child a {
  padding-left: 0;
  font-size: calc(var(--sol-title-size) * var(--sol-prose-scale));
  font-weight: var(--sol-title-weight);
  text-transform: none;
}

/* The brand takes the free space, so the sections and the icon links pack
   together on the right. */
header nav > ul:first-child {
  margin-right: auto;
}

/* Icon links (GitHub, Discord): muted until hover, no underline. */
#site-links {
  gap: 0;
}

#site-links a {
  display: flex;
  padding: var(--sol-spacing-md);
  color: var(--sol-text-muted);
}

#site-links a:hover {
  color: var(--sol-text);
}

/* The menu button that stands in for the section links on narrow screens,
   where they do not fit beside the brand. */
.menu-toggle {
  display: none;
}

/* The pages of the one published section, listed in the phone menu only
   (see menuPages in build.ts). Temporary, like that list. */
#site-sections > .menu-page {
  display: none;
}

@media (max-width: 50rem) {
  header nav {
    flex-wrap: wrap;
  }

  .menu-toggle {
    display: inline-flex;
    order: 1;
    padding: var(--sol-spacing-md);
    background: transparent;
    color: var(--sol-text);
  }

  .menu-toggle:hover {
    color: var(--sol-text);
  }

  /* Collapsed until the button opens it, then a full-width column under
     the brand row, which keeps the icon links. */
  #site-sections {
    display: none;
    order: 2;
    flex-basis: 100%;
    flex-direction: column;
    align-items: stretch;
  }

  .menu-toggle[aria-expanded="true"] + #site-sections {
    display: flex;
  }

  /* While the menu is open, the page under it holds still, and the header
     takes the page's fill, so the menu reads as a panel and the page does
     not scroll by beneath it. */
  :root:has(.menu-toggle[aria-expanded="true"]) {
    overflow: hidden;
  }

  header:has(.menu-toggle[aria-expanded="true"]) {
    background: var(--sol-background);
  }

  #site-sections a {
    display: block;
  }

  /* With the section's pages in the menu, the lone section link is left
     out. */
  #site-sections > .menu-page {
    display: list-item;
  }

  #site-sections:has(> .menu-page) > li:not(.menu-page) {
    display: none;
  }

  /* Page links set like the sidebar's: tight rows, page titles as written. */
  #site-sections > .menu-page > a {
    padding-top: var(--sol-spacing-sm);
    padding-bottom: var(--sol-spacing-sm);
    font-weight: var(--sol-body-weight);
    text-transform: none;
  }

  #site-sections > .menu-page > a[aria-current] {
    font-weight: var(--sol-label-weight);
  }

  /* In the stacked menu the marker is the sidebar's upright dash on the
     left, not an underline. */
  #site-sections a:hover::after,
  #site-sections a[aria-current]::after {
    inset: 0 auto 0 0;
    width: var(--sol-accent-line-width);
    height: auto;
  }

  /* The menu then carries the section's pages, so the sidebar stacked
     above the article would list them a second time. */
  body:has(#site-sections > .menu-page) aside {
    display: none;
  }
}

/* -- Type scale --------------------------------------------------------- */

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0 0 var(--sol-spacing-lg);
  color: var(--sol-text);
  font-weight: var(--sol-heading-weight);
  line-height: var(--sol-heading-line-height);
  scroll-margin-top: calc(var(--sol-header-height) + var(--sol-spacing-xl));
}

h1 {
  font-size: calc(var(--sol-heading-size) * var(--sol-prose-scale) * 1.4);
}

h2 {
  font-size: calc(var(--sol-heading-size) * var(--sol-prose-scale));
  margin-top: calc(var(--sol-spacing-xl) * 2);
}

h3 {
  font-size: calc(var(--sol-title-size) * var(--sol-prose-scale));
  font-weight: var(--sol-title-weight);
  line-height: var(--sol-title-line-height);
  margin-top: calc(var(--sol-spacing-xl) * 1.5);
}

h4,
h5,
h6 {
  font-size: calc(var(--sol-body-size) * var(--sol-prose-scale));
  font-weight: var(--sol-label-weight);
  margin-top: var(--sol-spacing-xl);
}

/* Landing hero: title plus a muted subtitle, no gap between them. */
hgroup {
  margin-bottom: var(--sol-spacing-lg);
}

hgroup > * {
  margin: 0;
}

hgroup > p {
  color: var(--sol-text-muted);
  font-size: calc(var(--sol-title-size) * var(--sol-prose-scale));
}

p,
ul,
ol,
blockquote,
pre,
table,
img {
  margin: 0 0 var(--sol-spacing-lg);
}

ul,
ol {
  padding-left: calc(var(--sol-spacing-xl) + var(--sol-spacing-sm));
}

li {
  margin-bottom: var(--sol-spacing-md);
}

small {
  color: var(--sol-text-muted);
}

li > ul,
li > ol {
  margin-top: var(--sol-spacing-md);
  margin-bottom: 0;
}

strong {
  font-weight: var(--sol-title-weight);
}

/* -- Links and buttons -------------------------------------------------- */

a {
  color: var(--sol-primary);
  text-decoration: none;
}

a:hover {
  color: var(--sol-text);
}

/* Button primary: accent fill, on-primary label, hover tint, press scale. */
a[role="button"],
button {
  display: inline-block;
  padding: var(--sol-spacing-md) var(--sol-spacing-lg);
  border: 0;
  border-radius: var(--sol-radius-md);
  background: var(--sol-primary);
  color: var(--sol-on-primary);
  font: inherit;
  font-weight: var(--sol-label-weight);
  line-height: var(--sol-label-line-height);
  text-decoration: none;
  cursor: pointer;
  transition: transform 80ms ease-out;
}

a[role="button"]:hover,
button:hover {
  /* The theme's overlay tint composited over the fill, like Button does. */
  background-image: linear-gradient(var(--sol-overlay-hover), var(--sol-overlay-hover));
  color: var(--sol-on-primary);
}

a[role="button"]:active,
button:active {
  transform: scale(0.97);
}

:focus-visible {
  outline: var(--sol-border-width-sm) solid var(--sol-primary);
  outline-offset: 2px;
}

/* -- Surfaces ----------------------------------------------------------- */

/* Landing sections are cards: surface fill on the window fill. */
article > section {
  padding: var(--sol-spacing-xl);
  border-radius: var(--sol-radius-lg);
  background: var(--sol-surface-gradient);
  margin-bottom: var(--sol-spacing-lg);
}

article > section > :last-child {
  margin-bottom: 0;
}

article > section > h2 {
  margin-top: 0;
}

/* Inline code sits on the panel quotes and code blocks share. */
code {
  font-family: var(--sol-font-mono);
  font-size: 0.9em;
  padding: 0.1em 0.35em;
  border-radius: var(--sol-radius-sm);
  background: var(--sol-panel);
}

/* A code block is panelled like a quote: the accent bar, then the panel in
   its outline, with the code on the column's left edge and the panel
   reaching out to the left. The bar is a border, not a positioned layer, so
   it stays put while a long line scrolls. */
pre {
  margin-left: calc((var(--sol-spacing-md) + var(--sol-accent-line-width)) * -1);
  padding: var(--sol-spacing-lg) var(--sol-spacing-lg) var(--sol-spacing-lg) var(--sol-spacing-md);
  border: 1.5px solid var(--sol-panel-stroke);
  border-left: var(--sol-accent-line-width) solid var(--sol-accent-line-rest);
  border-radius: 0 var(--sol-radius-md) var(--sol-radius-md) 0;
  background: var(--sol-panel);
  overflow-x: auto;
  transition: border-left-color 120ms ease-out;
}

pre:hover,
blockquote:hover {
  border-left-color: var(--sol-accent-line);
}

pre code,
pre code.hljs {
  display: block;
  padding: 0;
  font-size: calc(var(--sol-label-size) * var(--sol-prose-scale));
  line-height: var(--sol-body-line-height);
  background: transparent;
  color: var(--sol-text);
}

/* A quote: an outlined panel opening with the accent bar, like the current
   page in the sidebar. Its text stays on the column's left edge with the
   paragraphs around it; the panel and the bar reach out to the left. */
blockquote {
  margin-left: calc((var(--sol-spacing-md) + var(--sol-accent-line-width)) * -1);
  padding: var(--sol-spacing-lg) var(--sol-spacing-xl) var(--sol-spacing-lg) var(--sol-spacing-md);
  border: 1.5px solid var(--sol-panel-stroke);
  border-left: var(--sol-accent-line-width) solid var(--sol-accent-line-rest);
  border-radius: 0 var(--sol-radius-md) var(--sol-radius-md) 0;
  background: var(--sol-panel);
  color: var(--sol-text-muted);
  transition: border-left-color 120ms ease-out;
}

blockquote > :last-child {
  margin-bottom: 0;
}

/* No rules anywhere: a thematic break is spacing only. */
hr {
  border: 0;
  height: var(--sol-spacing-xl);
  margin: 0;
}

/* -- Tables ------------------------------------------------------------- */

/* The wrapper of every table: a table too wide for a phone scrolls inside
   it, rather than widening the page. */
.table {
  overflow-x: auto;
  margin: 0 0 var(--sol-spacing-lg);
}

.table > table {
  margin: 0;
}

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  border: 1.5px solid var(--sol-panel-stroke);
  border-radius: var(--sol-radius-md);
  overflow: hidden;
  background: var(--sol-panel);
}

th,
td {
  padding: var(--sol-spacing-md) var(--sol-spacing-lg);
  text-align: left;
  vertical-align: top;
  /* Lets a cell of long inline code break, so the table narrows to fit a
     phone instead of widening the page; with room to spare nothing breaks. */
  overflow-wrap: anywhere;
}

th {
  font-size: calc(var(--sol-label-size) * var(--sol-prose-scale));
  font-weight: var(--sol-label-weight);
  color: var(--sol-text-muted);
  background: var(--sol-panel-stroke);
  /* A header is a short label, never long code: it breaks between words
     only, so a squeezed column cannot split one mid-word. */
  overflow-wrap: normal;
}

/* A token of inline code (see unbrokenTokens in markdown.ts): the code
   breaks between tokens, never inside one. */
.nobr {
  white-space: nowrap;
}

/* Inline code inside a panel would vanish into it; it takes the outline's
   color instead. */
:is(blockquote, td) code {
  background: var(--sol-panel-stroke);
}

/* -- Media -------------------------------------------------------------- */

img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--sol-radius-md);
}

::selection {
  background: color-mix(in srgb, var(--sol-primary) 25%, transparent);
}

/* -- Small screens ------------------------------------------------------ */

@media (max-width: 40rem) {
  header,
  main {
    padding-left: var(--sol-spacing-lg);
    padding-right: var(--sol-spacing-lg);
  }
}
