/* Lissajous Labs: the only stylesheet.
   Change the theme variables below to re-skin every page.
   Colours are the Chronos UI palette (Chronos source/gui/Colours.h). */
:root {
  --bg:          #1a1a1d; /* page background */
  --panel:       #171818; /* cards and buy box */
  --border:      #2a2a2d; /* outlines and rules */
  --text:        #ababab; /* body text */
  --text-bright: #d0d0d0; /* headings */
  --text-muted:  #8e8e8e; /* captions and footer */
  --accent:      #ff5c4a; /* links and buttons (MN3008 red); the logo colour lives in tools/logo.py */
  --code-bg:     color-mix(in srgb, var(--bg), #fff 6%);  /* code block background */
  --code-border: color-mix(in srgb, var(--bg), #fff 10%); /* code block outline */
  --code-number: color-mix(in srgb, var(--bg), #fff 43%); /* code line numbers */
  --footer-bg:   #0e0e10; /* footer background, a step darker than the page */
  --width:       1080px;  /* maximum content width */
  --mono:        ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace; /* code and small footer text */
  /* Form "slips" (signup, contact): a grained card holding a darker form panel. */
  --slip-paper:  #222225;             /* the card, a step lighter than the page */
  --slip-ink:    var(--text);         /* text on the card */
  --slip-panel:  #111113;             /* form panel, a step darker than the page */
  --slip-field:  var(--text-bright);  /* typed text */
  --slip-line:   rgba(255, 255, 255, 0.28); /* dashed field outlines */
  --slip-hint:   #77777c;             /* placeholders */
  --slip-button: var(--accent);       /* button, ticked box, focused field */
  --slip-font:   "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .08 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); /* faint light speckle */
}

@font-face {
  font-family: "Clash Grotesk";
  src: url("fonts/ClashGrotesk-Semibold.ttf") format("truetype");
  font-weight: 600;
  font-display: swap;
}

* { box-sizing: border-box; }

/* A column that's at least the window's height, so main can stretch and keep the
   footer at the bottom of short pages. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
img { display: block; max-width: 100%; height: auto; }

h1, h2, h3, .brand {
  font-family: "Clash Grotesk", sans-serif;
  font-weight: 600;
  line-height: 1.2;
  color: var(--text-bright);
}
h1 { font-size: 2.6rem; margin: 0; }
/* Section headings: small uppercase labels, like the plugin's page tabs. */
h2 { font-size: 0.95rem; letter-spacing: 0.12em; text-transform: uppercase; margin: 2rem 0 0.6rem; }
strong { color: var(--text-bright); }

/* Page frame: header, content and footer share one centred column. */
.site-header, main, .footer-inner { width: 100%; max-width: var(--width); margin: 0 auto; padding: 0 1.25rem; }

.site-header {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  justify-content: space-between;
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}
.brand { display: flex; align-items: center; gap: 0.6rem; font-size: 1.25rem; }
.brand:hover { text-decoration: none; }
.brand img { width: 32px; height: 32px; }
nav { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem 1.25rem; }
nav a { color: var(--text); }
nav a:hover, nav a[aria-current="page"] { color: var(--text-bright); }
.nav-join { padding: 0.2rem 0.8rem; border: 1px solid var(--accent); border-radius: 999px; color: var(--accent); }
.nav-join:hover { background: var(--accent); color: var(--bg); text-decoration: none; }

main { flex-grow: 1; padding-top: 2rem; padding-bottom: 2.5rem; }
.lead { font-size: 1.2rem; max-width: 36em; margin: 0.5rem 0 1.5rem; }
.tagline { margin: 0.25rem 0 1rem; color: var(--accent); font-size: 0.9rem; letter-spacing: 0.12em; text-transform: uppercase; }
.muted { color: var(--text-muted); font-size: 0.9rem; }

/* Home page: one card per plugin. */
.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.5rem; }
.card { display: block; padding: 1rem; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; color: var(--text); }
.card:hover { border-color: var(--accent); text-decoration: none; }
.card h2 { margin: 1rem 0 0.2rem; font-size: 1.3rem; letter-spacing: 0; text-transform: none; }
.card p { margin: 0; color: var(--text-muted); }

/* Product page: screenshot on the left, name and buy box on the right. */
.product { display: grid; grid-template-columns: minmax(0, 560px) 1fr; gap: 2.5rem; align-items: start; }
.shot { width: 100%; border: 1px solid var(--border); border-radius: 8px; }
.buy { margin-top: 1.5rem; padding: 1.5rem; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; }
.price { margin: 0 0 1rem; font-size: 1.6rem; color: var(--text-bright); }
.btn { display: block; margin-bottom: 0.75rem; padding: 0.75rem 1rem; border: 1px solid var(--accent); border-radius: 6px; background: var(--accent); color: var(--bg); font-weight: 600; text-align: center; }
.btn:hover { text-decoration: none; filter: brightness(1.1); }
.btn.outline { background: transparent; color: var(--accent); }

ul { padding-left: 1.2rem; }
li { margin-bottom: 0.4rem; }
li::marker { color: var(--accent); }

/* Footer: a scrolling strip of the name, then the link columns and the copyright line. */
.site-footer { background: var(--footer-bg); color: var(--text-muted); }
.site-footer a { color: var(--text-bright); }
.site-footer a:hover { color: var(--accent); text-decoration: none; }
.site-footer h2 { margin: 0 0 0.75rem; font-size: 1.15rem; letter-spacing: 0; text-transform: none; }

/* The strip holds two identical groups. Both slide left by one group's width and then
   jump back, which looks like one endless line. Each group is at least as wide as the
   window (min-width), so there's never a gap. */
.marquee { display: flex; overflow: hidden; background: var(--bg); border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); color: var(--text-bright); text-transform: uppercase; white-space: nowrap; user-select: none; -webkit-user-select: none; }
.marquee-group { display: flex; flex-shrink: 0; align-items: baseline; justify-content: space-around; min-width: 100%; animation: marquee 40s linear infinite; } /* 40s to scroll one group: lower is faster */
.marquee span, .marquee b { padding: 0.6rem 0.35rem; }
.marquee span { font: 13px/1.2 var(--mono); letter-spacing: 0.04em; color: var(--text); }
.marquee b { font: 600 15px/1.2 "Clash Grotesk", sans-serif; letter-spacing: 0.02em; }
.marquee:hover .marquee-group { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .marquee-group { animation: none; } }

.footer-top { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem 3rem; padding: 2.25rem 0 2rem; }
.footer-links { display: flex; flex-direction: column; align-items: flex-start; gap: 0.45rem; border-right: 1px solid var(--border); }
.footer-links a { margin: 0; font: 600 1.25rem/1.2 "Clash Grotesk", sans-serif; }
.social { display: flex; gap: 1rem; }
.social svg { display: block; width: 22px; height: 22px; }
.contact { display: flex; flex-wrap: wrap; gap: 0.5rem 2rem; }
.contact a, .footer-bottom { font: 12px/1.6 var(--mono); letter-spacing: 0.06em; text-transform: uppercase; }
.footer-bottom { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0.25rem 2rem; padding: 1rem 0 1.25rem; border-top: 1px solid var(--border); }
.footer-bottom p { margin: 0; }

/* Blog: the post list, and long-form posts (equations, diagrams, code, tables). */
.post { max-width: 44rem; margin: 0 auto; }
.post h2, .post-list h2 { font-size: 1.35rem; letter-spacing: 0; text-transform: none; }
.post h3 { font-size: 1.1rem; margin: 2rem 0 0.5rem; }
.post-list article { padding: 1.25rem 0; border-bottom: 1px solid var(--border); }
.post-list h2 { margin: 0.25rem 0 0.5rem; }
.post-list p { margin: 0; }
code, pre { font-family: var(--mono); font-size: 0.88em; }
pre { padding: 1rem; overflow-x: auto; line-height: 1.45; color: var(--text-bright); background: var(--panel); border: 1px solid var(--border); border-radius: 6px; }
pre code { font-size: 1em; }
/* Code blocks in posts copy Substack's: a panel a step lighter than the page, 13px type,
   line numbers, GitHub Dark Default colours and a copy button on hover. code.js adds the
   colours, numbers and button. */
.post pre { margin: 20px 0; padding: 20px; color: #e6edf3; background: var(--code-bg); border-color: var(--code-border); border-radius: 8px; font-size: 13px; line-height: 20px; tab-size: 2; font-feature-settings: "liga" 0; scrollbar-width: thin; scrollbar-color: var(--code-border) transparent; }
.code { position: relative; margin: 20px 0; }
.code pre { margin: 0; }
.code code { counter-reset: line; }
.code .line::before { content: counter(line); counter-increment: line; display: inline-block; width: 20px; margin-right: 24px; text-align: right; color: var(--code-number); font-variant-numeric: tabular-nums; }
.code .k { color: #ff7b72; } /* keywords and operators */
.code .f { color: #d2a8ff; } /* function names */
.code .t { color: #ffa657; } /* type and namespace names */
.code .n { color: #79c0ff; } /* numbers and constants */
.code .s { color: #a5d6ff; } /* strings */
.code .c { color: #8b949e; } /* comments */
.copy { position: absolute; top: 12px; right: 12px; display: grid; place-items: center; width: 32px; height: 32px; padding: 0; color: var(--text-bright); background: var(--code-bg); border: 1px solid var(--code-border); border-radius: 8px; cursor: pointer; opacity: 0; transition: opacity 0.15s; }
.code:hover .copy, .copy:focus-visible { opacity: 1; }
.copy:hover { border-color: var(--code-number); }
@media (hover: none) { .copy { opacity: 1; } }
/* Math is LaTeX, typeset by KaTeX. Equations on their own line sit in <div class="eq">;
   long ones scroll sideways on narrow screens instead of overflowing the page. */
.eq { color: var(--text-bright); }
.katex-display { padding: 0.25rem 0; overflow-x: auto; overflow-y: hidden; }
/* math.js wraps inline math with any text touching it, like "(x" or "x,", so they share a line. */
.math-glue { white-space: nowrap; }
.math-glue .katex { white-space: normal; }
.note { padding-left: 1rem; border-left: 3px solid var(--accent); }
table { display: block; overflow-x: auto; border-collapse: collapse; font-size: 0.95rem; }
th, td { padding: 0.35rem 1.2rem 0.35rem 0; border-bottom: 1px solid var(--border); text-align: left; }
th { color: var(--text-bright); }
figure { margin: 1.5rem 0; }
figcaption { margin-top: 0.5rem; color: var(--text-muted); font-size: 0.9rem; }
figcaption > :first-child { margin-top: 0; } /* a caption split into paragraphs sits as close to its figure as a plain one */
/* Interactive figures (unit-circle.js, prewarp.js): a canvas in a panel like the code
   blocks, then a play button and live readouts. */
.viz { padding: 12px; background: var(--code-bg); border: 1px solid var(--code-border); border-radius: 8px; }
.viz canvas { display: block; width: 100%; cursor: crosshair; touch-action: pan-y; }
.viz-bar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.7rem; margin-top: 10px; color: var(--text-muted); font: 12px/1.4 var(--mono); }
.viz-bar b { display: inline-block; color: var(--text-bright); font-weight: 400; text-align: right; font-variant-numeric: tabular-nums; }
.viz-play { display: grid; place-items: center; width: 32px; height: 32px; padding: 0; color: var(--text-bright); background: var(--bg); border: 1px solid var(--code-border); border-radius: 8px; cursor: pointer; }
.viz-play:hover { border-color: var(--code-number); }
/* The coloured readouts (cos, sin, tan, gain; φ, tan φ) are toggles, dimmed while their part is hidden. */
.viz-toggle { padding: 2px 8px; font: inherit; background: none; border: 1px solid var(--code-border); border-radius: 999px; cursor: pointer; }
.viz-toggle:hover { border-color: var(--code-number); }
.viz-toggle[aria-pressed="false"] { opacity: 0.35; }
/* Caption words coloured to match a figure, as <mark class="purple">gain</mark>, in the
   figures' own colours. Math uses \textcolor instead. A mark, because tools/edit.py's
   editor keeps its class but strips spans and inline styles. */
.post mark { color: inherit; background: none; }
.post mark.blue { color: #79c0ff; }
.post mark.red { color: #ff5c4a; }
.post mark.yellow { color: #e3b341; }
.post mark.purple { color: #d2a8ff; }

/* Home: Chronos up top, then text beside the signup slip. Contact uses the same split. */
.hero { display: grid; grid-template-columns: 1.2fr 1fr; gap: 2.5rem; align-items: center; padding-bottom: 2rem; border-bottom: 1px solid var(--border); }
.hero h1 { font-size: 3.4rem; }
.hero .lead { margin-bottom: 1.25rem; }
.hero-shot img { width: 100%; max-width: 440px; margin-left: auto; border: 1px solid var(--border); border-radius: 8px; }
.actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-bottom: 0.9rem; }
.actions .btn { display: inline-block; margin: 0; }
.split { display: grid; grid-template-columns: 1fr minmax(0, 600px); gap: 0 3rem; align-items: start; }
.split > .slip { margin-top: 2rem; }
.teaser h3 { margin: 0.2rem 0 0.4rem; font-size: 1.25rem; }
.teaser p { margin: 0 0 0.5rem; }
.narrow > * { max-width: 46rem; }
.narrow form .btn { display: inline-block; }
.facts dt { margin-top: 1.1rem; color: var(--text-bright); font: 600 0.75rem/1.2 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; }
.facts dd { margin: 0.3rem 0 0; }

/* Chronos downloads: direct links to the release files. site.js marks the visitor's OS
   with .is-current, which fills that button and moves it to the top. */
.price .muted { font-size: 0.9rem; }
.downloads { display: flex; flex-direction: column; gap: 0.6rem; margin-bottom: 1rem; }
.downloads .btn { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; margin: 0; }
.downloads small { font-weight: 400; font-size: 0.8rem; opacity: 0.8; }
.downloads .is-current { order: -1; background: var(--accent); color: var(--bg); }
.buy .note { margin: 1rem 0 0; font-size: 0.85rem; }
.buy p.muted { margin: 0; }

/* FAQ: one <details> per question. */
.faq { border-top: 1px solid var(--border); }
.faq details { border-bottom: 1px solid var(--border); }
.faq details[open] { padding-bottom: 0.4rem; }
.faq summary { display: flex; justify-content: space-between; gap: 1rem; padding: 0.85rem 0; color: var(--text-bright); font-weight: 600; cursor: pointer; list-style: none; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; color: var(--accent); font-family: var(--mono); }
.faq details[open] > summary::after { content: "\2212"; }
.faq details > p, .faq details > ul { margin-top: 0; }

/* Form slips (signup and contact), modelled on a printed membership form: a grained card,
   a dashed-leader header, and a darker panel with dashed fields and a zig-zag edge. */
.slip { padding: 1.4rem 1.4rem 1.6rem; color: var(--slip-ink); background: var(--grain), var(--slip-paper); border: 1px solid var(--border); font: 13px/1.55 var(--slip-font); }
.slip p { margin: 0 0 1rem; }
.slip a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.slip-head { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1.1rem; text-transform: uppercase; white-space: nowrap; }
.slip-head i { flex: 1; border-top: 1px dashed currentColor; opacity: 0.8; }
.slip-form {
  --zigzag: conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) 50% / 10px 100%;
  display: grid; gap: 0.5rem; margin-top: 1.25rem; padding: 0.4rem 0.4rem calc(0.6rem + 5px);
  color: var(--slip-field); background: var(--grain), var(--slip-panel);
  -webkit-mask: var(--zigzag); mask: var(--zigzag);
}
.slip-form input:not([type="checkbox"]), .slip-form textarea, .slip-form select {
  width: 100%; padding: 0.55rem 0.6rem; color: var(--slip-field); background: transparent;
  border: 1px dashed var(--slip-line); border-radius: 0; font: inherit;
}
/* The arrow's fill is --accent, written out because CSS variables can't reach into a data URL. */
.slip-form select { appearance: none; -webkit-appearance: none; cursor: pointer; background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23ff5c4a'/%3E%3C/svg%3E") no-repeat right 0.7rem center; }
.slip-form option { color: var(--text-bright); background: var(--panel); }
.slip-form textarea { resize: vertical; min-height: 4.5rem; }
.slip-form ::placeholder { color: var(--slip-hint); opacity: 1; }
.slip-form input:focus, .slip-form textarea:focus, .slip-form select:focus { outline: none; border-style: solid; border-color: var(--slip-button); background-color: rgba(255, 255, 255, 0.03); }
.check { display: flex; align-items: center; gap: 0.55rem; padding: 0.2rem 0; cursor: pointer; }
.check input { appearance: none; -webkit-appearance: none; display: grid; place-content: center; flex: none; width: 13px; height: 13px; margin: 0; border: 1px dashed var(--slip-line); cursor: pointer; }
.check input:checked::before { content: ""; width: 7px; height: 7px; background: var(--slip-button); }
.check input:focus-visible { outline: 1px solid var(--slip-button); outline-offset: 2px; }
.slip-form .note { display: none; }
.slip-form:has(input[name="beta"]:checked) .note { display: block; } /* ticking the beta box asks for the note */
.slip-form button { margin-top: 0.15rem; padding: 0.7rem; color: var(--bg); background: var(--slip-button); border: 0; border-radius: 0; font: 600 13px/1.55 var(--slip-font); letter-spacing: 0.08em; text-transform: uppercase; cursor: pointer; }
.slip-form button:hover { filter: brightness(1.1); }
.slip-form button:disabled { opacity: 0.6; cursor: progress; }
.slip-form .fine { margin: 0.2rem 0 0; color: var(--text-muted); font-size: 11px; }
.form-status { margin: 0; padding: 0.5rem 0.6rem; border: 1px dashed currentColor; font-size: 12px; }
.form-status:empty { display: none; }
.form-status.ok { color: var(--text-bright); }
.form-status.error { color: #ff8a7a; }
.sr-only, .hp { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Intro animation (loader.js): a small ASCII scope trace on the page colour, no box. */
.intro { position: fixed; inset: 0; z-index: 9999; display: grid; place-content: center; justify-items: center; gap: 1rem; background: var(--bg); transition: opacity 0.35s; }
.intro.out { opacity: 0; }
.intro-art { margin: 0; color: var(--accent); font: 11px/1 var(--mono); white-space: pre; user-select: none; -webkit-user-select: none; }
.intro-art .t1 { opacity: 0.14; }
.intro-art .t2 { opacity: 0.32; }
.intro-art .t3 { opacity: 0.6; }
.intro-art .t5 { color: var(--text-bright); } /* the beam head */
.intro-cap { padding-left: 0.5em; color: var(--text-muted); font: 10px/1 var(--mono); letter-spacing: 0.5em; text-transform: uppercase; }

/* Narrow screens (phones): stack the columns, drop code line numbers (as Substack
   does), and put the footer links above the Social and Contact columns. */
@media (max-width: 760px) {
  .product, .hero, .split { grid-template-columns: 1fr; }
  .hero-shot img { margin: 0; }
  h1, .hero h1 { font-size: 2.2rem; }
  .slip { padding: 1.1rem 1rem 1.3rem; }
  .code .line::before { display: none; }
  .footer-top { grid-template-columns: 1fr 1fr; padding: 2rem 0 1.75rem; }
  .footer-links { grid-column: 1 / -1; padding-bottom: 1.5rem; border-right: 0; border-bottom: 1px solid var(--border); }
}
