/* heddled.com — the same restraint as the console: density over decoration,
   one stylesheet, no build step, and it reads in whichever theme you use. */

:root {
  --bg: #0f1115;
  --panel: #161920;
  --panel-2: #1c202a;
  --line: #262b36;
  --text: #d7dce5;
  /* #7d8698 measures 4.45:1 on --panel-2, just under the 4.5 floor —
     the console carries the same corrected value for the same reason. */
  --muted: #80899b;
  --accent: #7aa2f7;
  --ok: #7ac07a;
  --warn: #e0af68;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --measure: 68ch;
  /* A primary button puts ink on --accent, and which ink reads depends on the
     theme: dark ink on the light theme's #2f5fd0 measures 3.30:1, under the
     4.5:1 floor. The console carries the same pair for the same reason. */
  --on-accent: #0f1115;
  /* The madder thread, lightened for a dark ground: #B4442E measures
     3.19:1 on the nav panel, which is scraping the 3:1 graphics floor. */
  --brand-thread: #D2634A;
}

@media (prefers-color-scheme: light) {
  :root {
    --bg: #f7f8fa; --panel: #fff; --panel-2: #f0f2f6; --line: #dfe3ea;
    --text: #1c2028; --muted: #5d6577; --accent: #2f5fd0;
    --ok: #2f7d32; --warn: #8a5d00;
    --on-accent: #ffffff;
    --brand-thread: #B4442E;   /* as designed, on paper */
  }
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 16px/1.65 var(--sans);
  -webkit-text-size-adjust: 100%;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

.skip-link {
  position: absolute; left: -9999px; top: 0; background: var(--panel);
  border: 1px solid var(--accent); padding: 10px 14px; border-radius: 0 0 6px 0;
}
.skip-link:focus { left: 0; z-index: 50; }

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

.nav {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 22px;
  padding: 0 20px; height: 52px;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--line);
}
.nav .brand { font-weight: 600; color: var(--text); font-size: 17px; }
.nav a { color: var(--muted); font-size: 14.5px; display: inline-flex;
         align-items: center; min-height: 44px; }
.nav a:hover, .nav a[aria-current="page"] { color: var(--text); text-decoration: none; }
.nav a[aria-current="page"] { box-shadow: inset 0 -2px 0 var(--accent); }
.nav .spacer { flex: 1; }

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

main { max-width: 900px; margin: 0 auto; padding: 20px 20px 72px; }
/* The nav is sticky, so a heading jumped to from the contents would otherwise
   land underneath it. */
:target, h2[id], h3[id] { scroll-margin-top: 72px; }
section { margin: 44px 0; }
h1 { font-size: clamp(28px, 5vw, 40px); line-height: 1.2; margin: 0 0 12px;
     letter-spacing: -.02em; }
h2 { font-size: 21px; margin: 40px 0 10px; letter-spacing: -.01em; }
h3 { font-size: 16px; margin: 26px 0 6px; }
p, li { max-width: var(--measure); }
.lede { font-size: 18.5px; color: var(--muted); max-width: var(--measure); }
.muted { color: var(--muted); }
.small { font-size: 14px; }

/* ----------------------------------------------------------------- hero */

/* The hero is a section, so it also picked up the 44px section margin. */
.hero { padding: 18px 0 0; margin: 0 0 26px; }
/* Wide enough that the headline lands in two lines rather than four,
   which is most of what was pushing the evidence off the screen. */
.hero h1 { max-width: 26ch; }
.hero .lede { margin: 0 0 22px; }
.actions { display: flex; gap: 10px; flex-wrap: wrap; margin: 0; }

/* Buttons and the one command that gets you running, on one line where there
   is room: the two things a first visit needs, without spending a screenful. */
.hero-start { display: flex; gap: 18px; align-items: center; flex-wrap: wrap; }
.hero-start > :is(pre, .copy-wrap) { margin: 0; flex: 1 1 22rem; min-width: 0; }
.hero-start .copy-wrap pre { margin: 0; }
pre.tight { padding: 10px 14px; font-size: 13px; line-height: 1.55; }

/* Facts worth knowing before scrolling, as a line rather than a section. */
.facts {
  list-style: none; display: flex; flex-wrap: wrap; gap: 8px 20px;
  margin: 22px 0 0; padding: 0; color: var(--muted); font-size: 14px;
}
.facts li { max-width: none; display: flex; align-items: center; gap: 8px; }
.facts li::before {
  content: ""; width: 5px; height: 5px; border-radius: 50%;
  background: var(--ok); flex: none;
}
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; padding: 0 18px; border-radius: 7px; font-size: 15px;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  font-family: inherit; cursor: pointer;
}
.btn:hover { border-color: var(--accent); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent);
               color: var(--on-accent); font-weight: 600; }
.btn.primary:hover { filter: brightness(1.08); }

/* ----------------------------------------------------------------- code */

pre, code, kbd { font-family: var(--mono); }
pre {
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px;
  padding: 14px 16px; overflow-x: auto; font-size: 13.5px; line-height: 1.6;
  margin: 16px 0;
}
p code, li code, td code {
  background: var(--panel-2); border: 1px solid var(--line); border-radius: 4px;
  padding: 1px 5px; font-size: .88em;
}
pre .c { color: var(--muted); }         /* a comment */
pre .k { color: var(--accent); }        /* a key */
pre .s { color: var(--ok); }            /* a string */

/* ---------------------------------------------------------------- cards */

.cards { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(238px, 1fr)); }
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: 10px; padding: 18px 20px;
}
.card h3 { margin-top: 0; }
.card p { margin-bottom: 0; color: var(--muted); font-size: 15px; }

/* ---------------------------------------------------------------- table */

table { border-collapse: collapse; width: 100%; margin: 16px 0; font-size: 15px; }
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--line);
         vertical-align: top; }
th { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
     color: var(--muted); font-weight: 600; }
.table-scroll { overflow-x: auto; }

/* ------------------------------------------------------------ callouts */

.note {
  border-left: 3px solid var(--accent); background: var(--panel);
  padding: 14px 18px; border-radius: 0 8px 8px 0; margin: 22px 0;
}
.note p { margin: 0; }
.note.warn { border-left-color: var(--warn); }

/* ------------------------------------------------------------- doc nav */

/* Grid items default to min-width:auto, so a wide <pre> widens the column even
   though the <pre> scrolls by itself. */
.doc { display: grid; grid-template-columns: 210px 1fr; gap: 40px; align-items: start; }
.doc > * { min-width: 0; }
.toc { position: sticky; top: 76px; font-size: 14.5px; }
.toc ul { list-style: none; margin: 8px 0 0; padding: 0; }
.toc li { margin: 0; }
.toc a { display: block; padding: 6px 0; color: var(--muted); }
.toc a:hover { color: var(--text); text-decoration: none; }
.toc strong { font-size: 12px; text-transform: uppercase; letter-spacing: .06em;
              color: var(--muted); }

/* ---------------------------------------------------------------- foot */

footer {
  border-top: 1px solid var(--line); margin-top: 72px; padding: 28px 20px 48px;
  color: var(--muted); font-size: 14px;
}
footer .inner { max-width: 900px; margin: 0 auto; display: flex; gap: 18px;
                flex-wrap: wrap; align-items: center; }
/* Footer and table-of-contents links are navigation, not prose, so they get a
   real target rather than the 23px a bare line of text happens to occupy. */
footer a { display: inline-flex; align-items: center; min-height: 28px; }
footer .spacer { flex: 1; }

/* -------------------------------------------------------------- phones */

/* The contents is a plain list on a wide screen: no control, no summary. */
.toc-panel > summary { display: none; }

@media (max-width: 760px) {
  .doc { grid-template-columns: 1fr; gap: 8px; }
  .toc { position: static; border-bottom: 1px solid var(--line); padding-bottom: 12px; }
  .toc ul { display: flex; flex-wrap: wrap; gap: 4px 16px; }
  .toc-panel > summary {
    display: flex; align-items: center; gap: 8px; cursor: pointer;
    list-style: none; min-height: var(--tap, 44px); font-size: 14.5px;
    color: var(--text); font-weight: 600;
    border: 1px solid var(--line); border-radius: 8px; padding: 0 14px;
    background: var(--panel); margin: 10px 0 4px;
  }
  .toc-panel > summary::-webkit-details-marker { display: none; }
  .toc-panel > summary::after { content: "▾"; margin-left: auto; font-size: 11px;
                                color: var(--muted); }
  .toc-panel:not([open]) > summary::after { content: "▸"; }
  .toc-panel > summary:hover { border-color: var(--accent); }
  .toc-panel[open] > summary { margin-bottom: 10px; }
  .hero { padding: 12px 0 0; }
  /* The install line and the buttons stack rather than sharing a row, and the
     buttons take a share of the width instead of 100% each — two full-width
     children of a wrapping flex row size the row from their content, which
     pushed the page sideways. */
  .hero-start { flex-direction: column; align-items: stretch; }
  .hero-start > :is(pre, .copy-wrap) { flex: 0 0 auto; width: 100%; min-width: 0; }
  .actions { width: 100%; }
  .actions .btn { flex: 1 1 auto; width: auto; }
  .facts { gap: 6px 16px; font-size: 13.5px; }
  .nav { gap: 16px; overflow-x: auto; scrollbar-width: none; }
  .nav::-webkit-scrollbar { display: none; }
  .nav a, .nav .brand { flex: 0 0 auto; white-space: nowrap; }
}

/* ---------------------------------------------------------- screenshots */

.shot { margin: 28px 0 0; }
.shot-section { margin-top: 28px; }
.shot picture, .shot img {
  display: block; width: 100%; height: auto; border-radius: 10px;
  border: 1px solid var(--line);
}
.shot figcaption {
  color: var(--muted); font-size: 14px; margin-top: 10px; max-width: var(--measure);
}

/* ------------------------------------------------------- copy a command */

/* Every block on these pages is something you are meant to paste somewhere
   else, so selecting it by hand is work the page can do for you. */
/* The copy button wraps every <pre> in a div, which then becomes the flex or
   grid child in its place — so it has to carry the same layout role, or the
   sizing rules below silently stop applying to anything. */
.copy-wrap { position: relative; min-width: 0; }
.copy-btn {
  position: absolute; top: 8px; right: 8px;
  min-height: 28px; padding: 0 10px; border-radius: 5px;
  font: 12px/1 var(--sans); color: var(--muted);
  background: var(--panel-2); border: 1px solid var(--line); cursor: pointer;
  opacity: 0; transition: opacity .12s ease;
}
.copy-wrap:hover .copy-btn, .copy-btn:focus-visible { opacity: 1; }
.copy-btn:hover { color: var(--text); border-color: var(--accent); }
.copy-btn[data-copied] { opacity: 1; color: var(--ok); border-color: var(--ok); }
@media (hover: none) { .copy-btn { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .copy-btn { transition: none; } }

/* ------------------------------------------------- where you are, in the docs */

.toc a.here { color: var(--text); box-shadow: inset 2px 0 0 var(--accent); padding-left: 8px; }
@media (max-width: 760px) {
  .toc a.here { box-shadow: none; text-decoration: underline;
                text-underline-offset: 3px; padding-left: 0; }
}

/* The opening screenshot runs wider than the text column — it is the evidence,
   and a 900px-wide console shrunk into a text measure proves nothing. */
.shot.bleed {
  max-width: 1180px; margin-left: auto; margin-right: auto;
  padding: 0 20px; box-sizing: content-box;
}
.shot.bleed figcaption { max-width: var(--measure); }

/* The file and the form that edits it, next to each other where there is room:
   the claim is that they are the same thing, so they should be seen together. */
.side-by-side {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px; align-items: start; margin: 18px 0 10px;
}
.side-by-side > * { min-width: 0; margin: 0; }
.side-by-side :is(pre, .copy-wrap) { height: 100%; }
.side-by-side pre { font-size: 12.5px; }
@media (max-width: 860px) {
  .side-by-side { grid-template-columns: 1fr; }
}

.cards { gap: 14px; }
.card p { font-size: 14.5px; line-height: 1.55; }
.start :is(pre, .copy-wrap) { max-width: var(--measure); }

details.reveal { margin: 16px 0; }
details.reveal > summary {
  cursor: pointer; list-style: none; color: var(--accent); font-size: 14.5px;
  display: inline-flex; align-items: center; gap: 8px; min-height: 32px;
}
details.reveal > summary::-webkit-details-marker { display: none; }
details.reveal > summary::before { content: "▸"; font-size: 10px; }
details.reveal[open] > summary::before { content: "▾"; }
details.reveal > summary:hover { text-decoration: underline; }


/* =========================================================================
   The landing page, on one screen.
   No scrolling on a desktop: the pitch, the evidence and what it can do are
   all in the first view. Below the breakpoint — phones, short windows — it
   goes back to being an ordinary scrolling page rather than a squeeze.
   ========================================================================= */

@media (min-width: 1000px) and (min-height: 720px) {
  body.landing { height: 100dvh; overflow: hidden; display: flex; flex-direction: column; }
  body.landing main.screen {
    flex: 1; min-height: 0; display: flex; flex-direction: column;
    justify-content: center; gap: clamp(14px, 2.2vh, 30px);
    max-width: 1240px; padding: clamp(10px, 2vh, 26px) 24px;
  }
  body.landing footer { margin-top: 0; padding: 12px 24px; flex: none; }
}

.pitch {
  display: grid; grid-template-columns: minmax(0, 31rem) minmax(0, 1fr);
  gap: clamp(20px, 3vw, 44px); align-items: center;
}
/* Grid items default to min-width:auto, so the install command — the widest
   thing in this column — sets the column width and pushes the page sideways.
   The <pre> scrolls on its own; it does not need the room. */
.pitch-text { min-width: 0; }
.pitch-text > * { max-width: min(31rem, 100%); }
.pitch-text > :is(pre, .copy-wrap) { min-width: 0; max-width: 100%; }
.pitch h1 {
  font-size: clamp(26px, 2.6vw + .4rem, 38px); line-height: 1.15;
  margin: 0 0 10px; letter-spacing: -.02em;
}
.pitch .lede { font-size: clamp(15px, .5vw + .8rem, 17px); margin: 0 0 16px; }
.pitch .actions { margin: 0 0 12px; }
/* The only tight block in the hero lives inside .install, which carries the
   spacing for the pair of them. */
.pitch pre.tight { margin: 0; font-size: 12.5px; }
.pitch .facts { margin: 0; }

.pitch-shot { margin: 0; }
.pitch-shot img { border-radius: 8px; }
.pitch-shot figcaption { margin-top: 8px; font-size: 13px; }

/* What it can do, as scannable lines rather than a wall of cards. */
.capabilities {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: 10px clamp(18px, 2.4vw, 34px);
  border-top: 1px solid var(--line); padding-top: clamp(12px, 2vh, 20px);
}
.capabilities li {
  max-width: none; font-size: 14px; line-height: 1.5; color: var(--muted);
}
.capabilities b { color: var(--text); font-weight: 600; }

.more { max-width: none; margin: 0; font-size: 13.5px; }
.more .muted { font-size: 13px; }

@media (max-width: 999px), (max-height: 719px) {
  .pitch { grid-template-columns: 1fr; gap: 24px; }
  .pitch-text > * { max-width: min(var(--measure), 100%); }
  main.screen { display: flex; flex-direction: column; gap: 32px; }
}

/* ------------------------------------------------------------- carousel */

/* A scroll-snapping strip first, controls second: it can be swiped, scrolled
   or tabbed through with no JavaScript at all. The buttons and dots below are
   added by site.js on top of something that already works. */
.carousel { position: relative; min-width: 0; }
.slides {
  display: flex; overflow-x: auto; scroll-snap-type: x mandatory;
  gap: 0; scrollbar-width: none; border-radius: 8px;
}
.slides::-webkit-scrollbar { display: none; }
.slide {
  flex: 0 0 100%; scroll-snap-align: center; margin: 0; min-width: 0;
}
.slide img {
  display: block; width: 100%; height: auto; border-radius: 8px;
  border: 1px solid var(--line);
}
.slide figcaption {
  color: var(--muted); font-size: 13px; margin-top: 8px;
  /* Two lines' worth, always: a shorter caption must not make the page jump
     as you move between slides. */
  min-height: 2.6em;
}

.carousel-controls {
  display: flex; align-items: center; gap: 10px; margin-top: 4px;
}
.carousel-btn {
  width: 34px; height: 34px; border-radius: 50%; flex: none;
  border: 1px solid var(--line); background: var(--panel-2); color: var(--text);
  cursor: pointer; font: 15px/1 var(--sans);
}
.carousel-btn:hover:not(:disabled) { border-color: var(--accent); }
.carousel-btn:disabled { opacity: .35; cursor: default; }
.carousel-dots { display: flex; gap: 6px; }
.carousel-dot {
  width: 26px; height: 24px; padding: 0; border: 0; background: none;
  cursor: pointer; display: grid; place-items: center;
}
.carousel-dot::before {
  content: ""; width: 100%; height: 3px; border-radius: 2px;
  background: var(--line);
}
.carousel-dot[aria-current="true"]::before { background: var(--accent); }
.carousel-dot:hover::before { background: var(--muted); }
.carousel-count { color: var(--muted); font-size: 12.5px; margin-left: auto; }

@media (prefers-reduced-motion: reduce) { .slides { scroll-behavior: auto; } }

/* ------------------------------------------------------------------ brand */

/* The mark takes the surrounding text colour, so one drawing serves both
   themes; the single madder thread is fixed, which is the point of it. */
.brand-mark { flex: none; }
.nav .brand { display: inline-flex; align-items: center; gap: 8px; }
.nav .brand .brand-mark { width: 20px; height: 20px; }
.foot-brand { display: inline-flex; align-items: center; gap: 7px; }
.foot-brand .brand-mark { width: 15px; height: 15px; }

/* --------------------------------------------------------- doc screenshots */

.doc-shot { margin: 20px 0 26px; }
.doc-shot img {
  display: block; width: 100%; height: auto; border-radius: 8px;
  border: 1px solid var(--line);
}
.doc-shot figcaption {
  color: var(--muted); font-size: 13.5px; margin-top: 9px;
  max-width: var(--measure);
}
.toc strong + ul { margin-bottom: 14px; }

/* ------------------------------------------------------------- category */

.eyebrow {
  font-size: 12.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--muted); margin: 0 0 10px; font-weight: 600;
}


/* ------------------------------------------------------------ screenshots */

/* A frame, so a screenshot reads as an application rather than a picture.
   Deliberately slim — the page has to stay on one screen. */
.frame {
  border: 1px solid var(--line); border-radius: 9px; overflow: hidden;
  background: var(--panel);
}
.frame-bar {
  display: flex; align-items: center; gap: 5px;
  padding: 0 10px; height: 26px;
  background: var(--panel-2); border-bottom: 1px solid var(--line);
}
.frame-bar span {
  width: 8px; height: 8px; border-radius: 50%; background: var(--line); flex: none;
}
.frame-bar em {
  font: 11px var(--mono); color: var(--muted); font-style: normal;
  margin-left: 8px; opacity: .8;
}
.frame img { display: block; width: 100%; height: auto; border: 0; border-radius: 0; }


/* The next slide peeks in, so it is obvious the strip moves. */
.slides { gap: 10px; scroll-padding-left: 0; }
.slide { flex: 0 0 calc(100% - 26px); scroll-snap-align: start; }

/* The neighbour peeks to show the strip moves — but a half-visible caption
   reads as broken text, so the peek is dimmed and only the current slide's
   caption is shown. `inert` is set by the script on every slide but the
   current one; without JavaScript all slides simply stay legible. */
.slide { transition: opacity .18s ease; }
.slide[inert] { opacity: .32; }
.slide[inert] figcaption { visibility: hidden; }
@media (prefers-reduced-motion: reduce) { .slide { transition: none; } }

/* --------------------------------------------------------- rotating headline */

/* All three phrases share one grid cell, so the tallest reserves the space and
   nothing below shifts when it changes. */
.rotator { display: grid; }
.rot {
  grid-area: 1 / 1; opacity: 0; transition: opacity .6s ease;
  pointer-events: none;
}
.rot.is-on { opacity: 1; }

/* WCAG 2.2.2 wants a way to pause anything that moves or updates by itself for
   more than five seconds. There is no visible stop button — it read as clutter
   directly under the headline — so the pause mechanism is the hero itself:
   pointing at it or tabbing into it holds the phrase, and that is exactly where
   both pointer and keyboard users are while reading it. Reduced-motion turns
   the whole thing off, and a hidden tab stops the timer. */

@media (prefers-reduced-motion: reduce) {
  .rot { transition: none; }
}

/* -------------------------------------------------------- install command */

.install { margin: 0 0 12px; min-width: 0; }

.os-switch { display: flex; gap: 2px; margin-bottom: 6px; }
.os-switch button {
  background: none; border: 0; border-bottom: 2px solid transparent;
  padding: 5px 10px; min-height: 30px;
  color: var(--muted); font: inherit; font-size: 12.5px; cursor: pointer;
  border-radius: 4px 4px 0 0;
}
.os-switch button:hover { color: var(--text); }
.os-switch button[aria-selected="true"] {
  color: var(--text); border-bottom-color: var(--accent);
}
.os-switch button:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px;
}

.os-pane { min-width: 0; }
.os-pane > :is(pre, .copy-wrap) { margin: 0; min-width: 0; max-width: 100%; }

/* On a phone the command runs off the right edge, and a one-line command you
   cannot read is worse than one on two lines. Wrap it there only. */
@media (max-width: 760px) {
  .os-pane pre { white-space: pre-wrap; overflow-wrap: anywhere; }
}
[hidden] { display: none !important; }

/* A short screen: the rotating headline reserves the tallest phrase's height,
   which is enough to tip 1152x720 over. Buy it back from the spacing. */
@media (min-width: 1000px) and (max-height: 790px) {
  .pitch h1 { font-size: clamp(24px, 2.3vw + .3rem, 34px); }
  .pitch .lede { margin-bottom: 12px; }
  .install { margin-bottom: 8px; }
  .os-switch { margin-bottom: 4px; }
}
