/* Substrator public pages (/ and /changelog). Self-contained: the admin and error pages use
   site.css, so nothing here can leak into them.

   The page's own elements wear the app's look (crates/sub-app/src/theme.rs, glass.rs): the page
   is the viewport, cards are glass panes (tinted blurred fill, 1px white-alpha stroke, a sheen
   on the top edge, 14px corners, a title row in the app's heading style), buttons and links are
   the app's 8px widgets, and the accent is rgb(122, 162, 255). No pictures of app controls. Type: Bricolage Grotesque for display, Instrument Sans for text (both OFL,
   self-hosted in static/fonts with their licences). */

@font-face {
  font-family: "Bricolage Grotesque";
  src: url("/static/fonts/bricolage-grotesque-v9-latin.woff2") format("woff2");
  font-weight: 200 800; font-stretch: 75% 100%; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Instrument Sans";
  src: url("/static/fonts/instrument-sans-v4-latin.woff2") format("woff2");
  font-weight: 400 700; font-stretch: 75% 100%; font-style: normal; font-display: swap;
}
/* Fallbacks scaled to the same width (measured), so the swap barely moves the layout. */
@font-face { font-family: "Bricolage fallback"; src: local("Arial Bold"), local("Arial-BoldMT"); font-weight: 600 800; size-adjust: 93%; }
@font-face { font-family: "Bricolage fallback"; src: local("Arial"), local("ArialMT"); font-weight: 200 599; size-adjust: 97%; }
@font-face { font-family: "Instrument fallback"; src: local("Arial"), local("ArialMT"); size-adjust: 101%; }

:root {
  color-scheme: dark;
  --bg: #0b0d13;
  --text: #e8ebf2;
  --weak: #969caa;
  --accent: rgb(122, 162, 255);
  --accent-ink: #0b1124;
  --sel: rgba(122, 162, 255, .35);
  --paint: #ff7a5c;
  --stroke: rgba(255, 255, 255, .10);
  --line: rgba(255, 255, 255, .07);
  --widget: rgba(255, 255, 255, .055);
  --widget-hover: rgba(255, 255, 255, .10);
  --tint: rgba(16, 18, 26, .73);
  --solid: #1c1e26;
  --sheen: rgba(255, 255, 255, .16);
  --shadow: 0 10px 28px rgba(0, 0, 0, .35);
  --blur: blur(22px) saturate(140%);
  --display: "Bricolage Grotesque", "Bricolage fallback", "Arial Narrow", sans-serif;
  --ui: "Instrument Sans", "Instrument fallback", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
body {
  margin: 0; min-height: 100vh; display: flex; flex-direction: column;
  font: 16px/1.55 var(--ui); color: var(--text); background: var(--bg);
  -webkit-font-smoothing: antialiased;
}
h1, h2, h3, p { margin: 0; }
h1, h2, h3 { font-family: var(--display); font-weight: 700; line-height: 1.05; letter-spacing: -.02em; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }
:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
::selection { background: var(--sel); }
.i { width: 1.15em; height: 1.15em; flex: none; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.skip { position: absolute; left: -999px; top: 10px; z-index: 20; }
.skip:focus { left: 10px; padding: 10px 14px; border-radius: 8px; background: var(--solid); color: var(--text); }

/* The viewport behind everything: a dark field, a texel grid fading out, two soft lights. */
.backdrop { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none;
  background: radial-gradient(120% 80% at 50% 0%, #151a28 0%, var(--bg) 60%); }
.backdrop::before {
  content: ""; position: absolute; inset: 0;
  background-image: linear-gradient(rgba(255, 255, 255, .035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, .035) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: linear-gradient(180deg, #000, transparent 85%);
  mask-image: linear-gradient(180deg, #000, transparent 85%);
}
.backdrop span { position: absolute; border-radius: 50%; filter: blur(100px); }
.backdrop .b1 { width: 50vmax; height: 40vmax; left: 25vw; top: 20vh; background: #23408f; opacity: .35; }
.backdrop .b2 { width: 24vmax; height: 24vmax; left: 46vw; top: 52vh; background: #a8452f; opacity: .12; }
.backdrop .b3, .backdrop .b4 { display: none; }

/* The top bar: full width, square, a divider underneath, like the app's toolbar. */
.top {
  position: sticky; top: 0; z-index: 5;
  background: var(--tint); border-bottom: 1px solid var(--stroke);
  -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur);
}
.top-in { max-width: 1240px; margin: 0 auto; padding: 0 28px; height: 52px; display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.brand { display: inline-flex; align-items: center; gap: 10px; color: var(--text); font: 700 17px/1 var(--display); letter-spacing: -.01em; }
.brand:hover { text-decoration: none; }
.mark {
  width: 26px; height: 26px; border-radius: 8px; display: inline-grid; place-items: center; flex: none;
  color: var(--accent-ink); background: var(--accent); box-shadow: inset 0 1px 0 rgba(255, 255, 255, .45);
}
.mark .i { width: 16px; height: 16px; }
.top nav { display: flex; gap: 4px; }
/* Nav links are the app's selectable labels. */
.top nav a { color: var(--text); font-size: 14px; padding: 5px 10px; border-radius: 8px; }
.top nav a:hover { background: var(--widget-hover); box-shadow: inset 0 0 0 1px var(--stroke); text-decoration: none; }
.top nav a[aria-current="page"] { background: var(--sel); color: var(--accent); }

.site { width: 100%; max-width: 1240px; margin: 0 auto; padding: 0 28px; flex: 1; display: flex; flex-direction: column; }
main { flex: 1; }

/* Hero: the name, one line and the download on the left, the sphere on the right. The title is
   sized to its column: "Substrator" is about 4.5em wide in this face, and the left column is
   .535 of the content width minus the gap, so it never clips. */
.hero {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: 40px; align-items: center;
  padding: clamp(32px, 7vh, 80px) 0 clamp(24px, 5vh, 56px);
}
.hero h1 {
  font-size: clamp(44px, calc((100vw - 96px) * .535 / 4.7), 128px);
  font-weight: 800; letter-spacing: -.045em; line-height: .9;
  margin-left: -.04em; padding: 0 .05em .04em 0;
}
.lede { margin-top: clamp(18px, 2.4vw, 28px); max-width: 38ch; font-size: clamp(17px, 1.45vw, 19px); line-height: 1.5; color: #c9cdd8; }

/* Download block: the app's buttons. The download is in the active state (accent stroke over the
   selection fill), the changelog link an ordinary widget. */
.dl { margin-top: 28px; display: grid; gap: 10px; justify-items: start; }
.dl-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.get, .more {
  display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 16px 0 13px; border-radius: 8px;
  font-weight: 600; font-size: 15px; color: var(--text); transition: background .12s ease, box-shadow .12s ease;
}
.get { background: var(--sel); box-shadow: inset 0 0 0 1px var(--accent); }
.get:hover { background: rgba(122, 162, 255, .48); text-decoration: none; }
.more { background: var(--widget); }
.more:hover { background: var(--widget-hover); box-shadow: inset 0 0 0 1px var(--stroke); text-decoration: none; }
.get .i, .more .i { width: 18px; height: 18px; }
.more .i { transform: rotate(180deg); color: var(--accent); }
.soon { display: inline-flex; align-items: center; gap: 8px; height: 40px; padding-right: 8px; font-weight: 600; }
.soon .i { color: var(--accent); width: 18px; height: 18px; }
.dl-meta, .card .dl-meta { font: 12.5px/1.4 var(--mono); color: var(--weak); }
.card .soon { font-size: 15px; color: var(--text); }

/* The sphere: art, not UI. */
.art { position: relative; display: grid; place-items: center; }
.art::before {
  content: ""; position: absolute; inset: 12% 12% 18%; border-radius: 50%; z-index: -1;
  background: radial-gradient(closest-side, rgba(122, 162, 255, .40), rgba(122, 162, 255, .08) 65%, transparent);
  filter: blur(24px);
}
.sphere { display: block; width: 100%; max-width: 460px; height: auto; }
.sb-cursor { transform: translate(318px, 212px); }

/* ---------- Cards: glass panes with a title row ---------- */
.card {
  position: relative; padding: 14px 16px 16px; border-radius: 14px;
  background: var(--tint); border: 1px solid var(--stroke); box-shadow: var(--shadow);
  -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur);
}
.card::before { content: ""; position: absolute; top: 0; left: 14px; right: 14px; height: 1px; background: var(--sheen); }
.card-head { display: flex; align-items: center; gap: 8px; min-height: 26px; margin-bottom: 8px; }
.card-head .i { width: 17px; height: 17px; color: var(--accent); }
.card-head h2, .card-head h3 { font: 650 15px/1.3 var(--ui); letter-spacing: 0; }
.card p { font-size: 14px; line-height: 1.5; color: #cdd1db; }
/* Dense rows, like the app's lists. */
.rows { list-style: none; margin: 12px 0 0; padding: 0; font-size: 13px; }
.rows li { display: flex; align-items: center; min-height: 28px; padding: 4px 2px; border-top: 1px solid var(--line); color: var(--text); }

.features { padding-top: clamp(24px, 6vh, 64px); scroll-margin-top: 64px; }
.section-title { font-size: clamp(26px, 3vw, 34px); letter-spacing: -.03em; margin: 0 0 18px 2px; }
.cards { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; align-items: start; }

/* Closing call: a pane with the download. */
.cta { margin: 72px auto 0; max-width: 620px; width: 100%; padding: 14px 18px 20px; }
.cta .card-head { margin-bottom: 4px; }
.cta .dl { margin-top: 12px; }

/* Footer. */
.foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px 24px;
  margin-top: 72px; padding: 22px 0 30px; border-top: 1px solid var(--line); color: var(--weak); font-size: 14px; }
.foot span { display: inline-flex; align-items: center; gap: 10px; }
.foot .mark { width: 20px; height: 20px; border-radius: 6px; }
.foot .mark .i { width: 12px; height: 12px; }
.foot a { color: var(--text); padding: 5px 10px; border-radius: 8px; }
.foot a:hover { background: var(--widget-hover); text-decoration: none; }

/* ---------- Changelog: one pane per release ---------- */
.log { max-width: 760px; width: 100%; margin: 0 auto; padding: 64px 0 0; }
.log-head { margin-bottom: 28px; }
.log-head h1 { font-size: clamp(44px, 8vw, 80px); font-weight: 800; letter-spacing: -.045em; }
.log-head p { margin-top: 10px; color: var(--weak); font-size: 17px; }
.releases { list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; }
.release { scroll-margin-top: 72px; }
.release .card-head { flex-wrap: wrap; gap: 6px 10px; padding-bottom: 10px; margin-bottom: 12px; border-bottom: 1px solid var(--line); }
.release .card-head h2 { font-size: 17px; font-variant-numeric: tabular-nums; }
.release time { margin-left: auto; font: 12.5px var(--mono); color: var(--weak); }
.chip { padding: 1px 8px; border-radius: 8px; font-size: 12px; font-weight: 600; color: var(--accent); background: var(--sel); }
.release .none, .card.empty { color: var(--weak); }
.card.empty { text-align: center; padding: 28px; }
.prose { color: #cdd1db; font-size: 14px; }
.prose > * { margin: 0; }
.prose > * + * { margin-top: .7em; }
.prose ul, .prose ol { padding-left: 1.3em; }
.prose li + li { margin-top: .25em; }
.prose h1, .prose h2, .prose h3, .prose h4 { font: 650 14px/1.3 var(--ui); letter-spacing: 0; color: var(--text); margin-top: 1.1em; }
.prose strong { color: var(--text); }
.prose code { font: 13px var(--mono); background: var(--widget); padding: 1px 6px; border-radius: 6px; }
.prose pre { background: rgba(0, 0, 0, .35); padding: 12px 14px; border-radius: 8px; overflow-x: auto; }
.prose pre code { background: none; padding: 0; }
.prose a { text-decoration: underline; text-underline-offset: 3px; }

/* ---------- Motion: the stroke draws itself with the cursor riding along it, the UV grid sways,
   the sphere floats and its highlight drifts. Transforms, opacity and the stroke's dash only.
   Nothing moves when reduced motion is asked for: the drawing is then complete and still. ---------- */
@media (prefers-reduced-motion: no-preference) {
  .sphere { animation: sb-float 9s ease-in-out infinite; }
  .art::before { animation: sb-glow 9s ease-in-out infinite; }
  .sb-grid { transform-box: view-box; transform-origin: 200px 200px; animation: sb-sway 16s ease-in-out infinite; }
  .sb-spec { transform-box: view-box; animation: sb-spec 9s ease-in-out infinite; }
  .sb-stroke path { stroke-dasharray: 1 1; animation: sb-draw 8s cubic-bezier(.45, 0, .3, 1) infinite; }
  @supports (offset-path: path("M0 0")) {
    .sb-cursor {
      transform: none; offset-path: path("M58 238C118 200 190 264 250 228S305 204 318 212"); offset-rotate: 0deg;
      animation: sb-ride 8s cubic-bezier(.45, 0, .3, 1) infinite;
    }
  }
}
@keyframes sb-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
@keyframes sb-glow { 0%, 100% { opacity: .75; } 50% { opacity: 1; } }
@keyframes sb-sway { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
@keyframes sb-spec { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(8px, 5px); } }
@keyframes sb-draw {
  0% { stroke-dashoffset: 1; opacity: 1; }
  55% { stroke-dashoffset: 0; }
  84% { stroke-dashoffset: 0; opacity: 1; }
  96% { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: 1; opacity: 0; }
}
@keyframes sb-ride {
  0% { offset-distance: 0%; opacity: 0; }
  6% { opacity: 1; }
  55% { offset-distance: 100%; }
  84% { offset-distance: 100%; opacity: 1; }
  96% { offset-distance: 100%; opacity: 0; }
  100% { offset-distance: 0%; opacity: 0; }
}

/* ---------- Responsive ---------- */
@media (max-width: 980px) {
  .hero { grid-template-columns: minmax(0, 1fr); gap: 8px; }
  .hero h1 { font-size: clamp(44px, calc((100vw - 56px) / 4.7), 150px); }
  .art { order: -1; }
  .sphere { max-width: 340px; }
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .site, .top-in { padding: 0 16px; }
  .hero h1 { font-size: clamp(40px, calc((100vw - 32px) / 4.7), 150px); }
  .sphere { max-width: 260px; }
  .cards { grid-template-columns: minmax(0, 1fr); }
  .cta { margin-top: 56px; }
  .log { padding-top: 40px; }
  .release time { margin-left: 0; width: 100%; }
}

/* ---------- Preferences ---------- */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .top, .card { background: var(--solid); }
}
@media (prefers-reduced-transparency: reduce) {
  .top, .card { background: var(--solid); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .backdrop span { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
}
@media (forced-colors: active) {
  .top, .card { border: 1px solid CanvasText; }
  .get, .more { border: 1px solid LinkText; }
  .backdrop { display: none; }
}
