/* drawbridge — site stylesheet. No framework, no webfonts, no third-party
   requests: the site keeps the same "nothing leaves the device" spirit as
   the app it describes. */

/* Light-green theme. The variable names are historical (--cream, --wood,
   --slate): they name the *role* a colour plays, not its hue, so the palette
   can be re-tinted here without touching a single rule below. */

:root {
  --cream: #eef3e6;          /* page background */
  --paper: #f7faf2;          /* cards, raised surfaces */
  --ink: #1f2a20;            /* body text */
  --ink-soft: #556354;       /* secondary text */
  --slate: #4a6b4d;          /* step markers, accents */
  --slate-dark: #2f4a33;     /* links */
  --wood: #4c7a4f;           /* primary button */
  --wood-dark: #3a5f3d;      /* primary button, hover */
  --olive: #6d8a4a;          /* "works well" accent */
  --line: #d5e2c9;           /* borders, rules */
  --rust: #a2544a;           /* warnings, "coming soon" */
  --radius: 10px;
  --max: 920px;
  --shadow: 0 1px 2px rgba(31, 42, 32, 0.06), 0 8px 24px rgba(31, 42, 32, 0.07);
}

@media (prefers-color-scheme: dark) {
  :root {
    --cream: #161b16;
    --paper: #1c221c;
    --ink: #e4ecdf;
    --ink-soft: #a7b3a2;
    --slate: #9dbb9f;
    --slate-dark: #c6dcc5;
    --wood: #8fc192;
    --wood-dark: #a9d5ab;
    --olive: #a2b87a;
    --line: #2e372e;
    --rust: #d98a7c;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 8px 24px rgba(0, 0, 0, 0.3);
  }
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  line-height: 1.6;
  font-size: 17px;
}

img { max-width: 100%; display: block; }

a { color: var(--slate-dark); text-decoration-color: var(--line); text-underline-offset: 3px; }
a:hover { text-decoration-color: currentColor; }

h1, h2, h3, h4 { line-height: 1.2; letter-spacing: -0.01em; color: var(--ink); }
h1 { font-size: clamp(2rem, 5vw, 2.9rem); margin: 0 0 0.5rem; }
h2 { font-size: 1.6rem; margin: 2.4rem 0 0.9rem; }
h3 { font-size: 1.15rem; margin: 1.8rem 0 0.5rem; }
/* Fourth level exists only on the Q&A page, where "how do I switch it off"
   has three answers under it. Sized close to body text on purpose: it is a
   label for the paragraph below it, not another section. */
h4 { font-size: 1rem; margin: 1.4rem 0 0.4rem; color: var(--ink-soft); }

p { margin: 0 0 1rem; }
.lede { font-size: 1.15rem; color: var(--ink-soft); }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 1.5rem; }
.wrap--wide { max-width: 1100px; }

/* header */

.site-header {
  border-bottom: 1px solid var(--line);
  padding: 1.1rem 0;
}
.site-header .wrap { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.brand { display: flex; align-items: center; gap: 0.6rem; font-weight: 600; font-size: 1.15rem; color: var(--ink); text-decoration: none; }
.brand img { width: 28px; height: 28px; border-radius: 6px; }
.header-right { display: flex; align-items: center; gap: 1.4rem; flex-wrap: wrap; }
.nav { display: flex; gap: 1.4rem; font-size: 0.95rem; flex-wrap: wrap; }
.nav a { color: var(--ink-soft); text-decoration: none; }
.nav a:hover, .nav a[aria-current="page"] { color: var(--ink); }
.lang-switch { display: flex; gap: 0.3rem; font-size: 0.82rem; }
.lang-switch a { color: var(--ink-soft); text-decoration: none; padding: 0.15rem 0.4rem; border-radius: 4px; }
.lang-switch a:hover { color: var(--ink); }
.lang-switch a.lang-current { color: var(--ink); background: var(--paper); border: 1px solid var(--line); font-weight: 600; }

/* Package ids and shell commands. The blocklist page names about a hundred and
   thirty Android packages, and in a proportional face `com.a3.sgt` reads as
   prose rather than as an identifier. Wraps rather than overflows: some of
   these are long and they sit in narrow table cells. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.88em;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 4px;
  padding: 0.05em 0.35em;
  overflow-wrap: anywhere;
}

/* hero */

.hero { padding: 2.5rem 0 2.5rem; }
.hero-art {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: var(--shadow);
  margin: 0 0 1.6rem;
  /* So the title can be sized against the *picture* rather than the viewport:
     the art is capped at --max while the window is not, and text set in vw
     kept outgrowing the sky it sits in. */
  container-type: inline-size;
}
.hero-art img { display: block; width: 100%; height: auto; }

/* The title sits on the picture, in the top-right sky.
   Deliberately not themed. The same illustration is served to light and dark
   readers alike -- it is a sunset, warm enough for either -- so the text over
   it is keyed to the sky it lies on and not to --ink, which would turn pale
   in dark mode and vanish. */
.hero-art-title {
  position: absolute;
  top: 5%;
  right: 4.5%;
  max-width: 55%;
  text-align: right;
  color: #2a2118;
  text-shadow: 0 1px 10px rgba(255, 244, 224, 0.75);
}
.hero-art-title h1 {
  margin: 0;
  color: inherit;
  font-size: clamp(1.25rem, 5.2vw, 2.6rem);   /* fallback where cqw is unknown */
  font-size: max(1.25rem, 5.2cqw);
  letter-spacing: -0.015em;
}
.hero-art-title p {
  margin: 0.15em 0 0;
  font-weight: 600;
  font-size: clamp(0.72rem, 2.4vw, 1.15rem);
  font-size: max(0.72rem, 2.4cqw);
  line-height: 1.25;
  opacity: 0.9;
  /* Two even lines rather than a lone trailing word, where it wraps. */
  text-wrap: balance;
}

/* Below about 700px the picture is small enough that text scaled purely to it
   stops being readable -- 8px at a phone width -- so both sizes have a floor
   above, and the block is given more of the sky to spend it in. The tagline
   wraps to two lines there, which the far-right sky has room for. */
@media (max-width: 700px) {
  .hero-art-title { max-width: 68%; top: 4%; }
}
/* The people this is for, in their own words: five one-liners between the hero
   art and the explanation. A grid rather than a column, because five stacked
   cards would push the paragraph that explains the project off the screen. */
.voices-lead { margin-bottom: 0.6rem; font-weight: 600; }
.voices {
  list-style: none;
  margin: 0 0 1.6rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 0.8rem;
}
.voices li {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 3px solid var(--slate);
  border-radius: var(--radius);
  padding: 0.85rem 1rem;
  color: var(--ink-soft);
}
.voice-quote { font-style: italic; }

/* The answer to each quote, behind a "How?" the reader opens.
 *
 * <details> rather than the radio trick the pager and the install picker use:
 * this is the element for exactly this, several can be open at once (which
 * radios cannot do), and it needs no script — the same constraint as the rest
 * of the site. The marker is dropped and the summary drawn as a small button,
 * because a bare disclosure triangle beside a quotation reads as decoration.
 */
.voice-how { margin-top: 0.7rem; font-style: normal; }
.voice-how > summary {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--cream);
  color: var(--slate-dark);
  font-size: 0.82rem;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.voice-how > summary::-webkit-details-marker { display: none; }
/* A plus and a minus rather than a triangle: the small triangles are not in
   every system font, and the one this fell back to rendered as a dot. */
.voice-how > summary::after { content: "+"; font-size: 1em; line-height: 1; }
.voice-how[open] > summary::after { content: "\2212"; }
.voice-how > summary:hover { border-color: var(--slate); }
.voice-how > p {
  margin: 0.7rem 0 0;
  font-size: 0.9rem;
  line-height: 1.55;
  color: var(--ink);
}

/* buttons */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.75rem 1.3rem;
  border-radius: 8px;
  font-weight: 600;
  font-size: 1rem;
  text-decoration: none;
  border: 1px solid transparent;
  cursor: pointer;
}
.btn--primary { background: var(--wood); color: #fff; }
.btn--primary:hover { background: var(--wood-dark); }
@media (prefers-color-scheme: dark) { .btn--primary { color: #201914; } }
.btn--ghost { background: transparent; border-color: var(--line); color: var(--ink); }
.btn--ghost:hover { border-color: var(--ink-soft); }
.btn--disabled {
  background: var(--paper);
  border: 1px dashed var(--line);
  color: var(--ink-soft);
  cursor: default;
}
.btn-row { display: flex; gap: 0.8rem; flex-wrap: wrap; margin: 1.2rem 0; }

/* sections / cards */

.section { padding: 2.5rem 0; }
.section--alt { background: var(--paper); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }

/* "How it works": prose in one column, three screens of the running system
   crossfading in the other.
 *
 * The section had prose and nothing else for a few hours on 2026-08-24, after
 * the photographs moved to "On what devices?" — and the explainer under it
 * opens on the *policy*, so a reader met six pages of settings without having
 * seen the thing once. These three are the overview that was missing: open,
 * locked, and a site refused.
 */
.how-grid { display: grid; grid-template-columns: 1.35fr 1fr; gap: 2rem; align-items: center; margin-top: 1.2rem; }
@media (max-width: 780px) {
  .how-grid { grid-template-columns: 1fr; gap: 1.4rem; }
}

/* Six seconds held, one second of crossfade, three slides to an 18s cycle.
   Negative delays put all three mid-cycle on the first frame, so slide one is
   simply the one showing; the first figure stays in the flow and gives the
   block its height, and the other two are laid over it. All three are the same
   1080x2400 screen, so they measure the same. */
.how-slides { position: relative; max-width: 300px; margin: 0 auto; }
.how-slides figure { margin: 0; opacity: 0; animation: how-slide-fade 18s infinite; }
.how-slides figure:first-child { position: relative; }
.how-slides figure + figure { position: absolute; top: 0; left: 0; right: 0; }
.how-slides figure:nth-child(2) { animation-delay: -12s; }
.how-slides figure:nth-child(3) { animation-delay: -6s; }
.how-slides img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

@keyframes how-slide-fade {
  0%     { opacity: 1; }
  27.78% { opacity: 1; }   /* 5s  */
  33.33% { opacity: 0; }   /* 6s  */
  94.44% { opacity: 0; }   /* 17s */
  100%   { opacity: 1; }   /* 18s */
}

/* Asked not to move things: all three at once, in a column. Nothing is lost —
   the slideshow's whole content is three screenshots, and so is this. */
@media (prefers-reduced-motion: reduce) {
  .how-slides figure,
  .how-slides figure + figure { position: relative; animation: none; opacity: 1; }
  .how-slides figure + figure { margin-top: 1.2rem; }
}

/* Three photographs, one under the other.
 *
 * They crossfaded in one frame until 2026-08-24. The column beside them is
 * three paragraphs, a list and two buttons tall, so the room for all three was
 * always there — and a reader can compare three pictures they can see at once,
 * which is the whole reason there are three of them.
 *
 * 280px rather than the 340 the illustration had: three of those plus captions
 * ran taller than the text beside them, and the grid centres whichever column
 * is shorter, so the pair reads as one block only while the two are close.
 */
.phone-photos { max-width: 280px; margin: 0 auto; display: grid; gap: 1.4rem; }
.phone-photos figure { margin: 0; }
.phone-photos img { display: block; width: 100%; height: auto; border-radius: var(--radius); box-shadow: var(--shadow); }
.phone-photos figcaption {
  margin-top: 0.45rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--ink-soft);
}

/* The photographs sit under "On what devices?" now, three across, under the
   two cards. That is the question they answer — three phones, three
   manufacturers, one of them not even running an OEM's Android — and it is why
   they left "how it works", where they were decoration beside text about
   installing. Across rather than stacked because the page is full width here
   and a column of three would run twice the height of everything beside it. */
.phone-photos--row {
  max-width: none;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.2rem;
  margin: 1.4rem 0 0;
}
@media (max-width: 780px) {
  .phone-photos--row { grid-template-columns: 1fr; max-width: 280px; }
}

.steps { list-style: none; margin: 1.2rem 0; padding: 0; display: grid; gap: 0.9rem; }
.steps li {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
}
.steps .num {
  flex: 0 0 auto;
  width: 1.7rem; height: 1.7rem;
  border-radius: 50%;
  background: var(--slate);
  color: #fff;
  display: flex; align-items: center; justify-content: center;
  font-size: 0.85rem; font-weight: 700;
}
@media (prefers-color-scheme: dark) { .steps .num { color: #12181f; } }

.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; margin: 1.2rem 0; }
@media (max-width: 780px) { .grid-3 { grid-template-columns: 1fr; } }
.card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.2rem;
}
.card h3 { margin-top: 0; }

.device-lists { display: grid; grid-template-columns: 1fr 1fr; gap: 1.2rem; margin: 1.2rem 0; }
@media (max-width: 780px) { .device-lists { grid-template-columns: 1fr; } }
.device-lists .card--good { border-left: 3px solid var(--olive); }
.device-lists .card--warn { border-left: 3px solid var(--rust); }

.browser-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.3rem 1.4rem;
  margin: 1.4rem 0;
}
/* A card with a screenshot beside it. The picture is the half the bullets
   cannot carry: what a refusal looks like to whoever hits one, and that
   "monochrome" is the whole page rather than a theme setting. Capped narrow —
   it is a phone screen next to a paragraph, not the subject of the section. */
.browser-card--shot { display: grid; grid-template-columns: 1fr minmax(0, 210px); gap: 1.6rem; align-items: start; }
.browser-card-shot { margin: 0; }
.browser-card-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
}
@media (max-width: 780px) {
  .browser-card--shot { grid-template-columns: 1fr; }
  .browser-card-shot { max-width: 210px; margin: 0 auto; }
}

.browser-card-head { display: flex; align-items: center; gap: 0.8rem; margin-bottom: 0.6rem; }
.browser-card-icon { width: 44px; height: 44px; border-radius: 10px; flex: 0 0 auto; box-shadow: var(--shadow); }
.browser-card-head h3 { margin: 0; }
/* Emoji bullets instead of discs. Markers live in CSS rather than in the
   content strings so the same text stays reusable and the list keeps its
   real <ul>/<li> semantics for screen readers. */
.browser-card ul { margin: 0.6rem 0 0; padding-left: 0; list-style: none; }
.browser-card ul ul { margin: 0.4rem 0 0; }
.browser-card li { margin: 0.35rem 0; position: relative; padding-left: 1.9rem; }
.browser-card li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: 0;
  color: var(--wood);
  font-weight: 700;
  line-height: inherit;
}
/* The nested note is an aside, not another feature, so it gets an info mark
   rather than a tick. */
.browser-card ul ul li::before {
  content: "ℹ";
  color: var(--slate);
  font-weight: 400;
}

.callout {
  background: var(--paper);
  border: 1px solid var(--line);
  border-left: 3px solid var(--slate);
  border-radius: var(--radius);
  padding: 1rem 1.2rem;
  margin: 1.2rem 0;
}
/* The loud callout. One page uses it, for the one thing on the install page a
   reader cannot afford to find out afterwards: that installing uninstalls.
   Named for what it does, not for what it used to say; it carried the beta
   notice as well until that notice was dropped on 2026-08-25. */
.callout--warn {
  border-left-width: 4px;
  border-left-color: var(--rust);
  background: color-mix(in srgb, var(--rust) 7%, var(--paper));
}
.callout--warn strong { color: var(--rust); }

.footnote { font-size: 0.85rem; color: var(--ink-soft); }

/* The install page's three situations, behind one dropdown.
 *
 * A <select> and eleven lines of script. It replaced three radios and a sibling
 * selector on 2026-08-25, when the installer moved onto this page and took its
 * module script with it: the CSS-only trick had been worth its awkwardness only
 * while it was buying a page with no JavaScript at all, and that page no longer
 * exists. What the radios could never do is what a dropdown does for free on a
 * phone, which is not take three fat tap targets of vertical space above the
 * content they select.
 *
 * Every panel is in the HTML and visible by default; the script hides the two
 * that are not chosen. With script off a reader gets all three in order under
 * their own headings, which is the failure mode worth having.
 */
.flow-picker { margin: 1.6rem 0 1.2rem; }
.flow-legend { display: block; font-weight: 600; margin: 0 0 0.5rem; }

.flow-select {
  font: inherit;
  width: 100%;
  max-width: 34rem;
  padding: 0.75rem 2.6rem 0.75rem 1rem;
  color: var(--ink);
  background-color: var(--paper);
  /* The chevron is drawn here because appearance:none takes the platform's
     own away, and it has to be spelled out twice: a data URI cannot reach
     currentColor, so the dark scheme gets its own copy below. */
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%23556354" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  background-repeat: no-repeat;
  background-position: right 1rem center;
  border: 1px solid var(--line);
  border-left: 3px solid var(--wood);
  border-radius: var(--radius);
  appearance: none;
  cursor: pointer;
}
@media (prefers-color-scheme: dark) {
  .flow-select {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="8" viewBox="0 0 12 8"><path d="M1 1.5 6 6.5 11 1.5" fill="none" stroke="%23a7b3a2" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>');
  }
}
.flow-select:hover { border-color: var(--ink-soft); }
.flow-select:focus-visible { outline: 2px solid var(--wood); outline-offset: 2px; }

.flow-panel { margin-top: 1.6rem; }
.flow-panel[hidden] { display: none; }

/* The panel's own heading answers "which one am I reading" for a reader with
   no script. With script, the dropdown a line above it already says so, and
   two copies of the same sentence in a row reads like a mistake, so the
   heading goes to the screen readers only. */
.flow-panel-title { margin-top: 0; }
.flow-picker[data-js] .flow-panel-title {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
}

/* Screenshots of the phone screens a step walks through.
 *
 * A row on a wide screen, two columns on a phone: four 480px pictures side by
 * side on a 375px screen is four pictures nobody can read, and one per row
 * turns a step into a scroll. The arrows are drawn into the images, so nothing
 * here has to position anything on top of them.
 */
.shot-strip { margin: 1.2rem 0 1.4rem; }
.shot-strip-title { font-weight: 600; font-size: 0.92rem; margin: 0 0 0.7rem; }
.shot-strip-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.9rem; align-items: start; }
.shot-strip[data-count="3"] .shot-strip-row { grid-template-columns: repeat(3, 1fr); }
.shot-strip[data-count="2"] .shot-strip-row { grid-template-columns: repeat(2, 1fr); max-width: 380px; }
@media (max-width: 640px) {
  .shot-strip-row,
  .shot-strip[data-count="3"] .shot-strip-row { grid-template-columns: repeat(2, 1fr); }
}
.shot-strip figure { margin: 0; }
.shot-strip a { display: block; }
.shot-strip img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: #fff;
}
.shot-strip figcaption { margin-top: 0.4rem; font-size: 0.78rem; line-height: 1.35; color: var(--ink-soft); }

/* tables (why-blocked page) */

.table-scroll { overflow-x: auto; margin: 1rem 0 1.8rem; border: 1px solid var(--line); border-radius: var(--radius); }
table { border-collapse: collapse; width: 100%; font-size: 0.92rem; }
caption { text-align: left; font-weight: 600; padding: 0.7rem 0.9rem; background: var(--paper); border-bottom: 1px solid var(--line); }
th, td { text-align: left; padding: 0.6rem 0.8rem; border-bottom: 1px solid var(--line); vertical-align: top; }
thead th { background: var(--paper); white-space: nowrap; font-size: 0.82rem; color: var(--ink-soft); text-transform: uppercase; letter-spacing: 0.03em; }
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--paper); }
td:first-child, th:first-child { white-space: nowrap; }

.source-links { white-space: nowrap; }
.source-links a { margin-right: 0.4rem; }

.app-entry { border: 1px solid var(--line); border-radius: var(--radius); padding: 1.1rem 1.3rem; margin: 1rem 0; background: var(--paper); }
.app-entry h4 { margin: 0 0 0.5rem; font-size: 1.05rem; }
.app-entry dl { margin: 0.6rem 0 0; display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 1rem; font-size: 0.93rem; }
.app-entry dt { color: var(--ink-soft); }
.app-entry dd { margin: 0; }

.lang-note {
  display: inline-block;
  font-size: 0.85rem;
  color: var(--ink-soft);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.25rem 0.75rem;
  margin-bottom: 1rem;
}

/* footer */

.site-footer {
  border-top: 1px solid var(--line);
  padding: 2rem 0;
  margin-top: 2rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.site-footer a { color: inherit; }
.site-footer .wrap { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
.footer-main { display: flex; flex-direction: column; gap: 0.2rem; }

.toc { columns: 2; column-gap: 2rem; margin: 1rem 0 2rem; }
@media (max-width: 780px) { .toc { columns: 1; } }
.toc a { display: block; padding: 0.15rem 0; font-size: 0.95rem; text-decoration: none; }

/* --- the USB installer ---------------------------------------------------
   The only page on this site that runs JavaScript. These rules cover the
   progress list and the log it writes; everything else on the page reuses the
   callouts, steps and buttons above. */

.installer-status { font-weight: 600; margin: 0.4rem 0 0; min-height: 1.4em; }
.installer-status--ok { color: var(--wood); }
.installer-status--error { color: var(--rust); }

.installer-steps { list-style: none; padding: 0; margin: 1.2rem 0; }
.installer-steps li {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  padding: 0.45rem 0;
  color: var(--ink-soft);
}
/* The dot is the whole status indicator: empty until a step starts, filled
   while it runs, solid when it is done, rust if it failed. */
.step-dot {
  flex: 0 0 auto;
  width: 0.85rem;
  height: 0.85rem;
  border-radius: 50%;
  border: 2px solid var(--line);
  background: transparent;
}
.installer-steps li[data-state="active"] { color: var(--ink); font-weight: 600; }
.installer-steps li[data-state="active"] .step-dot { border-color: var(--wood); background: var(--paper); }
.installer-steps li[data-state="done"] { color: var(--ink); }
.installer-steps li[data-state="done"] .step-dot { border-color: var(--wood); background: var(--wood); }
.installer-steps li[data-state="error"] { color: var(--rust); font-weight: 600; }
.installer-steps li[data-state="error"] .step-dot { border-color: var(--rust); background: var(--rust); }

.installer-log {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 0.9rem 1.1rem;
  margin: 1.2rem 0;
  max-height: 20rem;
  overflow-y: auto;
  overflow-x: auto;
  font-size: 0.85rem;
  line-height: 1.6;
  white-space: pre-wrap;
  word-break: break-word;
}
.log-line { color: var(--ink-soft); }
.log-line--ok { color: var(--ink); }
.log-line--error { color: var(--rust); font-weight: 600; }
.log-line--prompt { color: var(--ink); font-weight: 600; }

/* A step the run does not reach — granting device ownership when this is an
   update rather than a first provision. Struck through rather than hidden, so
   the list does not silently renumber itself mid-run. */
.installer-steps li[data-state="skipped"] { color: var(--ink-soft); opacity: 0.55; }
.installer-steps li[data-state="skipped"] .step-dot { border-style: dashed; }

/* Non-production channel band. Deliberately loud and deliberately not themed:
   the one thing it must never do is look like part of the site. */
.channel-banner {
  background: var(--rust);
  color: #fff;
  text-align: center;
  padding: 0.55rem 1rem;
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* "Drawbridge app explained": six screens of the app, one at a time.
 *
 * Radios and a sibling selector, like the install page's picker, because this
 * site runs no JavaScript outside the USB installer and a carousel is not the
 * thing to break that for. Every page is in the HTML and one is shown, so with
 * CSS off a reader gets six sections in order under their own headings —
 * which is a perfectly good way to read this page.
 */
.explainer {
  position: relative;
  margin: 1.4rem 0 0;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.4rem;
}
.explainer > input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
}

/* Direct access to any page, and the only part of the widget that is always
   visible — so it doubles as the "there are six of these" signal. */
.explainer-dots { display: flex; justify-content: center; gap: 0.5rem; margin-bottom: 1.2rem; }
.explainer-dots label {
  width: 0.85rem; height: 0.85rem;
  border-radius: 50%;
  border: 1px solid var(--slate);
  cursor: pointer;
}
.explainer-dots label:hover { background: var(--line); }

.explainer-page { display: none; }
.explainer-page {
  grid-template-columns: minmax(0, 300px) 1fr;
  gap: 1.8rem;
  align-items: start;
}
#dbp-1:checked ~ #dbpage-1,
#dbp-2:checked ~ #dbpage-2,
#dbp-3:checked ~ #dbpage-3,
#dbp-4:checked ~ #dbpage-4,
#dbp-5:checked ~ #dbpage-5,
#dbp-6:checked ~ #dbpage-6 { display: grid; }

#dbp-1:checked ~ .explainer-dots label[for="dbp-1"],
#dbp-2:checked ~ .explainer-dots label[for="dbp-2"],
#dbp-3:checked ~ .explainer-dots label[for="dbp-3"],
#dbp-4:checked ~ .explainer-dots label[for="dbp-4"],
#dbp-5:checked ~ .explainer-dots label[for="dbp-5"],
#dbp-6:checked ~ .explainer-dots label[for="dbp-6"] { background: var(--wood); border-color: var(--wood); }

#dbp-1:focus-visible ~ .explainer-dots label[for="dbp-1"],
#dbp-2:focus-visible ~ .explainer-dots label[for="dbp-2"],
#dbp-3:focus-visible ~ .explainer-dots label[for="dbp-3"],
#dbp-4:focus-visible ~ .explainer-dots label[for="dbp-4"],
#dbp-5:focus-visible ~ .explainer-dots label[for="dbp-5"],
#dbp-6:focus-visible ~ .explainer-dots label[for="dbp-6"] { outline: 2px solid var(--wood); outline-offset: 3px; }

.explainer-shot { margin: 0; }
.explainer-shot img {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--line);
  border-radius: 8px;
}
.explainer-copy h3 { margin-top: 0; }
.explainer-copy h4 { margin: 1.1rem 0 0.2rem; font-size: 1rem; color: var(--slate-dark); }
.explainer-copy p { margin: 0.5rem 0; }

/* Above the page rather than below it, as of 2026-08-24. The pages differ a
   lot in height — page 5 is four options long — so controls at the bottom sat
   somewhere different every time and, on the long pages, below the fold: a
   reader could finish page 1 without ever learning there were five more. */
.explainer-nav {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.8rem;
  margin-bottom: 1.4rem;
  padding-bottom: 1rem;
  border-bottom: 1px solid var(--line);
}
.explainer-nav .btn { padding: 0.55rem 1rem; font-size: 0.92rem; }
.explainer-count { font-size: 0.85rem; color: var(--ink-soft); white-space: nowrap; }

@media (max-width: 780px) {
  .explainer { padding: 1rem; }
  .explainer-page { grid-template-columns: 1fr; gap: 1.1rem; }
  .explainer-shot { max-width: 300px; margin: 0 auto; }
}

.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
}
