/*
 * One stylesheet, no framework and no build step. The portal has a dozen pages of forms and tables; a CSS
 * toolchain would be a second thing to install before anyone can change a label.
 *
 * Almost nothing here is invented. DeskPair already had a design system -- it just lived in code comments,
 * principally mobile/androidApp/.../ui/Colour.kt, with Cards.kt, BrandMark.swift and the desktop's
 * App.axaml filling it in. The website was the one surface not following it. The colours below are quoted
 * from those files rather than chosen again, so that somebody with the phone app open beside this page
 * cannot tell they were built by different hands.
 */

/* ---- the typeface ------------------------------------------------------------------------------------
 *
 * Self-hosted, and that is not a preference. /privacy says there are no third-party scripts and no
 * analytics, and the whole site being JavaScript-free is what makes that true. A font from
 * fonts.googleapis.com hands every visitor's IP to Google, which would make the policy false -- on the
 * page two app stores are reviewing.
 *
 * IBM Plex, SIL OFL 1.1 (fonts/OFL.txt), subset to Latin: no Greek, no Cyrillic. 98 KB for all four.
 *
 * Four faces: Regular for text, Medium for labels and navigation, SemiBold for headings and buttons, and the
 * Mono. Each is the same subset, codepoint for codepoint.
 *
 * Chinese deliberately has no font file. A CJK face is measured in megabytes, and on a Taiwanese mobile
 * connection that costs far more than it returns; the reader's own PingFang or 正黑體 is already good.
 * The stack falls through per character, so Plex sets the Latin and the system sets the Chinese.
 *
 * Plex Mono is here because the product already treats monospace as a brand signal rather than a
 * convenience: the desktop app has exactly one font declaration, Consolas/Menlo, and uses it only for the
 * peer ID, the one-time password and the key fingerprint -- the things a person reads aloud or types. The
 * SHA-256 strings and shell commands on this site are the same kind of thing.
 */
@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/IBMPlexSans-Regular.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 500;
    font-display: swap;
    src: url("fonts/IBMPlexSans-Medium.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Sans";
    font-style: normal;
    font-weight: 600;
    font-display: swap;
    src: url("fonts/IBMPlexSans-SemiBold.woff2") format("woff2");
}

@font-face {
    font-family: "IBM Plex Mono";
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url("fonts/IBMPlexMono-Regular.woff2") format("woff2");
}

/* ---- tokens ------------------------------------------------------------------------------------------
 *
 * There were eight, all colour. Spacing was eighteen different pixel values chosen one at a time.
 */
:root {
    color-scheme: light dark;

    /* Surfaces. Grey ground, white cards -- the opposite way round from what this sheet used to do, and
     * the way both phone apps do it. Colour.kt is blunt about why it matters: "The ground is grey and the
     * cards are white, which is the opposite way round from stock Material 3. That is the whole look."
     *
     * The grey has a blue bias rather than being neutral: "against the accent a true grey reads as dirty." */
    --ground: #F2F4F7;
    --card: #FFFFFF;
    --sunken: #EDF0F4;
    --fg: #151A21;
    --muted: #5C6672;
    --line: #E3E7EE;

    /* The accent both phone apps use, to the digit. #2E6BD6 -- the desktop's Button.accent -- was
     * deliberately rejected there because white text on it falls short of 4.5:1, and this sheet had drifted
     * to #1f6feb, which matches nothing at all. */
    --accent: #1F5FC9;
    --on-accent: #FFFFFF;
    /* The logo's cyan, which has a sanctioned UI role on Android and had no role here at all. */
    --accent-2: #00687E;

    --bad: #B3261E;
    --good: #067647;

    /* What a floating card casts. Most separation here is still the fill step and a hairline, the way the
     * desktop app does it; this is only for the few things meant to look picked up off the page. */
    --lift: 0 1px 2px rgb(21 26 33 / 0.05), 0 14px 34px -14px rgb(21 26 33 / 0.18);

    /* Spacing. The first five are the desktop app's rhythm: page 24, card padding 18. The large ones are
     * the website's own -- a page of sections needs air that an app window never does. */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 18px;
    --s5: 24px;
    --s6: 40px;
    --s7: 64px;
    --s8: 96px;
    --s9: 144px;

    /* Radii: 8 for what is pressed or typed into, 12 for a card, 16 for a panel that holds a drawing. The
     * desktop app's are 6 and 10; a web page is read from further away and wants them a size up. The pill is
     * for dots and tags only -- no button on this site is a pill. */
    --r-control: 8px;
    --r-card: 12px;
    --r-panel: 16px;
    --r-pill: 999px;

    --sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", "PingFang TC", "Microsoft JhengHei", "Noto Sans TC", sans-serif;
    --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

    /* In pixels, not ch: a heading and the paragraph under it are both held to the measure, and a ch is as
     * wide as the font it is in, so 68ch gave the two different widths and different left edges. */
    --measure: 720px;
    --col-console: 880px;
    /* Wide: on an ordinary laptop a public page reaches nearly to the edges of the window, and only running
     * text is held narrower than the column. It follows the window up to a point; past that the words on
     * one side of the first screen and the picture on the other stop belonging to each other. */
    --col-marketing: min(1440px, 92vw);

    /* The inverse surface: the bar, the footer, and one band on the front page. It is not a new colour. On
     * a light page it is the ink used as a ground, with the ground used as ink, so its contrast is the
     * body text's own, the other way up. The accent on it is mixed towards the card so that it still reads
     * against something that dark. */
    --inv-bg: var(--fg);
    --inv-fg: var(--ground);
    --inv-muted: color-mix(in srgb, var(--ground) 68%, var(--fg));
    --inv-line: color-mix(in srgb, var(--ground) 16%, var(--fg));
    --inv-accent: color-mix(in srgb, var(--accent) 45%, var(--card));
}

@media (prefers-color-scheme: dark) {
    :root {
        /* Quoted from the desktop app, not derived. #12151A is its window, #1E2228 its card, #2C323A its
         * in-card divider, #8A929E its label text, #E6E9EF its body text. Colour.kt again: "Someone running
         * the phone next to the desk should not be able to tell the two greys apart." Nor this page. */
        --ground: #12151A;
        --card: #1E2228;
        --sunken: #1A1E24;
        --fg: #E6E9EF;
        --muted: #8A929E;
        --line: #2C323A;

        /* The light half of the pair, so the dark text below meets contrast on it. White on the old
         * #5d9bff was about 3.6:1, under AA, and nothing on screen said so. */
        --accent: #7FB0F5;
        --on-accent: #08284F;
        --accent-2: #66D3EC;

        --bad: #EE8888;
        --good: #6EE7A8;

        /* A shadow on a dark ground is mostly invisible; the top edge catching light is what reads. */
        --lift: inset 0 1px 0 rgb(255 255 255 / 0.05), 0 18px 40px -18px rgb(0 0 0 / 0.7);

        /* On a dark page the inverse surface cannot be darker than the ground without inventing a colour,
         * so it is the card: a step up, the way the desktop app sets its title bar off from its window. */
        --inv-bg: var(--card);
        --inv-fg: var(--fg);
        --inv-muted: var(--muted);
        --inv-line: var(--line);
        --inv-accent: var(--accent);
    }
}

* { box-sizing: border-box; }

html { scroll-padding-top: 80px; }

body {
    margin: 0;
    background: var(--ground);
    color: var(--fg);
    font: 17px/1.65 var(--sans);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* For a heading that a screen reader needs and the eye does not. */
.sr-only {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

.icon { width: 24px; height: 24px; flex: none; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }

/* ---- the bar -----------------------------------------------------------------------------------------
 *
 * A solid band across the top, the mark at one end and everything else at the other. It is the inverse
 * surface -- the ink colour as a ground -- so it is the darkest thing on a light page and a step up from
 * the ground on a dark one, and in both it reads as the frame the page hangs from. The same width on every
 * page: it is the one thing the console and the public pages share.
 */
.bar {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    gap: var(--s4) var(--s6);
    padding: var(--s3) max(var(--s5), calc(50vw - var(--col-marketing) / 2));
    background: var(--inv-bg);
    color: var(--inv-fg);
    border-bottom: 1px solid var(--inv-line);
}

.bar nav { display: flex; align-items: center; gap: var(--s5); font-size: 15px; font-weight: 500; }
.bar nav.site { margin-left: auto; }
.bar nav a, .bar nav button.link { color: var(--inv-fg); text-decoration: none; font-size: inherit; font-weight: inherit; }
.bar nav a:hover, .bar nav button.link:hover { color: var(--inv-accent); }
.bar nav.account a.cta, .bar nav.account a.cta:hover { color: var(--on-accent); }

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--s2);
    font-weight: 600;
    font-size: 20px;
    letter-spacing: -0.01em;
    text-decoration: none;
    color: inherit;
}

.brand svg { width: 30px; height: 30px; display: block; }

main {
    position: relative;
    isolation: isolate;
    max-width: var(--col-console);
    margin: 0 auto;
    padding: var(--s7) var(--s5) var(--s8);
}

/* ---- type --------------------------------------------------------------------------------------------
 *
 * Heavy headings, plain text, nothing clever: this is a utility and should look like one. Running text is
 * a size up from a browser's default because the explanations are what a visitor came to read, and half of
 * them are Chinese, which needs more pixels than Latin to be legible at all. The slight tightening of
 * headings is for Latin only; Chinese is already set solid and the same tracking would make it touch.
 */
h1, h2, h3 { font-weight: 600; text-wrap: balance; }
h1 { font-size: 32px; line-height: 1.2; margin: 0 0 var(--s2); letter-spacing: -0.02em; }
h2 { font-size: 24px; line-height: 1.25; margin: var(--s7) 0 var(--s3); letter-spacing: -0.015em; }
h3 { font-size: 19px; line-height: 1.35; margin: 0 0 var(--s1); letter-spacing: -0.01em; }
p.lede { color: var(--muted); margin: 0 0 var(--s5); font-size: 18px; }

:lang(zh) :is(h1, h2, h3, .brand) { letter-spacing: 0; }
:lang(zh) :is(h1, h2) { line-height: 1.35; }

a { color: var(--accent); text-underline-offset: 3px; }

/* The small label over a heading: the accent, capitals, a little tracking, and no box round it. */
.eyebrow {
    margin: 0 0 var(--s3);
    color: var(--accent);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

:lang(zh) .eyebrow { letter-spacing: 0.06em; font-size: 15px; }

/* ---- forms and the console ---- */
form.narrow { max-width: 400px; }
form.inline { display: inline; }

label { display: block; margin: var(--s4) 0 var(--s1); font-weight: 500; font-size: 15px; }

input[type=text], input[type=email], input[type=password], select {
    width: 100%;
    padding: 11px 13px;
    border: 1px solid var(--line);
    border-radius: var(--r-control);
    background: var(--card);
    color: var(--fg);
    font: inherit;
}

select { width: auto; padding: 8px 12px; }

input:focus-visible, select:focus-visible, button:focus-visible, a:focus-visible, summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

button {
    margin-top: var(--s4);
    padding: 11px 22px;
    border: 1px solid transparent;
    border-radius: var(--r-control);
    background: var(--accent);
    color: var(--on-accent);
    font: inherit;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
}

button.secondary { background: var(--card); border-color: var(--line); color: var(--fg); }
button.link { margin: 0; padding: 0; background: none; border: 0; border-radius: 0; color: var(--accent); cursor: pointer; font-weight: 400; }
button.danger { background: var(--card); border-color: var(--line); color: var(--bad); }

td button, td form { margin: 0; }
td button { margin-top: 0; padding: 6px 14px; font-size: 15px; }

/* Separation comes from the fill step, the way the desktop app does it: a card is lighter than the ground
 * and carries a hairline, not a shadow. */
.card {
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--card);
    padding: var(--s5);
    margin: var(--s3) 0;
}

/* The coloured edge is an inset shadow rather than a border, so it follows the corner instead of cutting
 * it off square. */
.notice {
    padding: var(--s4) var(--s5);
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r-control);
    box-shadow: inset 3px 0 0 var(--accent);
    margin: var(--s5) 0;
}

.notice.bad { box-shadow: inset 3px 0 0 var(--bad); }
.notice.good { box-shadow: inset 3px 0 0 var(--good); }

.secret {
    font-family: var(--mono);
    font-size: 17px;
    letter-spacing: 1px;
    user-select: all;
}

table { width: 100%; border-collapse: collapse; margin-top: var(--s2); }
th, td { text-align: left; padding: var(--s3); border-bottom: 1px solid var(--line); vertical-align: middle; }
th { font-size: 14px; color: var(--muted); font-weight: 500; }

/* Figures that line up as figures. */
.secret, .hash, td.num, .win dd, .field, .book code, .facts dt { font-variant-numeric: tabular-nums; }

.role { font-size: 13px; padding: 2px var(--s2); border-radius: var(--r-pill); border: 1px solid var(--line); color: var(--muted); }
.muted { color: var(--muted); }
.centre { text-align: center; }
.row { display: flex; gap: var(--s3); align-items: flex-end; flex-wrap: wrap; }
.row > * { margin-top: 0; }
.row label { margin-top: 0; }

/* ---- the footer ----
 * The same inverse surface as the bar, so the page is held between two of them. Columns of links under a
 * small heading each, then a rule and the version. */
footer { background: var(--inv-bg); color: var(--inv-muted); font-size: 15px; }

.foot { max-width: var(--col-marketing); margin: 0 auto; padding: var(--s7) var(--s5) var(--s6); }

.foot-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--s6) var(--s7);
    flex-wrap: wrap;
    padding-bottom: var(--s6);
    border-bottom: 1px solid var(--inv-line);
}

.foot-top .brand { color: var(--inv-fg); }
.foot-bottom { padding-top: var(--s5); }
.foot .version { font-family: var(--mono); font-size: 13px; }
.foot-groups { display: flex; gap: var(--s6) var(--s8); flex-wrap: wrap; }
footer nav { display: grid; gap: var(--s2); align-content: start; min-width: 130px; }

footer nav h2, body.marketing footer nav h2 {
    margin: 0 0 var(--s2);
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--inv-fg);
}

footer nav a { color: var(--inv-muted); text-decoration: none; }
footer nav a:hover { color: var(--inv-fg); }

/* ---- the public pages --------------------------------------------------------------------------------
 *
 * Scoped to body.marketing, which only MarketingPageModel sets, so the account console keeps the narrow
 * column its tables were built for.
 *
 * clip rather than hidden: hidden would make the body a scroll container and break position: sticky.
 */
body.marketing { overflow-x: clip; }
body.marketing main { max-width: var(--col-marketing); padding-top: 0; padding-bottom: 0; }

/* A band is a stretch of the page with a ground of its own, edge to edge, while what is in it stays in the
 * column. The colour is painted by a pseudo-element that runs further than any window is wide in both
 * directions, and the body clips what is left over. Measuring the window with vw instead comes up short by
 * the width of a scrollbar on some platforms, and a band that stops twelve pixels from the edge is worse
 * than none. */
.band { position: relative; padding-block: var(--s8); }
.band.tight { padding-block: var(--s6); }

.band::before {
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0 -100vmax;
    background: var(--ground);
}

.band.alt::before { background: var(--card); border-block: 1px solid var(--line); }
.band.tint::before { background: color-mix(in srgb, var(--accent) 9%, var(--ground)); }
.band.inv::before { background: var(--inv-bg); }

.band.inv { color: var(--inv-fg); }
.band.inv .eyebrow { color: var(--inv-accent); }
.band.inv p { color: var(--inv-muted); }

/* A link that has to look like the button beside it. The button rule lives on <button>, and making it a
 * class would touch every form on every console page, so the few declarations are repeated instead. */
.cta {
    display: inline-flex; align-items: center; justify-content: center; gap: 10px;
    padding: 13px 26px; border-radius: var(--r-control);
    background: var(--accent); color: var(--on-accent);
    font-size: 17px; font-weight: 600; line-height: 1.3; text-decoration: none;
    border: 1px solid transparent;
    transition: filter 0.15s ease, box-shadow 0.15s ease;
}

.cta:hover { filter: brightness(1.08); box-shadow: var(--lift); }
.cta.secondary { background: var(--card); border-color: color-mix(in srgb, var(--muted) 40%, var(--line)); color: var(--fg); }
.cta.secondary:hover { filter: none; border-color: var(--accent); }
.cta.small { padding: 8px 18px; font-size: 15px; }

/* The main button says two things: what it does, and what it is for. */
.cta.two-line { flex-direction: column; gap: 0; padding: 10px 30px; }
.cta.two-line strong { font-weight: 600; }
.cta.two-line small { font-size: 13px; font-weight: 400; opacity: 0.9; }

.actions { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: stretch; }

/* ---- the front page: the hero ------------------------------------------------------------------------
 *
 * Words on the left, the product on the right, and under both of them the row of platforms -- the three
 * things a visitor wants in the first screen: what is it, what does it look like, will it run on mine.
 */
.hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s7) var(--s7);
    align-items: center;
    padding-block: var(--s8) var(--s7);
}

.hero::before { background: color-mix(in srgb, var(--accent) 9%, var(--ground)); }

.hero h1 {
    font-size: clamp(36px, 3.9vw, 56px);
    line-height: 1.1;
    letter-spacing: -0.03em;
    margin: 0 0 var(--s4);
}

/* The second half of the headline takes its own line and the accent, so the turn in the sentence is a line
 * break at every width. Upright: it is the same sentence, not an aside. */
.hero h1 em { display: block; font-style: normal; color: var(--accent); }

:lang(zh) .hero h1 { letter-spacing: 0; line-height: 1.25; }

.hero .sub { max-width: 34em; margin: 0; font-size: 20px; color: var(--muted); }
.hero .actions { margin-top: var(--s5); }

/* Three things that are true, each behind a small icon. */
.claims { list-style: none; display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: var(--s5) 0 0; padding: 0; font-size: 15px; color: var(--muted); }
.claims li { display: inline-flex; align-items: center; gap: 8px; }
.claims .icon { width: 18px; height: 18px; color: var(--accent); }
.claims a { color: inherit; }

/* Both ends of a connection: two of the application's windows, one over the other's shoulder, and a dashed
 * line from the ID one shows to the box the other types it into. */
.pairing {
    position: relative;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--s5);
    padding: var(--s6);
    border-radius: var(--r-panel);
    background: color-mix(in srgb, var(--accent) 16%, var(--card));
}

.pairing .host { align-self: start; }
.pairing .ctrl { align-self: end; margin-top: var(--s8); }
.pairing .wire { position: absolute; left: 44%; right: 44%; top: 50%; }

.win {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    box-shadow: var(--lift);
    overflow: hidden;
    text-align: left;
}

.win-bar { display: flex; align-items: center; gap: 5px; padding: 10px 12px; border-bottom: 1px solid var(--line); font-size: 12px; color: var(--muted); }
.win-bar i { width: 8px; height: 8px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--muted) 35%, var(--line)); }
.win-bar span { margin-left: var(--s2); }
.win-body { padding: var(--s4) var(--s4) var(--s5); }
.win-body small { display: block; font-size: 14px; color: var(--muted); }
.win dl { margin: 0; }
.win dt { margin: var(--s3) 0 0; font-size: 13px; font-weight: 400; color: var(--muted); }
.win dd { margin: 0; font-family: var(--mono); font-size: clamp(18px, 1.7vw, 26px); letter-spacing: 0.02em; color: var(--fg); white-space: nowrap; }
.win dd.otp { color: var(--accent); }

.field {
    display: flex;
    align-items: center;
    margin-top: var(--s3);
    padding: 9px 12px;
    border: 1px solid var(--accent);
    border-radius: var(--r-control);
    background: var(--sunken);
    font-family: var(--mono);
    font-size: clamp(14px, 1.2vw, 18px);
    white-space: nowrap;
}

/* The caret at the end of what has just been typed. */
.field b { width: 1.5px; height: 1.1em; margin-left: 3px; background: var(--accent); animation: caret 1.1s steps(1) infinite; }

.go {
    margin-top: var(--s3);
    padding: 9px 12px;
    border-radius: var(--r-control);
    background: var(--accent);
    color: var(--on-accent);
    font-size: 15px;
    font-weight: 600;
    text-align: center;
}

.wire { height: 1px; background: repeating-linear-gradient(90deg, var(--fg) 0 4px, transparent 4px 9px); opacity: 0.6; }
.wire i {
    position: absolute; top: -3px; left: 0;
    width: 7px; height: 7px; border-radius: var(--r-pill);
    background: var(--accent);
    animation: travel 2.4s ease-in-out infinite alternate;
}

@keyframes caret { 50% { opacity: 0; } }
@keyframes travel { to { left: calc(100% - 7px); } }

/* The platforms, across the foot of the hero. The glyph says computer or phone; the name says which. */
.platform-row {
    grid-column: 1 / -1;
    list-style: none;
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    gap: var(--s3);
    margin: 0;
    padding: var(--s6) 0 0;
    border-top: 1px solid color-mix(in srgb, var(--accent) 22%, var(--line));
}

.platform-row a {
    display: grid;
    justify-items: center;
    gap: 2px;
    padding: var(--s3);
    border-radius: var(--r-card);
    color: var(--fg);
    text-align: center;
    text-decoration: none;
}

.platform-row a:hover { background: var(--card); box-shadow: var(--lift); }
.platform-row .icon { width: 34px; height: 34px; margin-bottom: var(--s2); color: var(--accent); stroke-width: 1.5; }
.platform-row strong { font-size: 18px; font-weight: 600; }
.platform-row span { font-size: 14px; color: var(--muted); }

/* ---- the facts ----
 * Where a product with a marketing department puts its user counts. Large figures, small labels, a rule
 * between each pair. */
.facts { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 0; text-align: center; }
.facts div { padding: 0 var(--s4); }
.facts div + div { border-left: 1px solid var(--line); }
.facts dt { margin: 0; font-size: clamp(24px, 2.6vw, 38px); font-weight: 600; line-height: 1.2; letter-spacing: -0.02em; color: var(--accent); }
.facts dd { margin: var(--s1) 0 0; color: var(--muted); font-size: 16px; }

/* ---- sections ---- */
.section-head { text-align: center; max-width: 820px; margin: 0 auto var(--s7); }

body.marketing .band h2 {
    font-size: clamp(28px, 3.2vw, 42px);
    line-height: 1.15;
    letter-spacing: -0.025em;
    margin: 0;
}

:lang(zh) body.marketing .band h2 { letter-spacing: 0; line-height: 1.3; }

.section-head h2 + p { font-size: 19px; margin: var(--s4) auto 0; color: var(--muted); text-wrap: balance; }

/* An icon in a soft square, a heading, a few lines. No box round the whole: the band is the box. */
.icon-grid { list-style: none; display: grid; gap: var(--s7) var(--s6); margin: 0; padding: 0; text-align: center; counter-reset: step; }
.icon-grid.three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.icon-grid.four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.icon-grid.left { text-align: left; }

.icon-tile {
    position: relative;
    display: inline-grid;
    place-items: center;
    width: 56px;
    height: 56px;
    margin-bottom: var(--s4);
    border-radius: var(--r-card);
    background: color-mix(in srgb, var(--accent) 13%, var(--card));
    color: var(--accent);
}

.icon-tile .icon { width: 28px; height: 28px; }

/* A step carries its number on the corner of its icon. */
.icon-grid.numbered li { counter-increment: step; }
.icon-grid.numbered .icon-tile::after {
    content: counter(step);
    position: absolute;
    top: -8px;
    right: -8px;
    display: grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border-radius: var(--r-pill);
    background: var(--accent);
    color: var(--on-accent);
    font-size: 13px;
    font-weight: 600;
}

.icon-grid h3 { font-size: 21px; margin: 0 0 var(--s2); }
.icon-grid p { margin: 0 auto; max-width: 38ch; color: var(--muted); }
.icon-grid.left p { margin: 0; }

/* A list with a tick on every line. The tick is two borders of a box, turned: no glyph to be missing from
 * a font. Green, because it is a list of things that are so. */
.checks { list-style: none; display: grid; gap: var(--s3); margin: 0; padding: 0; }
.checks li { position: relative; padding-left: 30px; }

.checks li::before {
    content: "";
    position: absolute;
    left: 5px;
    top: 0.4em;
    width: 7px;
    height: 12px;
    border: solid var(--good);
    border-width: 0 2.5px 2.5px 0;
    transform: rotate(45deg);
}

.checks.two { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3) var(--s7); max-width: 1000px; margin-inline: auto; }
.checks.two.narrow { max-width: 760px; }
.checks.spec { margin-top: var(--s8); padding-top: var(--s7); border-top: 1px solid var(--line); }
.checks + .centre { margin: var(--s6) 0 0; }

/* ---- what people use it for ----
 * Text on one side, a drawing on the other, and they change sides on the way down so the eye does not run
 * down one edge of the page. */
.rows { display: grid; gap: var(--s8); }
.rows article { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s6) var(--s8); align-items: center; }
.rows article:nth-child(even) .row-copy { order: 2; }

.rows h3 { font-size: clamp(24px, 2.4vw, 32px); line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 var(--s3); }
:lang(zh) .rows h3 { letter-spacing: 0; line-height: 1.35; }
.row-copy > p { margin: 0; color: var(--muted); font-size: 19px; }
.row-copy .checks { margin-top: var(--s5); }

/* The thing the second one must not leave out. */
.rows .note {
    margin-top: var(--s5);
    padding: var(--s3) var(--s4);
    border-radius: var(--r-control);
    box-shadow: inset 3px 0 0 var(--accent);
    background: var(--sunken);
    color: var(--fg);
    font-size: 16px;
}

.visual {
    position: relative;
    display: grid;
    place-items: center;
    align-content: center;
    gap: var(--s5);
    min-height: 340px;
    padding: var(--s6);
    border-radius: var(--r-panel);
    background: color-mix(in srgb, var(--accent) 11%, var(--ground));
    overflow: hidden;
}

/* somebody is connected, and you are told */
.toast {
    display: flex; align-items: center; gap: 12px;
    width: min(100%, 340px); padding: 16px;
    border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card); box-shadow: var(--lift);
}

.toast i { flex: none; width: 10px; height: 10px; border-radius: var(--r-pill); background: var(--accent); box-shadow: 0 0 0 5px color-mix(in srgb, var(--accent) 20%, transparent); }
.toast span { flex: 1; display: grid; gap: 7px; }
.toast span b { height: 7px; border-radius: 4px; background: var(--line); }
.toast span b:last-child { width: 60%; }
.toast u { flex: none; width: 54px; height: 26px; border-radius: var(--r-control); border: 1.5px solid var(--bad); }

.bubble {
    justify-self: end;
    display: grid;
    gap: 6px;
    width: 170px;
    padding: 12px 14px;
    border: 1px solid var(--line);
    border-radius: 14px 14px 4px 14px;
    background: var(--card);
    box-shadow: var(--lift);
}

.bubble b { height: 7px; border-radius: 4px; background: var(--line); }
.bubble b:last-child { width: 55%; }

/* the saved list, and files going both ways */
.book { list-style: none; margin: 0; padding: 0; width: min(100%, 320px); border: 1px solid var(--line); border-radius: var(--r-card); background: var(--card); box-shadow: var(--lift); }
.book li { display: flex; align-items: center; gap: 10px; padding: 12px 16px; }
.book li + li { border-top: 1px solid var(--line); }
.book i { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--good); }
.book li:last-child i { background: var(--line); }
.book code { font: 14px var(--mono); }
.book b { flex: 1; height: 7px; border-radius: 4px; background: var(--line); }

.files { display: grid; gap: var(--s2); font: 14px var(--mono); }
.file {
    display: flex; align-items: center; justify-content: space-between; gap: var(--s5);
    min-width: 220px; padding: 10px 14px;
    border: 1px solid var(--line); border-radius: var(--r-control); background: var(--card);
    translate: -18px 0;
}

.file.back { translate: 18px 0; }
.file b { color: var(--accent); font-weight: 400; }

/* a phone, with a computer's screen in it and a finger on the glass */
.phone { width: 150px; height: 270px; padding: 9px; border: 1px solid var(--line); border-radius: 28px; background: var(--card); box-shadow: var(--lift); }

.phone-screen {
    position: relative;
    display: flex;
    gap: 4px;
    height: 100%;
    padding: 10px;
    border-radius: 20px;
    background: color-mix(in srgb, var(--accent) 24%, var(--sunken));
}

.phone-screen i { width: 6px; height: 6px; border-radius: var(--r-pill); background: color-mix(in srgb, var(--fg) 35%, transparent); }

.phone-screen b {
    position: absolute;
    left: 50%;
    top: 56%;
    width: 28px;
    height: 28px;
    border-radius: var(--r-pill);
    border: 2px solid var(--accent);
    background: color-mix(in srgb, var(--accent) 30%, transparent);
    animation: tap 2.2s ease-in-out infinite;
}

@keyframes tap { 50% { scale: 0.7; opacity: 0.55; } }

/* ---- running it yourself: the inverse band ---- */
.band.split { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); gap: var(--s6) var(--s8); align-items: center; }
.band.split p:not(.eyebrow) { font-size: 19px; margin: var(--s4) 0 0; max-width: 60ch; }

.stack { list-style: none; margin: 0; padding: var(--s2); border: 1px solid var(--inv-line); border-radius: var(--r-card); background: color-mix(in srgb, var(--inv-fg) 6%, var(--inv-bg)); font: 16px var(--mono); color: var(--inv-fg); }
.stack li { display: flex; align-items: center; gap: 12px; padding: 14px 16px; }
.stack li + li { border-top: 1px solid var(--inv-line); }
.stack i { width: 8px; height: 8px; border-radius: var(--r-pill); background: var(--inv-accent); }

/* ---- the last band ---- */
.closing { text-align: center; }
.closing p:not(.eyebrow) { font-size: 19px; color: var(--muted); margin: var(--s4) auto 0; max-width: 56ch; }
.closing .actions { justify-content: center; margin-top: var(--s6); }

/* Inline, not a flex box: a flex box has its own idea of where its baseline is, and the name ends up a
 * few pixels above the sentence it is part of. */
.inline-brand { white-space: nowrap; }
.inline-brand svg { display: inline-block; width: 0.95em; height: 0.95em; margin-right: 0.14em; vertical-align: -0.13em; }

/* ---- the other public pages ----
 * Each opens with a tinted band of its own, and what follows sits in the middle of the column at a width
 * somebody can read. */
.page-head { text-align: center; padding-block: var(--s8); margin-bottom: var(--s8); }

body.marketing .page-head h1 {
    font-size: clamp(34px, 4vw, 52px);
    line-height: 1.12;
    letter-spacing: -0.03em;
    margin: 0 auto var(--s3);
    max-width: 18em;
}

:lang(zh) body.marketing .page-head h1 { letter-spacing: 0; line-height: 1.3; }
body.marketing .page-head p.lede { font-size: 20px; max-width: 56ch; margin: 0 auto; text-wrap: balance; }

body.marketing main:has(.page-head) { padding-bottom: var(--s9); }
body.marketing main > h2, body.marketing .prose h2 { font-size: 28px; letter-spacing: -0.02em; margin: var(--s8) 0 var(--s4); }
:lang(zh) body.marketing :is(main > h2, .prose h2) { letter-spacing: 0; }
body.marketing .prose > h2:first-child { margin-top: 0; }

/* The line straight under the opening belongs to it; any other loose paragraph belongs to the prose. */
body.marketing .page-head + p.muted { text-align: center; margin-top: calc(var(--s6) * -1); margin-bottom: var(--s6); }

.grid { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); margin: var(--s5) 0; }
.grid .card { margin: 0; display: flex; flex-direction: column; }

/* Only the card that has a button in it pushes that button to the bottom, so the download cards line their
 * actions up across a row of uneven heights. Written as :has rather than :last-child, which bottom-aligned
 * the paragraph on every text card and opened a hole under each heading. Where :has is not supported the
 * button simply sits under its text, which is what it did before. */
.grid .card p:has(> .cta) { margin-top: auto; padding-top: var(--s3); }
.grid .card .cta { width: 100%; }
.grid h3 { font-size: 21px; margin: 0 0 var(--s2); }
.grid p { margin: 0 0 var(--s2); color: var(--muted); }

.hash {
    font-family: var(--mono);
    font-size: 13px; word-break: break-all; color: var(--muted); user-select: all; margin: 0;
}

pre.cmd {
    background: var(--sunken); border: 1px solid var(--line); border-radius: var(--r-control);
    padding: var(--s4) var(--s5); overflow-x: auto; font-family: var(--mono); font-size: 14.5px;
    margin: var(--s2) 0 var(--s4);
}

/* Policy, help and other long prose: a measure rather than the whole column, in the middle of it. */
.prose { max-width: var(--measure); }
body.marketing .prose { margin-inline: auto; }
body.marketing main > :is(h2, ul.prose, p.muted, .actions) { max-width: var(--measure); margin-inline: auto; }
.faq-list + .prose { margin-top: var(--s8); }

/* The FAQ is <details>. There is no JavaScript anywhere on this site and this is why it can stay that way.
 * One column at a reading width, a rule between questions, and a plus at the far end of each that becomes
 * a minus while it is open. */
.faq-list { max-width: 860px; margin: 0 auto; }

details.faq { border-bottom: 1px solid var(--line); }
details.faq:first-of-type { border-top: 1px solid var(--line); }

details.faq summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--s5);
    padding: var(--s5) var(--s1);
    font-weight: 600;
    font-size: 19px;
}

details.faq summary::-webkit-details-marker { display: none; }
details.faq summary::after { content: "+"; flex: none; font-size: 24px; font-weight: 400; line-height: 1; color: var(--accent); }
details.faq[open] summary::after { content: "\2212"; }
details.faq summary:hover { color: var(--accent); }
details.faq p { margin: 0; padding: 0 var(--s1) var(--s5); color: var(--muted); max-width: 70ch; }

/* What to do when the answer is not among them. */
.ask {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4) var(--s6);
    flex-wrap: wrap;
    margin-top: var(--s6);
    padding: var(--s5);
    border: 1px solid var(--line);
    border-radius: var(--r-card);
    background: var(--ground);
}

.ask > div { flex: 1 1 320px; }
.ask strong { font-size: 19px; }
.ask p { margin: var(--s1) 0 0; color: var(--muted); font-size: 16px; }

dl dt { font-weight: 600; margin-top: var(--s4); }
dl dd { margin: var(--s1) 0 0; color: var(--muted); }

/* ---- narrower ---- */
@media (max-width: 1100px) {
    .icon-grid.four { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .hero { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--s6); }
    .pairing { padding: var(--s5); gap: var(--s4); }
}

@media (max-width: 900px) {
    .band { padding-block: var(--s7); }
    .hero { grid-template-columns: 1fr; padding-block: var(--s7) var(--s6); }
    .pairing { max-width: 560px; }
    .platform-row { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .icon-grid.three { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .rows article { grid-template-columns: 1fr; }
    .rows article:nth-child(even) .row-copy { order: 0; }
    .visual { min-height: 260px; }
    .band.split { grid-template-columns: 1fr; }
    .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s5); }
    .facts div:nth-child(odd) { border-left: 0; }
}

@media (max-width: 720px) {
    /* The links take a row of their own under the mark and the button. */
    .bar { flex-wrap: wrap; row-gap: var(--s2); }
    .bar nav.site { order: 3; flex-basis: 100%; margin-left: 0; }
    .bar nav.account { margin-left: auto; }
}

@media (max-width: 560px) {
    main { padding: var(--s6) var(--s4) var(--s7); }
    body.marketing main { padding-inline: var(--s4); }
    .foot { padding: var(--s6) var(--s4) var(--s5); }
    .bar { padding: var(--s3) var(--s4); column-gap: var(--s3); }
    .bar nav.account { gap: var(--s3); }
    h1 { font-size: 28px; }
    .band { padding-block: var(--s7); }
    .icon-grid.three, .icon-grid.four, .checks.two { grid-template-columns: 1fr; }
    .icon-grid { gap: var(--s6); }
    .platform-row { gap: var(--s1); }
    .platform-row a { padding: var(--s2) 2px; }
    .platform-row strong { font-size: 16px; }
    .platform-row span { font-size: 12px; }
    .pairing { grid-template-columns: 1fr; }
    .pairing .ctrl { margin-top: 0; }
    .pairing .wire { display: none; }
    .facts dt { font-size: 20px; }
    .actions .cta { flex: 1 1 auto; }
    .foot-groups { gap: var(--s5); }

    /* One row per record, each cell labelled by the header it lost. The previous version hid <th> with no
     * substitute, so the devices and members tables became columns of unlabelled values on a phone. */
    table, tbody, tr, td { display: block; }
    thead { display: none; }
    td { border: 0; padding: var(--s1) 0; }
    td[data-label]::before {
        content: attr(data-label) ": ";
        color: var(--muted);
        font-size: 14px;
    }

    tbody tr { border-bottom: 1px solid var(--line); padding: var(--s3) 0; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
    .field b, .wire i, .phone-screen b { animation: none; }
}
