html, body {
    font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

a, .btn-link {
    color: #006bb7;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

h1:focus {
    outline: none;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}
/* ===================== QSync brand ===================== */
:root {
    --qs-bg: #0b0d17;
    --qs-bg-soft: #12152339;
    --qs-ink: #1a1d2b;
    --qs-muted: #6b7280;
    --qs-brand: #5b6cff;
    --qs-brand-2: #14b8a6;
    --qs-line: #e6e8ef;

    /* ⛔TEXT GREYS ARE TOKENS BECAUSE A THEME CANNOT REACH A HEX LITERAL.
       These five values were pinned directly into twenty-one rules, every one of them a grey chosen to
       sit on WHITE. On a dark background they run from dim to unreadable, and there is no way to
       restyle them without listing all twenty-one - at which point the twenty-second rule someone adds
       next month inherits nothing and fails silently.
       ⭐The values here are the ORIGINAL hex, unchanged, so the light site renders exactly as before. */
    --qs-ink-strong: #111827;   /* headings and emphasis */
    --qs-ink-2:      #374151;   /* strong body copy */
    --qs-ink-3:      #4b5563;   /* secondary body copy */
    --qs-muted-2:    #9ca3af;   /* the quietest label */
}
html { font-size: 14px; }
html, body { font-family: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; color: var(--qs-ink); background: #fff; }
a { text-decoration: none; }
*, *::before, *::after { box-sizing: border-box; }
/* ⭐⭐THE SIDE GUTTER SCALES WITH THE SCREEN, AND THE READING WIDTH DOES NOT.
   ⛔Owner, 2026-09-01, on an iPad: "no side margins destroys the page appeal. the text is 0 pixel
   touching or outside the screen itself. Horrible." He was right and the measurement agrees - the
   gutter was a FLAT 20px from 360px all the way to 1180px. A 360px phone and a 1024px iPad got the
   same twenty pixels, so the wider the tablet, the more the text looked pasted onto the glass.

   ⭐--qs-gutter grows with the viewport; CONTENT-MAX is the reading measure and is fixed. Keeping
   those two separate is the whole point: max-width has to include the padding (box-sizing is
   border-box everywhere here), so a bigger gutter inside a FIXED max-width would have quietly
   narrowed every desktop page by 72px while fixing the tablet. The calc() adds the gutter back, so
   the text inset at 1440/1920/2560/3840 is byte-identical to what it was before this change -
   measured, 170/410/730/1370px - and only 360-1180px moves.
   ⛔So do not hard-code a number in either declaration. Changing --qs-gutter alone must keep the
   desktop measure fixed; that invariant is what ContainerGutterTests holds. */
/* ⭐THE BRAND AT LOW ALPHA, as tokens rather than as `#5b6cff14` written out in nine places. A wash
   is a colour, so it belongs to the theme: theme-dark.css lifts the brand from #5b6cff to #6d7cff and
   a literal wash would keep pointing at the old one. */
:root {
    --qs-wash:   #5b6cff0d;                       /* a hover, a tinted panel */
    --qs-wash-2: #5b6cff1f;                       /* a chip, an inline code span */

    --qs-content-max: 1100px;                     /* the reading measure, gutters excluded */
    --qs-gutter: clamp(20px, 5vw, 56px);          /* 20px at 360, 38 at 768, 51 at 1024, 56 from 1120 */
}
.container { max-width: calc(var(--qs-content-max) + 2 * var(--qs-gutter));
             margin: 0 auto; padding: 0 var(--qs-gutter); }
.muted { color: var(--qs-muted); }

/* ⭐FIELD DESCRIPTIONS, 20% smaller (owner, 2026-09-01). MEASURED at 14px, so 14 x 0.8 = 11.2 -> 11px.
   ⛔A SEPARATE CLASS RATHER THAN CHANGING .muted, and the reason is arithmetic: .muted appears 76 times
   across 20 pages - footer copy, page captions, pricing notes, the AMCF register. Shrinking all of it to
   11px to fix the three hint lines under the sign-up fields would have been a site-wide type change
   nobody asked for, arriving invisibly in a commit about a form.
   ⚠11px is small ON PURPOSE and only appropriate BELOW the field it explains. Do not reach for this
   class for body copy - it is a hint, not a paragraph. */
.field-hint { font-size: 11px; line-height: 1.5; }
.site-main { min-height: 60vh; }

/* nav */
.site-nav { position: sticky; top: 0; z-index: 50; background: rgba(255,255,255,.9); backdrop-filter: blur(8px); border-bottom: 1px solid var(--qs-line); }
/* ⚠min-height, NOT height. A fixed height silently CLIPS a wrapped second row, which is exactly how
   eleven links became an invisible navigation bar instead of an ugly one. If the links ever wrap again
   the bar now grows and the fault is visible immediately. */
/* ⚠gap IS A FLOOR, NOT A LAYOUT. justify-content: space-between distributes FREE space, and on the
   French page there is none left - the row fills completely - so the QSYNC logo sat flush against the
   first nav link with no separation at all. Measured from a screenshot; English had spare room and
   never showed it. ⭐gap guarantees a minimum whatever the language does to the widths. */
.nav-inner { display: flex; align-items: center; justify-content: space-between; min-height: 48px; gap: 20px; }
.nav-brand { display: inline-flex; align-items: center; gap: 9px; font-weight: 800; font-size: 1.35rem; color: var(--qs-ink); letter-spacing: -.02em; }
.brand-mark { display: block; border-radius: 5px; }
.nav-brand span { color: var(--qs-brand); }
/* ⛔⛔flex-wrap: nowrap, AND IT IS LOAD-BEARING AT EVERY WIDTH - NOT JUST THE NARROW ONES.

   ⚠This rule said `wrap` until 2026-09-06 and it put the live nav on THREE ROWS. Once the language
   picker started rendering - it appears only after a second language publishes - the row no longer fit,
   .nav-links wrapped, and .nav-inner's align-items: center then centred the QSYNC LOGO against the
   two-row block. Readers got links, then the logo, then the picker.
   ⭐Measured from screenshots of the live site at 1440px, twice: my first two fixes both left it broken,
   because I was reasoning about the CSS instead of looking at the render.

   ⛔wrap CANNOT be right here. A horizontal bar that silently becomes two rows moves the logo and the
   call to action, and there is no width at which that reads as intended. nowrap makes a second row
   structurally impossible; if the content ever genuinely overflows, the breakpoints below tighten the
   gap and type, and the mobile rule turns the whole thing into a dropdown.

   ⚠The gap came down 26px -> 18px to buy back the ~110px the picker costs. There are nine gaps in this
   row, so that is ~72px - measured against the container's max width, not guessed. */
.nav-links { display: flex; gap: 18px; align-items: center; flex-wrap: nowrap; }

/* ⛔A NAV LABEL MUST NEVER BREAK MID-PHRASE. MEASURED on the French page: with the row set to nowrap,
   the items wrapped INSIDE themselves instead - "À propos" over two lines, "Obtenir une licence" over
   three - which reads as a broken bar rather than a tight one. ⚠Every translated label is longer than
   its English, so this only ever showed up once a language went live. */
.nav-links > *, .lang-switch > summary { white-space: nowrap; }

/* ⭐⭐VISUALLY HIDDEN, STILL READ ALOUD. The language button shows a two-letter code so the bar fits;
   this carries the full language name for a screen reader, which would otherwise announce the control
   as "FR" and leave a blind reader guessing.

   ⛔NOT display:none AND NOT visibility:hidden - both remove the text from the accessibility tree as
   well as from the page, which is the exact opposite of what is wanted here. ⚠MEASURED: the class was
   used in LanguagePicker before it existed in this file, so for one build the endonym rendered as
   VISIBLE text next to the code - the defect this rule exists to prevent, caused by its absence. */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
/* ⭐VERTICAL PADDING IS THE TOUCH TARGET, and it is not cosmetic. From 901px up the nav shows the full
   link row, and 901-1366 is where TABLETS live - iPad Pro 12.9 portrait is 1024, Surface Pro portrait is
   912, iPad Air landscape is 1180. Those are FINGERS on a desktop-shaped bar. Without this the anchor is
   only as tall as its text, about 20px, against the ~44px a touch target needs, so every one of those
   users gets a row of links that are hard to hit accurately.
   ⚠It costs nothing on a mouse and .nav-inner's min-height absorbs it, so the bar stays 48px.
   ⛔:not(.btn-buy) because Get-a-licence carries its own padding and this selector would outrank it. */
.nav-links a { color: var(--qs-ink-2); font-weight: 500; }
.nav-links a:not(.btn-buy) { padding: 12px 0; }
.nav-links a:hover { color: var(--qs-brand); }
.nav-toggle, .nav-burger { display: none; }
.nav-burger { width: 44px; height: 44px; cursor: pointer; flex-direction: column; justify-content: center; gap: 5px; padding: 10px; margin: -10px; }
.nav-burger span { display: block; height: 2px; border-radius: 2px; background: var(--qs-ink); }
.btn-buy { background: var(--qs-brand); color: #fff !important; padding: 9px 16px; border-radius: 9px; font-weight: 600; }
.btn-buy:hover { filter: brightness(1.08); }

/* hero */
.hero { background: radial-gradient(1200px 500px at 70% -10%, #5b6cff22, transparent), linear-gradient(180deg, #0b0d17, #161a2e); color: #fff; padding: 18px 0 18px; }
.hero h1 { font-size: clamp(1.8rem, 4vw, 2.8rem); font-weight: 800; letter-spacing: -.03em; max-width: 18ch; line-height: 1.05; }
.hero .lead { color: #c3c8da; font-size: 1.05rem; max-width: 62ch; margin-top: 8px; }
/* ⛔⛔"THE BANNER STILL HAS LINEFEEDS" (owner, 2026-08-30), AND HE WAS RIGHT WHILE I WAS LOOKING AT
   THE WRONG THING. The rendered HTML is one unbroken line - I checked that and reported it - but what
   a reader sees is the 62ch measure above folding a 210-character sentence into four short lines
   inside a 1140px banner. The line feeds were real; they were just made by CSS, not by the content.
   ⚠65ch is the right measure for a COLUMN of prose. This is a single wide intro sentence under a
   heading, so it gets the container's width and wraps twice instead of four times. */
.hero-wide .lead { max-width: none; }
.hero-cta { margin-top: 12px; display: flex; gap: 10px; flex-wrap: wrap; }
.btn-primary, .btn-ghost { padding: 13px 22px; border-radius: 11px; font-weight: 600; display: inline-block; }
.btn-primary { background: var(--qs-brand); color: #fff; }
.btn-primary:hover { filter: brightness(1.08); }
.btn-ghost { border: 1px solid #ffffff44; color: #fff; }
.btn-ghost:hover { background: #ffffff14; }

/* sections */
section { padding: 5px 0; }
section h2 { font-size: 1.5rem; font-weight: 800; letter-spacing: -.02em; margin-bottom: 4px; }
.why-grid { display: grid; gap: 12px; margin-top: 10px; }
.why-grid { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.why-grid h3 { font-size: 1.15rem; }
/* ⭐FLEX, NOT GRID, and deliberately so. There are FIVE app tiles. A 3-column grid leaves the last two
   stranded against the left edge with a dead third column - it reads as a mistake rather than a layout.
   Flex-wrap with justify-content:center CENTRES the trailing row, so 3+2 looks intentional. It also
   survives the count changing: 4 tiles give 3+1 centred, 6 give 3+3, without touching this rule.
   ⚠max-width stops a lone trailing card stretching to the full container width. */
.product-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; margin-top: 10px; }
.product-grid > .product-card { flex: 1 1 300px; max-width: 366px; }

/* product cards */
.product-card { display: block; border: 1px solid var(--qs-line); border-radius: 14px; padding: 16px; background: #fff; transition: transform .15s, box-shadow .15s, border-color .15s; }
.product-card:hover { transform: translateY(-3px); box-shadow: 0 12px 30px #0b0d1714; border-color: var(--accent, var(--qs-brand)); }
.product-card .card-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.product-card .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent, var(--qs-brand)); }
.product-card .platforms { color: var(--qs-muted); font-size: .82rem; }
.product-card h3 { font-size: 1.3rem; color: var(--qs-ink); }
.product-card .tagline { color: var(--qs-ink-3); margin: 4px 0 10px; }
/* Headline capabilities on the home tile. Tick markers in the accent colour so the list reads as
   "what you get" at a glance rather than as body copy. Sized down from the tagline so the card still
   has a clear hierarchy: name, promise, proof, call to action. */
.product-card .card-highlights { list-style: none; margin: 0 0 12px; padding: 0; }
.product-card .card-highlights li { position: relative; padding-left: 18px; margin: 5px 0; color: var(--qs-ink-3); font-size: .88rem; line-height: 1.4; }
.product-card .card-highlights li::before { content: "\2713"; position: absolute; left: 0; top: 0; color: var(--accent, var(--qs-brand)); font-weight: 700; }
/* An add-on is not a standalone app. The tile has to say so, or a buyer reads it as one. */
.badge-addon { background: color-mix(in srgb, var(--accent, var(--qs-brand)) 14%, #fff); color: var(--accent, var(--qs-brand)); }
.product-card .learn { color: var(--accent, var(--qs-brand)); font-weight: 600; }
.badge { display: inline-block; background: #f3f4f6; color: var(--qs-muted); font-size: .72rem; font-weight: 700; padding: 3px 9px; border-radius: 99px; margin-right: 10px; text-transform: uppercase; letter-spacing: .04em; }

/* product detail */
.detail-hero { color: #fff; padding: 16px 0; }
.detail-hero h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 800; letter-spacing: -.02em; }
.detail-hero .tagline { font-size: 1.15rem; opacity: .9; margin-top: 6px; }
.pain { background: var(--qs-bg-soft); border-left: 4px solid var(--qs-brand); padding: 12px 18px; border-radius: 10px; font-size: 1.02rem; color: var(--qs-ink-2); }
.feature-list { list-style: none; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(280px,1fr)); gap: 8px; }
.feature-list li { padding: 12px 14px 12px 38px; position: relative; border: 1px solid var(--qs-line); border-radius: 10px; }
.feature-list li::before { content: "\2713"; position: absolute; left: 14px; color: var(--qs-brand-2); font-weight: 800; }

/* partners */
.partner-list { display: grid; gap: 16px; margin-top: 10px; }
.partner-card { border: 1px solid var(--qs-line); border-radius: 16px; padding: 24px; background: #fff; }
.partner-head { margin-bottom: 16px; }
/* ⭐Added 2026-09-16 with the first CONTENT partner. The kind label uses the same treatment as the
   home page's .partner-strip-label so the two surfaces read as one vocabulary, and .partner-name is
   the fallback for a partner who has given us no logo - a text name, never a mark we invented. */
.partner-kind { margin: 0 0 8px; font-size: .82rem; letter-spacing: .08em; text-transform: uppercase; color: var(--qs-ink-2); }
.partner-name { margin: 0; font-size: 1.5rem; line-height: 1.2; }
.partner-logo { height: 52px; width: auto; display: block; }
.partner-body { display: grid; grid-template-columns: 1.1fr .9fr; gap: 28px; align-items: center; }
.partner-figure { margin: 0; }
/* Owner ruling 2026-08-11: NO ROUNDED CORNERS ON ARTWORK, anywhere in QSync. Real sleeves and posters have square corners; rounding them is a gimmick that makes our own artwork look like someone else's chrome. Applies to covers, posters, artist images and stills - NOT to buttons, chips or cards. */
.partner-figure img { max-width: 100%; height: auto; border-radius: 0; }
.partner-quote { margin: 16px 0 0; padding: 14px 20px; border-left: 4px solid var(--qs-brand); background: var(--qs-bg-soft); border-radius: 10px; font-size: 1.05rem; font-style: italic; color: var(--qs-ink-2); }
.partner-links { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 18px; }

/* ⭐The home page credit strip. Owner, 2026-09-15: Charlie at Involve Audio asked for their mark on
   the home page. ⛔It is a CREDIT, not a partner block - the partnership itself stays on /partners,
   which is the ruling written on Devices.razor. Quiet, centred, and it gets out of the way of the
   call to action underneath it.
   ⚠The logo keeps .partner-logo, so it inherits the light plate the dark theme already gives a
   trademark that was drawn for white. One treatment, defined once. */
/* ⚠TIGHT ON PURPOSE. Owner, 2026-09-15, on the first render: "the logo is good, less vert space above
   below". A credit line sits between a section and the call to action, so generous padding reads as a
   gap in the page rather than as breathing room around the mark. */
.partner-strip { padding: 6px 0 0; }
.partner-strip .container { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.partner-strip-label { margin: 0; font-size: .82rem; letter-spacing: .08em; text-transform: uppercase; }
.partner-strip .partner-logo { height: 54px; }
/* ⚠Scoped to the strip, so the Partners page plate is untouched: there the logo heads a card and the
   padding is right; here the same padding is white space the mark does not need. */
.partner-strip .partner-logo { padding-top: 4px; padding-bottom: 4px; }
/* ⭐The visitor count at the foot of the home page (owner, 2026-09-16: "buried on bottom of the home
   page as just small text font number"). Deliberately the quietest thing on the page: small, muted and
   centred, below the call to action. It is a number for us, not a claim at the reader. */
.visit-count {
    margin: 0;
    padding: 10px 0 18px;
    text-align: center;
    font-size: .72rem;
    letter-spacing: .04em;
    color: var(--qs-ink-2);
    opacity: .55;
}

.partner-link { color: var(--qs-brand); font-weight: 600; }
.partner-link:hover { text-decoration: underline; }

/* pricing */
/* Currency switcher - small native <details> dropdown at the top of the pricing page. */
.ccy-switch { position: relative; float: right; margin: 2px 0 0; }
.ccy-switch > summary { list-style: none; cursor: pointer; user-select: none;
    border: 1px solid var(--qs-line); border-radius: 9px; padding: 6px 12px; font-weight: 600;
    display: inline-flex; align-items: center; gap: 6px; background: transparent; }
.ccy-switch > summary::-webkit-details-marker { display: none; }
.ccy-switch > summary .chev { color: var(--qs-muted); font-size: .8em; }
.ccy-switch[open] > summary { border-color: var(--qs-brand); }
.ccy-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 20; min-width: 128px;
    background: var(--qs-card, #fff); border: 1px solid var(--qs-line); border-radius: 10px;
    box-shadow: 0 12px 30px #0003; padding: 4px; }
.ccy-menu a { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-radius: 7px;
    text-decoration: none; color: inherit; font-weight: 500; }
.ccy-menu a:hover { background: #5b6cff14; }
.ccy-menu a.sel { color: var(--qs-brand); font-weight: 700; }
.ccy-menu a .sym { display: inline-block; min-width: 1.6em; color: var(--qs-muted); }

/* ===== the LANGUAGE menu only: 42 entries do not fit on a phone =====================================
   ⛔OWNER, 2026-09-22, on a phone: "so long it cant be used. The space between text is HUGE."
   .ccy-menu was written for the CURRENCY switch - four entries, where a panel that grows to fit is
   right and needs no limit. The language picker reuses the class and now carries 42 languages, so the
   panel became about 1,390px tall on a ~750px screen: most of the list simply could not be reached.

   ⭐--lang-rows IS THE KNOB, and it is a COUNT rather than a pixel height so it stays true if the row
     padding or the font ever changes. Eight rows is what the owner asked for.
   ⚠SCOPED TO .lang-switch. Putting a scrollbar on .ccy-menu would give the four-entry currency menu
     one too, and a scrollbar on a list that fits claims there is more when there is not. */
.lang-switch .ccy-menu {
    --lang-row: 2rem;                 /* one row: 6px + 6px padding around a 1.25 line-height */
    --lang-rows: 8;
    /* ⛔EIGHT ROWS *OR* WHAT THE SCREEN ACTUALLY HAS, WHICHEVER IS SMALLER. A fixed eight rows is
       still ~256px, and this file already learned the hard way that a phone in LANDSCAPE is ~375px
       tall with chrome - the burger menu had exactly this defect and its comment above records that
       at 667x375 only six of twelve items were reachable.
       ⭐dvh first with vh as the fallback, same as that block and for the same reason: 100vh on a
       phone is the CHROME-LESS height, so a vh-only cap still hides the last row behind the bar.
       Both are declared; later wins where supported. */
    max-height: min(calc(var(--lang-row) * var(--lang-rows)), calc(100vh - 110px));
    max-height: min(calc(var(--lang-row) * var(--lang-rows)), calc(100dvh - 110px));
    overflow-y: auto;
    /* ⛔Without this, flicking past the end of the list scrolls the PAGE behind it - on a phone the
       menu then appears to jump away under the thumb. */
    overscroll-behavior: contain;
}
/* ⭐The rows are tighter than the currency menu's because there are 42 of them and they are one word
   each. This is the "space between text is HUGE" half of the report, and it is a real change rather
   than a side effect of the max-height. */
.lang-switch .ccy-menu a { padding: 6px 10px; line-height: 1.25; }

/* ⛔A FIXED TRACK COUNT, NOT auto-fit. auto-fit with 1fr STRETCHES whatever is present, so a product
   with only two tiers got two cards half again as wide as every other product's, and nothing lined up
   down the page. QSYNC Media Reports has exactly two, and it is the last row on the page - the one a
   reader compares everything against.
   ⚠The old comment beside this rule reasoned about three tiers and four. It never considered TWO,
   which is the case that actually shipped.
   ⭐Still no arithmetic in the markup: three columns by default, four if a fourth card exists (so
   Developer returning needs no change here), and a single card - an add-on on its own - keeps the full
   width it had. :has() reads the child count so the razor does not have to count. */
.tier-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-top: 4px; }
.tier-grid:has(> :nth-child(4)) { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.tier-grid:has(> :only-child)   { grid-template-columns: 1fr; }
.tier { border: 1px solid var(--qs-line); border-radius: 14px; padding: 14px; }
.tier.highlight { border-color: var(--qs-brand); box-shadow: 0 10px 30px #5b6cff1f; }
/* ===================== Tier identity colours (owner, 2026-08-30) =====================
   ⭐One colour per RUNG, used everywhere that rung appears: Basic green, Pro orange, Pro+ QSYNC blue,
   and QSM purple. The point is recognition - a reader scanning five products should be able to find
   "the Pro one" by colour before reading a word.

   ⛔KEYED ON THE EDITION CODE, never on the display name. `basic`/`pro`/`proPlus`/`proPlusSM` are the
   same codes the licensing DB and the checkout URLs use, so a tier renamed in marketing copy keeps its
   colour and a NEW tier gets no colour rather than silently borrowing one.

   ⚠These are text colours on white, so they are the DARKER end of each hue - #22c55e green and #f97316
   orange look right as a fill and fail contrast as 1.2rem type. Blue is the existing brand token
   rather than a second blue that would drift from it. */
:root {
    --qs-tier-basic: #15803d;
    --qs-tier-pro:   #ea580c;
    --qs-tier-plus:  var(--qs-brand);
    --qs-tier-qsm:   #7c3aed;
}
.tier h3.t-basic { color: var(--qs-tier-basic); }
.tier h3.t-pro   { color: var(--qs-tier-pro); }
.tier h3.t-plus  { color: var(--qs-tier-plus); }
.tier h3.t-qsm   { color: var(--qs-tier-qsm); }

/* The bundle cards carry the same identity as an OUTLINE. ⚠The tint in the shadow is the same hex at
   1f alpha, matching how .tier.highlight already does it, so the cards read as a set. */
.tier.b-pro  { border-color: var(--qs-tier-pro);  box-shadow: 0 10px 30px #ea580c1f; }
.tier.b-plus { border-color: var(--qs-tier-plus); box-shadow: 0 10px 30px #5b6cff1f; }
.tier.b-qsm  { border-color: var(--qs-tier-qsm);  box-shadow: 0 10px 30px #7c3aed1f; }

.tier h3 { font-size: 1.2rem; }
/* Add-on cards carry a full product name ("QSync Surround Master (QSM)"), not a one-word tier label,
   and the add-on is a product in its own right rather than a rung on the ladder. Give it room -
   name and headline price both, so the card reads as a product rather than a footnote to one. */
.tier.addon h3 { font-size: 1.55rem; line-height: 1.25; }
.tier.addon .price { font-size: 2.1rem; }
/* The "or $5/mo" secondary line stays small - the annual figure is the one being led with. */
.tier.addon .price small { font-size: .8rem; }
.tier .price { font-size: 1.8rem; font-weight: 800; margin: 4px 0 10px; }
/* ⚠margin-left, because Razor eats the whitespace between `@bundlePrice` and `<small>`: the bundle
   cards rendered "$100/yrper year" with the price and the period touching. Seen in a screenshot, not
   in the markup, where the two sit on separate lines and look fine. */
.tier .price small { font-size: .9rem; color: var(--qs-muted); font-weight: 500; margin-left: 4px; }
/* ⚠The per-product cards put the period on its OWN line (display:block inline-styled), so the margin
   above would indent it. Reset for that case. */
.tier .price small[style*="block"] { margin-left: 0; }
/* ⭐Tightened 2026-08-30 (owner). The rows were 4px top and bottom PLUS the ul's default 1em margin,
   so a five-feature tier spent more height on gaps than on words and the cards ran long enough to
   push the availability line out of sight on a laptop. */
/* ⛔⛔PADDING WAS NOT THE GAP. Reducing it 4px -> 2px on 2026-08-30 changed almost nothing visible,
   and the owner said so: the row height is dominated by LINE-HEIGHT, which was inheriting the body's
   1.5 on a 14px base - about 21px of the ~25px row. A feature is one short line, so it does not need
   paragraph leading. Tightening line-height is what actually closes the gap. */
.tier ul { list-style: none; padding: 0; margin: 4px 0 0; }
.tier li { padding: 1px 0; line-height: 1.35; border-top: 1px solid var(--qs-line); color: var(--qs-ink-3); }

/* ⚠.tier-availability lived here for about an hour on 2026-08-30 and is DELETED, not kept "in case":
   the owner removed the "Available at launch" wording entirely the same afternoon. A CSS rule with no
   markup behind it is dead weight that the next reader has to prove is dead. */

/* Player pricing: fixed 3-column row - Basic | Pro/Pro+ stack | Developer.
   Add-ons moved OUT of this row into their own section below, so the ladder reads as a ladder.
   Cards size to their own content (align-items:start) so an uneven stack doesn't stretch the rest. */
/* Two columns when the Developer tier is not published. Declared AFTER the 3-column rule so it wins on
   source order - both are single-class selectors, so specificity alone would not decide it. */
/* footer */
.site-footer { border-top: 1px solid var(--qs-line); padding: 14px 0 10px; margin-top: 12px; background: #fafbff; }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 20px; }
.footer-brand { display: inline-flex; align-items: center; gap: 8px; font-weight: 800; font-size: 1.15rem; }
.footer-brand span { color: var(--qs-brand); }
.site-footer h6 { font-weight: 700; text-transform: uppercase; font-size: .75rem; letter-spacing: .06em; color: var(--qs-muted-2); margin-bottom: 6px; }
.site-footer a { display: block; color: var(--qs-ink-3); padding: 1px 0; }
.site-footer a:hover { color: var(--qs-brand); }
.copyright { margin-top: 14px; font-size: .85rem; }

/* ⚠BREAKPOINT RAISED 720 -> 900 on 2026-08-07, when App UI and FAQ were added to the nav.
   Nine links plus the Get-a-licence button no longer fit on one row below about 900px: the row wrapped
   and "Support" landed on top of the brand and the button. Measured at 760px before the change.
   ⭐If another nav item is ever added, re-measure this - the number is a consequence of the link count,
   not a round number someone liked. */
@media (max-width: 900px) {
    html, body { overflow-x: hidden; max-width: 100%; }
    .nav-inner > * { min-width: 0; }
    .hero-home .hero-copy h1 { white-space: normal; }
    .why-grid, .feature-list, .tier-grid, .partner-body { grid-template-columns: 1fr; }
    /* ⚠.product-grid is FLEX now, so grid-template-columns does nothing to it. One card per row on
       mobile has to come from the flex-basis instead, or the tiles stay side by side and squash. */
    .product-grid > .product-card { flex: 1 1 100%; max-width: none; }
    .hero { padding: 22px 0 20px; }
    .hero h1 { font-size: clamp(1.7rem, 7vw, 2.3rem); }
    .hero .lead { font-size: 1.08rem; }
    .footer-grid { grid-template-columns: 1fr; }
}
/* ⛔⛔THE NAV KEEPS ITS LINKS VISIBLE. A BURGER ON A DESKTOP READS AS A BROKEN TOOLBAR.
   ⚠OWNER, 2026-08-31: "the website toolbar no longer is visible and looks corrupted". He was looking
   at a window under 1180px, where an earlier version of this rule swapped the whole link row for a
   burger. That FIXED the clipping but it was the wrong trade - it hid the navigation on exactly the
   widths a laptop uses, and a lone hamburger on a wide screen looks like a page that failed to load.
   ⭐SO THE ROW TIGHTENS INSTEAD OF COLLAPSING. Twelve items - eleven links plus Get-a-licence - need
   about 1150px at the full 26px gap. Between 901px and 1180px the gap, the type and the brand shrink
   by measured amounts so all twelve still FIT ON ONE ROW and stay readable. The burger is for phones
   and small tablets, which is what it was always for.
   ⛔THESE NUMBERS ARE A CONSEQUENCE OF THE ITEM COUNT. Add or remove a nav item and RE-MEASURE them.
   NavCollapseBreakpointTests fails the build if the count moves, because the comment that used to say
   this was ignored twice - once when Atmos Mix Funding was added on 2026-08-28 and again when News was
   added on 2026-08-30, which is what produced a nav that did not fit at all. A comment is not a check. */
@media (max-width: 1180px) and (min-width: 901px) {
    .nav-links { gap: 14px; font-size: .92rem; }
    .nav-brand { font-size: 1.2rem; }
    .btn-buy { padding: 8px 12px; }
}

@media (max-width: 1040px) and (min-width: 901px) {
    .nav-links { gap: 11px; font-size: .88rem; }
    .nav-brand { font-size: 1.1rem; }
    .btn-buy { padding: 7px 11px; }
}

/* ⭐The burger is the PHONE and small-tablet form, and it keeps its own block rather than joining the
   shared 900px one below: that block also switches the hero, the footer and the product cards to their
   single-column layouts, so anyone re-measuring the nav would have restyled half the site by accident. */
@media (max-width: 900px) {
    .nav-burger { display: flex; }
    .nav-links {
        display: none;
        position: absolute; top: 64px; left: 0; right: 0;
        /* ⛔nowrap IS LOAD-BEARING HERE. .nav-links carries flex-wrap: wrap from the base rule, and a
           COLUMN flex box with a capped height does not overflow when it is allowed to wrap - it starts a
           SECOND COLUMN. Adding the max-height below without this produced a two-column menu with a
           horizontal scrollbar and half the links off the right edge. Seen 2026-08-31 the moment the cap
           was added, which is the only reason it was caught. */
        flex-direction: column; flex-wrap: nowrap; align-items: stretch; gap: 2px;
        background: #fff; border-bottom: 1px solid var(--qs-line);
        /* ⭐the same gutter as the page, so the menu links line up under the wordmark instead of
           sitting 25px to its left on a tablet-width phone. */
        padding: 10px var(--qs-gutter) 18px; box-shadow: 0 16px 30px #0000001a;
        /* ⛔THE OPEN MENU MUST SCROLL, OR ITS BOTTOM ITEMS DO NOT EXIST.
           Twelve items stack to about 570px. This panel is absolutely positioned inside a STICKY bar,
           so it does not lengthen the page and scrolling cannot bring the lower items into view - they
           are simply unreachable on any viewport shorter than the menu.
           ⚠MEASURED 2026-08-31 on the live site: at 667x375 (an ordinary phone in LANDSCAPE) only SIX
           of the twelve were reachable, and at 320x568 (iPhone SE) "Get a licence" - the buy button -
           could not be reached at all. A visitor could see the menu open and still not get to Support,
           Downloads or checkout.
           ⭐dvh first for the units, with vh as the fallback for anything that does not know dvh: on a
           phone 100vh is the chrome-less height, so a vh-only rule still hides the last item behind the
           browser bar. Both are declared, later wins where supported. */
        max-height: calc(100vh - 64px);
        max-height: calc(100dvh - 64px);
        overflow-y: auto;
        overscroll-behavior: contain;
    }
    .nav-toggle:checked ~ .nav-links { display: flex; }
    .nav-links a { padding: 12px 2px; width: 100%; }
    .nav-links .btn-buy { margin-top: 8px; text-align: center; }
    /* ⚠IN THE MOBILE DROPDOWN THE BAR IS A FULL-WIDTH COLUMN, not a row - so there is no "right side"
       to anchor to, and margin-left: auto would shove the picker off to one edge of its own line.
       ⭐Reset it to sit in the column like every other entry. */
    /* ⚠THE PICKER IS NO LONGER INSIDE .nav-links, so it does not live in the mobile dropdown either -
       it stays in the bar beside the burger, where it is a 100px control on a 430px bar. ⭐Nothing to
       reset: the desktop rule adds no margin or width for this to undo. */
    .lang-switch > summary { min-width: 0; }
}

/* ===================== live product demo ===================== */
.demo-wrap { display: flex; flex-direction: column; align-items: center; }
.demo-phone { width: 320px; max-width: 92vw; background: #0e1120; border: 1px solid #ffffff1a; border-radius: 28px; padding: 18px; box-shadow: 0 30px 70px #00000066; }
.demo-np { background: linear-gradient(165deg, #161a2e, #0b0d17); border-radius: 18px; padding: 18px; text-align: center; }
.demo-art { width: 190px; height: 190px; margin: 4px auto 16px; border-radius: 0; box-shadow: 0 14px 34px #00000055; }
.demo-track { color: #fff; font-weight: 700; font-size: 1.1rem; }
.demo-artist { color: #aab0c6; font-size: .92rem; margin-top: 2px; }
.demo-progress { height: 5px; background: #ffffff1f; border-radius: 99px; margin: 16px 4px 14px; overflow: hidden; }
.demo-progress span { display: block; height: 100%; background: var(--qs-brand); transition: width .35s ease; }
.demo-controls { display: flex; align-items: center; justify-content: center; gap: 22px; }
.demo-controls button { background: none; border: none; color: #fff; font-size: 1.5rem; cursor: pointer; line-height: 1; padding: 4px; }
.demo-controls .play { width: 56px; height: 56px; border-radius: 50%; background: var(--qs-brand); font-size: 1.4rem; display: inline-flex; align-items: center; justify-content: center; }
.demo-controls .play:hover { filter: brightness(1.1); }
.demo-list { margin-top: 14px; display: flex; flex-direction: column; gap: 6px; }
.demo-item { display: flex; align-items: center; gap: 12px; text-align: left; background: none; border: none; cursor: pointer; padding: 8px; border-radius: 12px; color: #e7e9f3; }
.demo-item:hover { background: #ffffff0d; }
.demo-item.on { background: #5b6cff22; outline: 1px solid #5b6cff55; }
.demo-thumb { width: 42px; height: 42px; border-radius: 0; flex: none; }
.demo-meta { display: flex; flex-direction: column; line-height: 1.25; }
.demo-meta small { color: #aab0c6; }
.demo-cap { margin-top: 16px; color: #aab0c6 !important; font-size: .85rem; }
/* ===================== live demo: tablet ===================== */
.demo-tablet { width: 760px; max-width: 95vw; aspect-ratio: 16 / 11; background: #0e1120; border: 1px solid #ffffff1a; border-radius: 26px; padding: 16px; box-shadow: 0 30px 70px #00000066; display: grid; grid-template-columns: 44% 56%; gap: 14px; }
.demo-np2 { background: linear-gradient(165deg, #161a2e, #0b0d17); border-radius: 18px; padding: 22px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; }
.demo-art2 { width: 200px; height: 200px; max-width: 78%; aspect-ratio: 1; border-radius: 0; box-shadow: 0 14px 34px #00000055; margin-bottom: 14px; }
.demo-list2 { background: #0b0d17; border: 1px solid #ffffff0d; border-radius: 18px; padding: 10px; overflow: auto; }
@media (max-width: 680px) {
    .demo-tablet { grid-template-columns: 1fr; aspect-ratio: auto; }
    .demo-art2 { width: 160px; height: 160px; }
}
/* ===================== Home hero: copy + animated waveform ===================== */
.hero-home .container { display: grid; grid-template-columns: .9fr 1.1fr; gap: 48px; align-items: center; }
/* home headline: small + LIGHT weight, held on one line on DESKTOP ONLY.
   ⛔⛔THIS ONE-LINE RULE TOOK THE MOBILE NAV OFF THE SCREEN. `white-space: nowrap` used to live
   here, in the base block - which sits AFTER the max-width:900px block that tries to set it back to
   `normal`. Same specificity, later source order, so NOWRAP WON ON PHONES TOO. The headline could not
   wrap, the page became about 496px wide, and everything past the viewport was clipped: on a 390px
   phone that is the whole right-hand side of the header, INCLUDING THE BURGER MENU. The site had no
   navigation at all on a phone, and nothing looked broken - the brand still rendered.
   ⭐Measured 2026-08-31: gone at 390 and 430, present at 500, which is exactly where a ~57-character
   headline at this font size stops fitting.
   ⭐Now scoped to a MIN-WIDTH query, so it cannot reach a narrow screen no matter where it sits in the
   file. An override that depends on source order is one reorder away from silently coming back. */
.hero-home .hero-copy h1 { max-width: none; font-weight: 300; letter-spacing: -.01em; font-size: clamp(1.05rem, 1.9vw, 1.2rem); line-height: 1.25; }
@media (min-width: 901px) {
    .hero-home .hero-copy h1 { white-space: nowrap; }
}
.hero-visual { display: flex; justify-content: center; align-items: center; min-height: 340px; }
.channel-wave { width: 100%; max-width: 600px; aspect-ratio: 16 / 9; height: auto; display: block; }
@media (max-width: 900px) {
    .hero-home .container { grid-template-columns: 1fr; }
    .hero-visual { display: none; }
}

/* ===================== Devices page ===================== */
/* .devices-partners removed 2026-08-06 - the partner band moved to /partners. */
.devices-supported, .devices-recos { padding: 4px 0; }
.dev-group { margin-top: 12px; }
.dev-label { font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--qs-muted-2); margin-bottom: 6px; }
.dev-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.dev-chip { font-weight: 600; font-size: .9rem; color: var(--qs-ink-2); background: #f3f4f6; border: 1px solid var(--qs-line); padding: 7px 14px; border-radius: 10px; }
.dev-card { border: 1px solid var(--qs-line); border-radius: 14px; padding: 14px 16px 11px; background: #fff; }
.dev-card h3 { font-size: 1.12rem; margin-bottom: 8px; }
.dev-specs { grid-template-columns: 1fr; gap: 0; margin: 4px 0 0; }
.dev-specs li { border: none; padding: 3px 0 3px 26px; }
.dev-specs li::before { left: 2px; }
.ast { color: var(--qs-brand); font-weight: 700; }
.foot { color: var(--qs-muted); font-size: .85rem; margin: 5px 0 0; }
.partner-band { border: 1px solid var(--qs-line); border-radius: 16px; padding: 18px 20px; background: linear-gradient(180deg,#f7f8ff,#fff); box-shadow: 0 10px 30px #5b6cff12; }
.partner-band h2 { margin-bottom: 6px; }
.partner-band p { max-width: 70ch; }
.partner-cta { margin-top: 10px; font-weight: 600; }
.partner-cta a { color: var(--qs-brand); }

/* Product page hero screenshot */
.shot-band { background: linear-gradient(180deg,#0b0d17,#0e1120); padding: 24px 0; }
.product-shot { width: min(1000px, 100%); border-radius: 0; border: 1px solid #ffffff14; box-shadow: 0 24px 70px #0009; }

/* ===================== Home hero: beta pill, spec chips, note ===================== */
.hero-beta { display: inline-flex; align-items: center; gap: 8px; font-size: .74rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: #d7dbea; background: #ffffff12; border: 1px solid #ffffff26; padding: 5px 12px; border-radius: 99px; margin-bottom: 10px; }
.hero-beta .dot { width: 7px; height: 7px; border-radius: 50%; background: #14b8a6; box-shadow: 0 0 10px #14b8a6; }
.hero-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.hero-chips .chip { font-size: .82rem; font-weight: 600; color: #e7e9f3; background: #ffffff10; border: 1px solid #ffffff20; padding: 6px 12px; border-radius: 10px; }
.hero-note { margin-top: 12px; color: #aab0c6; font-size: .9rem; }
.hero-note a { color: #8b97ff; font-weight: 600; }
.hero-note a:hover { color: #a7b0ff; }

/* ===================== Home: Social Music band ===================== */
/* full-bleed coloured band, but inner content stays in the shared container so it lines up with every other section */
.social { background: radial-gradient(1400px 460px at 18% -30%, #14b8a626, transparent), linear-gradient(180deg, #0b0d17, #101a1e); color: #fff; padding: 22px 0; margin-top: 4px; }
.social h2 { color: #fff; font-size: clamp(1.4rem, 3.4vw, 1.75rem); font-weight: 800; letter-spacing: -.02em; }
.social p { color: #c8cdde; max-width: none; margin-top: 8px; font-size: 1.05rem; line-height: 1.5; }
.social .social-note { color: #99a1b8; font-size: .85rem; margin-top: 12px; max-width: 66ch; }
.social .via { margin-top: 10px; color: #2dd4bf; font-weight: 700; font-size: .78rem; text-transform: uppercase; letter-spacing: .06em; }

/* ===================== App UI + FAQ =====================
   Shared section wrapper for the two pages added 2026-08-07. Uses the same tokens as .partner-card
   (var(--qs-line), var(--qs-bg-soft)) so these read as the same site rather than a bolt-on.
   ⚠.shot is NEW here - .shot-band and .product-shot already existed and are UNRELATED; do not merge them. */
.app-section { padding: 26px 0; border-top: 1px solid var(--qs-line); }
.app-section:first-of-type { border-top: 0; padding-top: 8px; }
.app-section h2 { font-size: clamp(1.3rem, 3vw, 1.6rem); font-weight: 800; letter-spacing: -.02em; margin-bottom: 6px; }
.app-section > p { color: var(--qs-ink-3); max-width: 74ch; line-height: 1.55; }

/* ⚠TWO columns, fixed - NOT auto-fit. auto-fit fitted THREE shots across at 1440px, which left the
   Media Player section as a row of three plus one orphan on its own line. A fixed pair makes four
   shots a tidy 2x2, and the single-figure section opts out via .shot-grid-one. */
.shot-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px; margin-top: 20px; }
.shot-grid-one { grid-template-columns: minmax(0, 820px); }
.shot { margin: 0; }
/* The trigger is a BUTTON, so every default has to be stripped before it can look like a picture. */
.shot-btn { display: block; position: relative; width: 100%; padding: 0; border: 0; background: none;
            cursor: zoom-in; border-radius: 12px; line-height: 0; }
.shot-btn img { width: 100%; height: auto; display: block; border: 1px solid var(--qs-line);
                border-radius: 12px; background: #fff; transition: border-color .15s, box-shadow .15s; }
.shot-btn:hover img { border-color: var(--qs-brand); box-shadow: 0 10px 26px #0b0d1721; }
.shot-btn:focus-visible { outline: 2px solid var(--qs-brand); outline-offset: 3px; }
/* A magnifier badge, because a bare screenshot gives no hint that it is clickable. Revealed on hover
   and focus, and left visible on touch, where there is no hover to reveal it. */
.shot-cue { position: absolute; right: 10px; bottom: 10px; display: grid; place-items: center;
            width: 34px; height: 34px; border-radius: 9px; color: #fff; background: #0b0d17b8;
            opacity: 0; transition: opacity .15s; pointer-events: none; }
.shot-btn:hover .shot-cue, .shot-btn:focus-visible .shot-cue { opacity: 1; }
@media (hover: none) { .shot-cue { opacity: .9; } }
.shot figcaption { margin-top: 8px; font-size: .88rem; color: var(--qs-muted); line-height: 1.45; }
.shot-hint { color: var(--qs-muted); font-size: .9rem; margin-bottom: 2px; }

/* ---------- lightbox ---------- */
body.lightbox-open { overflow: hidden; }
.lightbox { position: fixed; inset: 0; z-index: 200; background: #0b0d17f2; display: flex;
            flex-direction: column; align-items: center; justify-content: center; gap: 14px; padding: 28px; }
.lightbox[hidden] { display: none; }
/* Capped on BOTH axes. The report shots are tall portrait pages and would otherwise run off the top
   and bottom of the viewport - the one thing this overlay exists to prevent. */
.lightbox-img { max-width: 100%; max-height: calc(100vh - 130px); width: auto; height: auto;
                object-fit: contain; border-radius: 10px; background: #fff; cursor: default; }
.lightbox-cap { color: #c8cdde; font-size: .92rem; text-align: center; max-width: 80ch; margin: 0; }
.lightbox-close { position: absolute; top: 16px; right: 20px; width: 44px; height: 44px; border: 0;
                  border-radius: 10px; background: #ffffff1a; color: #fff; font-size: 1.9rem;
                  line-height: 1; cursor: pointer; }
.lightbox-close:hover { background: #ffffff2e; }
.lightbox-close:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* Native <details> accordion: no JavaScript, works with the keyboard, and survives with styles off. */
.faq { border: 1px solid var(--qs-line); border-radius: 12px; padding: 0; margin-top: 10px; background: #fff; }
.faq > summary { cursor: pointer; list-style: none; padding: 14px 18px; font-weight: 650; color: var(--qs-ink-strong); display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.faq > summary::-webkit-details-marker { display: none; }
.faq > summary::after { content: "+"; font-weight: 700; color: var(--qs-brand); font-size: 1.2rem; line-height: 1; flex: none; }
.faq[open] > summary::after { content: "\2212"; }
.faq[open] > summary { border-bottom: 1px solid var(--qs-line); }
.faq > p { margin: 0; padding: 14px 18px; color: var(--qs-ink-2); line-height: 1.6; }
.faq > summary:focus-visible { outline: 2px solid var(--qs-brand); outline-offset: -2px; border-radius: 12px; }

@media (max-width: 640px) {
  .shot-grid { grid-template-columns: 1fr; gap: 18px; }
}

/* ===================== Home: the four-app block diagram =====================
   ⭐Text INSIDE the SVG is styled here, not with presentation attributes, so it inherits the site font
   and can be tuned in one place. SVG text does not inherit body font automatically in every engine,
   hence the explicit family. */
.arch { margin-top: 34px; padding-top: 26px; border-top: 1px solid var(--qs-line); }
.arch h3 { font-size: 1.15rem; font-weight: 750; color: var(--qs-ink); text-align: center; margin-bottom: 6px; }
.arch-svg { display: block; width: 100%; max-width: 900px; height: auto; margin: 0 auto; }

/* ===== the product blocks are links (owner, 2026-09-01) =====
   ⛔A HOTSPOT NOBODY CAN SEE IS NOT NAVIGATION. Five boxes that silently became clickable look exactly
   like five boxes that did not, so the affordance is the feature - without it the work is invisible and
   the only people who find it are the ones who click at random.
   ⭐Three signals, because one is not enough for all three ways in: the POINTER changes for a mouse, the
   outline thickens and the name underlines for anyone hovering or tabbing, and focus-visible draws a
   real ring for the keyboard. Colour is deliberately not one of them - every box on this diagram already
   uses colour to say which product it is, and a fourth meaning for colour would collide with that.
   ⚠These rules must beat the rects' presentation attributes (stroke-width="2" and friends are written
   inline), which they do: a CSS declaration always wins over a presentation attribute. That is the same
   ordering the text styling above already relies on. */
.arch-link { cursor: pointer; }
.arch-link rect { transition: stroke-width .12s ease; }
.arch-link:hover rect,
.arch-link:focus-visible rect { stroke-width: 3.4; }
.arch-link:hover .arch-hot,
.arch-link:focus-visible .arch-hot { text-decoration: underline; text-underline-offset: 2px; }
/* ⚠SVG needs an explicit focus ring: the UA default outline is unreliable inside an <svg> and in some
   engines is drawn at zero size, which is a focus state that exists and cannot be seen. */
.arch-link:focus { outline: none; }
.arch-link:focus-visible rect { outline: 2px solid var(--qs-brand); outline-offset: 3px; }
.arch-hint { text-align: center; margin: 10px auto 0; color: var(--qs-muted); font-size: .88rem; }
/* ===== THE LABELS ARE HTML, NOT SVG <text> (2026-09-07) =====
   ⛔⛔AN SVG <text> DOES NOT WRAP, AND THAT ONLY SHOWS UP IN OTHER LANGUAGES. Every label on this
   diagram used to be one unbreakable run pinned at an x/y inside a fixed rect. A translation 40%
   longer than the English does not reflow: it runs out of its box, over its neighbour, or off the
   viewBox, silently, looking perfectly correct to anyone reading English. The site serves fourteen
   languages; German and Dutch compounds are routinely 30-50% longer ("Remote Control" ->
   "Afstandsbediening"), and Japanese, Korean and Chinese have no spaces at all, so nothing that breaks
   on a space would have helped them either.
   ⭐So each label is now a flex column inside a <foreignObject>, and the browser's own line-breaking
   engine does the wrapping - the one engine that already knows how to break a German compound, how to
   break CJK between characters, and which way Arabic runs. No JavaScript and no measurement, which is
   what makes it the only option compatible with this site's static SSR. Full reasoning beside the svg
   in Home.razor. */
.arch-lbl {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  box-sizing: border-box;
  padding: 0 3px;
  line-height: 1.14;
  font-family: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* ⛔A SINGLE UNBREAKABLE WORD IS THE CASE THAT DEFEATS ORDINARY WRAPPING, and German supplies them
     by the dozen. `anywhere` lets the browser break mid-word when there is no other option, which is
     ugly and legible; without it the word overhangs the box, which is neither. `hyphens: auto` is
     tried first and gives a proper hyphenated break where the language has rules for one - it works
     because App.razor sets a real lang= on <html>, not a hardcoded "en". */
  overflow-wrap: anywhere;
  hyphens: auto;
  /* ⛔THE LABEL MUST NOT TAKE THE CLICK. Five of these sit inside an SVG <a>, and whether activating
     HTML inside a foreignObject activates the SVG anchor around it is not something the engines agree
     on. Making the label transparent to the pointer removes the question instead of betting on it, and
     leaves exactly one hit target per block - the rect, which is what a reader is aiming at.
     ⚠Hover and focus styling is unaffected: the pointer is over the <a> either way, so
     .arch-link:hover .arch-hot still matches. */
  pointer-events: none;
}
/* ⚠overflow is VISIBLE, so a label that outgrows its box spills where a human can see it. A
   foreignObject clips by default, and a clipped label is a silent truncation - the reader sees a
   plausible shorter word and nothing reports it. Ugly beats invisible. */
.arch-svg foreignObject { overflow: visible; }
/* Labels that hug a lead rather than sitting in a box: pinned to the edge nearest the line so the
   growth happens away from it. `start`/`flex-start` follow `direction`, so these are correct in Arabic
   without a second rule - which is the whole reason for using them instead of left/right. */
.arch-lbl-near { align-items: flex-start; text-align: start; }
.arch-lbl-top { justify-content: flex-start; }
.arch-lbl-bottom { justify-content: flex-end; }
/* ⭐RIGHT-TO-LEFT. One property, set on the svg, inherited by every label inside it. It is what makes
   the browser lay Arabic out correctly and what makes `start` above mean the right-hand edge.
   ⚠The block LAYOUT is deliberately not mirrored - see the reasoning beside the svg in Home.razor and
   the assertion that keeps it true in ArchDiagramTests. */
.arch-rtl .arch-lbl { direction: rtl; }
.arch-name { font-size: 17px; font-weight: 700; }
/* ⛔A LABEL WITH NO COLOUR INHERITS THE DEFAULT INK, which is invisible on a black page. Every product
   name on this diagram used to carry an explicit fill= for its brand hue; "Your AVR / TV" is not a
   product and had none, so it rendered as near-black on near-black.
   ⛔⛔THE HUES MOVED OUT OF THE MARKUP AND INTO THESE CLASSES when the labels became HTML, and they had
   to. HTML text is coloured by `color` and ignores `fill` completely, so an inline fill= left on a
   label would have been dead markup and every product name would have inherited the body ink - the
   same black-on-black defect above, restored for all five products at once.
   ⚠Each hue therefore needs a value in BOTH themes now: the light value below is the one that used to
   sit in the markup, and theme-dark.css overrides it. Neither may be omitted. */
.arch-t-neutral { color: var(--qs-ink-2); }
.arch-t-qmp { color: #3b4bd8; }
.arch-t-qsm { color: #c2560a; }
.arch-t-qrc { color: #0d8a7d; }
.arch-t-qmt { color: #b46f04; }
.arch-t-qmr { color: #8a35d6; }
.arch-t-sem { color: #be123c; }
/* Content sources and the sub-blocks inside the Player. Smaller than .arch-name on purpose: they are
   not apps you install, and equal weight would read as five products rather than four. */
/* ⛔THE TOP ROW HAS ITS OWN SIZE, AND IT IS A CONSEQUENCE OF THE BOX WIDTH.
   Five content sources share the width three used to have - the diagram is already the widest thing on
   the page and below 640px it scrolls sideways at a 560px minimum, so widening it was not an option.
   164 units per box will not hold "Future Partner Content" at the .arch-small size, so the labels get
   smaller for the boxes to get narrower. Change one and re-measure the other.
   ⚠These sizes now govern how many LINES a translation takes, not just how wide it is. Raising one
   costs vertical room in every box that uses it. */
.arch-src { font-size: 12.2px; font-weight: 700; color: var(--qs-ink-2); }
.arch-src-sub { font-size: 10.3px; font-weight: 600; color: var(--qs-muted); letter-spacing: .05em;
                text-transform: uppercase; }
.arch-small { font-size: 14.5px; font-weight: 700; color: var(--qs-ink-2); }
.arch-sub { font-size: 12.5px; font-weight: 600; color: var(--qs-muted); letter-spacing: .06em; text-transform: uppercase; }
.arch-edge { font-size: 13px; font-weight: 600; color: var(--qs-muted); }

/* The owner's line. Deliberately the last thing under the diagram and given room to breathe - it is
   the emotional close to a technical picture, so it must not read as another caption. */
.arch-note { text-align: center; margin: 14px auto 0; max-width: 72ch; color: var(--qs-muted); font-size: .92rem; line-height: 1.5; }
.arch-note a { color: var(--qs-brand); font-weight: 600; }
.arch-quote { text-align: center; margin-top: 14px; font-size: 1.15rem; font-style: italic; font-weight: 600; color: var(--qs-brand); }

@media (max-width: 640px) {
  /* The diagram is one wide row of boxes; below ~640px the labels would be unreadable if it kept
     scaling down, so let it scroll sideways at a legible size instead of shrinking to nothing. */
  .arch { overflow-x: auto; }
  .arch-svg { min-width: 560px; }
}

/* ===================== App UI: the moving clip =====================
   Sized exactly like a .shot so the video and the stills read as one set rather than a video bolted on. */
.shot-video { margin: 20px 0 0; max-width: 100%; }
.app-video { width: 100%; height: auto; display: block; border: 1px solid var(--qs-line);
             border-radius: 12px; background: #000; }
.shot-video figcaption { margin-top: 8px; font-size: .88rem; color: var(--qs-muted); line-height: 1.45; }

/* ===================== The .qnfo specification page =====================
   ⭐The examples are real XML fixtures and they are the point of the page, so they get a proper code
   block rather than being squeezed into body type. ⚠overflow-x on the <pre>: some lines are long and a
   page that scrolls SIDEWAYS as a whole is the classic mobile defect - the block scrolls, the page
   does not. */
.spec-code { background: #0b0d17; color: #d7dbea; border: 1px solid #1c2138; border-radius: 12px;
             padding: 16px 18px; margin: 14px 0 0; overflow-x: auto; max-width: 100%; }
.spec-code code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
                  font-size: .82rem; line-height: 1.5; white-space: pre; display: block; }
.spec-list { margin: 10px 0 0; padding-left: 20px; color: var(--qs-ink-3); line-height: 1.6; max-width: 78ch; }
.spec-list li { margin-bottom: 8px; }
.spec-list code, .app-section p code, .spec-table code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .88em;
    background: #f3f4f6; border-radius: 5px; padding: 1px 5px; }
.spec-table { width: 100%; border-collapse: collapse; margin-top: 12px; font-size: .93rem; }
.spec-table th, .spec-table td { text-align: left; vertical-align: top; padding: 9px 12px;
                                 border-bottom: 1px solid var(--qs-line); color: var(--qs-ink-3); }
.spec-table thead th { color: var(--qs-ink-strong); font-weight: 700; }

/* ===================== App UI: the clip as a grid thumbnail =====================
   ⭐It reuses .shot-btn wholesale so the clip and the stills are the same size and behave the same way.
   Only the CUE differs - a play triangle rather than a magnifier - because the two do different things
   and a visitor should be able to tell which before clicking. */
.shot-cue-play { background: var(--qs-brand); color: #fff; opacity: .92; }
.shot-btn-video:hover .shot-cue-play, .shot-btn-video:focus-visible .shot-cue-play { opacity: 1; }

/* The clip inside the lightbox. ⭐Sized like .lightbox-img so a video and a screenshot open to the same
   near-fullscreen frame - the owner asked for "near full size", and matching the stills is what makes
   the overlay feel like one viewer rather than two. */
.lightbox-vid { max-width: 100%; max-height: calc(100vh - 130px); width: auto; height: auto;
                border-radius: 10px; background: #000; }
.lightbox-vid[hidden] { display: none; }
.lightbox-img[hidden] { display: none; }

/* ===================== News =====================
   ⭐The owner asked for "small page width sections, click to open to a full view". So a card is a
   NARROW strip - 760px, the same reading measure as About - and opening it expands the same strip
   rather than navigating to a different-looking page. The list stays visible underneath, which is what
   makes it feel like opening a section rather than leaving for an article. */
.news-wrap { max-width: 760px; }

.news-item { border: 1px solid var(--qs-line); border-radius: 12px; margin-bottom: 14px;
             background: #fff; overflow: hidden; transition: border-color .15s, box-shadow .15s; }
.news-item:hover { border-color: #cbd0e0; box-shadow: 0 2px 14px #0b0d1710; }
.news-item.open { border-color: var(--qs-brand); box-shadow: 0 4px 22px #5b6cff1f; }

/* The whole card is the control. ⚠display:block on the anchor so the hit area is the card, not just
   the run of text - a 12px "Read more" is a poor target on a phone. */
.news-card { display: block; padding: 18px 22px; color: inherit; }
.news-card:focus-visible { outline: 2px solid var(--qs-brand); outline-offset: -2px; border-radius: 12px; }

.news-meta { display: flex; align-items: center; gap: 10px; font-size: .82rem; color: var(--qs-muted); }
.news-tag { background: #eef0ff; color: var(--qs-brand); border-radius: 999px;
            padding: 2px 9px; font-weight: 600; font-size: .75rem; }

.news-item h2 { font-size: 1.3rem; font-weight: 800; letter-spacing: -.02em; margin: 6px 0 0; line-height: 1.25; }
.news-card:hover h2 { color: var(--qs-brand); }
.news-summary { color: var(--qs-ink-3); margin: 6px 0 0; }
.news-more { display: inline-block; margin-top: 10px; color: var(--qs-brand); font-weight: 600; font-size: .9rem; }

/* The full view. ⭐Indented under the card's own padding so it reads as the card having grown, and
   ruled off at the top so the summary above it does not look like a duplicated first paragraph. */
.news-full { padding: 0 22px 20px; border-top: 1px solid var(--qs-line); margin: 0 22px; }
.news-full p { color: var(--qs-ink-2); margin: 14px 0 0; line-height: 1.65; }
.news-link { margin-top: 18px !important; }
.news-link a { color: var(--qs-brand); font-weight: 600; }
.news-link a:hover { text-decoration: underline; }

@media (max-width: 640px) {
    .news-card { padding: 15px 16px; }
    .news-full { padding: 0 16px 16px; margin: 0 16px; }
}

/* ⬜The temporary .nav-dev-* dropdown styles were removed here on 2026-09-01 with the markup they
   styled. ⚠A first pass at deleting them by pattern left `.nav-dev-menu:hover .nav-dev-items,` behind -
   a selector ending in a COMMA, which would have swallowed the @media rule that followed it and taken
   the phone navigation with it. CSS fails silently at exactly this kind of edit, which is why the block
   was removed whole rather than by regex. */

/* ── Launch countdown (Pricing) ────────────────────────────────────────────────────────────────
   Colours come from the existing tokens, never from literals: theme-dark.css redefines the tokens
   and is loaded AFTER this file, so anything hard-coded here would stay light-themed in dark mode
   with nothing to explain why. */
.countdown {
    /* Same invented-token defect as .reg-cta, same fix - see the note there. */
    color: var(--qs-ink);
    margin: 22px 0 4px;
    padding: 16px 20px;
    border: 1px solid var(--qs-line, #d9dce6);
    border-radius: 14px;
    background: var(--qs-bg-soft);
    display: inline-block;
    min-width: min(100%, 420px);
}
.cd-label {
    font-size: .82rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--qs-muted, #6b7280);
    margin-bottom: 8px;
}
.cd-clock {
    display: flex;
    align-items: baseline;
    gap: 6px;
    /* Tabular figures, or every tick changes the WIDTH of the row and the whole block jitters once a
       second. It is the difference between a clock and a distraction. */
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}
.cd-unit { display: inline-flex; flex-direction: column; align-items: center; min-width: 46px; }
.cd-unit b {
    font-size: 2rem;
    line-height: 1.05;
    font-weight: 700;
    color: var(--qs-brand, #5b6cff);
}
.cd-unit i {
    font-style: normal;
    font-size: .68rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--qs-muted, #6b7280);
}
.cd-sep {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--qs-muted, #6b7280);
    transform: translateY(-6px);
}
.cd-when { margin-top: 10px; font-size: .84rem; color: var(--qs-muted, #6b7280); }
.cd-local { display: block; opacity: .8; }
/* Past the moment: the digits stay at zero and stop. It does NOT announce a sale - whether anything
   is purchasable then is a commercial fact this stylesheet cannot know. */
.countdown[data-elapsed="true"] .cd-label::after { content: " - launch day"; }

@media (max-width: 520px) {
    .cd-unit b { font-size: 1.5rem; }
    .cd-unit { min-width: 38px; }
}

/* ── Register call to action (home foot + under the launch clock) ──────────────────────────────
   Tokens only, never literals: theme-dark.css redefines the tokens and loads AFTER this file, so a
   hard-coded colour here stays light-themed in dark mode with nothing to explain why. */
.reg-cta {
    /* THE DEFECT THIS FIXES. This used a token that EXISTS IN NEITHER STYLESHEET - invented at the
       point of use. CSS does not complain: it silently takes the fallback, so this block and the
       countdown rendered a near-white panel in BOTH themes, and in dark mode the page ink is light.
       Light text on near-white. Invisible to anyone testing in light mode, where the fallback happens
       to look right - so the owner had to report it.
       --qs-bg-soft is the established panel token and is TRANSLUCENT, so it tints whatever ground it
       sits on instead of imposing one. The ink is STATED rather than inherited: inheriting is how a
       block ends up readable in one theme and not the other. */
    color: var(--qs-ink);
    margin: 26px 0 8px;
    padding: 22px 24px;
    border: 1px solid var(--qs-line, #d9dce6);
    border-radius: 16px;
    background: var(--qs-bg-soft);
}
.reg-cta-inner { max-width: 760px; }
.reg-cta h3 { margin: 0 0 8px; font-size: 1.28rem; }
.reg-cta p { margin: 0 0 10px; }
.reg-cta-note { font-size: .9rem; color: var(--qs-muted, #6b7280); }
.reg-cta-actions {
    display: flex;
    flex-wrap: wrap;      /* two buttons must not force a horizontal scroll on a phone */
    gap: 10px;
    margin-top: 14px;
}

/* ===================== The live Remote demo on the QRC product page =====================
   ⭐The frame around /remote-live/ - the real Remote compiled to WebAssembly. Presentation only:
   the demo's BEHAVIOUR lives in the app and in js/remote-demo.js, and must not start living here too.

   ⛔EVERY COLOUR IS AN EXISTING TOKEN. The reg-cta block above records what happens otherwise: a token
   invented at the point of use does not fail, CSS silently takes the fallback, and the result is a
   near-white panel that looks right in light mode and is unreadable in dark. Tokens used here are
   --qs-line, --qs-ink, --qs-ink-3, --qs-bg-soft, --qs-brand - all defined in one of the two sheets. */
.demo-frame-wrap { margin: 22px 0 0; }

/* THE POSTER. A button, not a div with a click handler - it must be reachable by keyboard and be
   announced as something that does a thing, which is the whole difference for anyone not using a mouse. */
.demo-poster {
    position: relative;
    display: block;
    width: 100%;
    padding: 0;
    border: 1px solid var(--qs-line, #d9dce6);
    border-radius: 16px;
    overflow: hidden;
    background: var(--qs-bg-soft);
    cursor: pointer;
    /* ⚠No hover-only affordance. The Remote is a TABLET app and this section is most likely to be
       driven by a finger, where :hover either never fires or sticks after the tap. The play badge and
       the label are visible at rest; hover only lifts them slightly. */
    transition: border-color .15s ease, transform .15s ease;
}
.demo-poster:hover { border-color: var(--qs-brand); transform: translateY(-2px); }
.demo-poster:focus-visible { outline: 3px solid var(--qs-brand); outline-offset: 3px; }
.demo-poster img { display: block; width: 100%; height: auto; }

/* The label sits ON the shot, over a scrim, so it is legible whatever the artwork underneath happens
   to be - a wall of cover art is exactly the busy background that eats plain overlaid text. */
.demo-poster-cta {
    position: absolute;
    inset: auto 0 0 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 46px 16px 20px;
    color: #fff;
    background: linear-gradient(to top, #000000d9, #00000000);
    text-align: center;
}
.demo-poster-lead { font-size: 1.05rem; font-weight: 800; letter-spacing: -.01em; }
.demo-poster-cta small { font-size: .82rem; opacity: .82; }
.demo-poster-play {
    display: inline-flex; align-items: center; justify-content: center;
    width: 52px; height: 52px; margin-bottom: 4px;
    border-radius: 50%;
    background: var(--qs-brand);
    color: #fff; font-size: 1.1rem; line-height: 1;
    box-shadow: 0 8px 26px #0007;
}

/* THE TABLET. ⚠A FIXED ASPECT RATIO on purpose: the Remote is a tablet app, and letting it reflow to
   whatever the page column happens to be would show a layout no customer will ever see. 16:10 is the
   common tablet landscape. ⭐aspect-ratio rather than a padding-top hack - it is the modern property,
   and every browser that can run a WASM app of this size supports it. */
.demo-tablet {
    width: 100%;
    aspect-ratio: 16 / 10;
    border: 1px solid var(--qs-line, #d9dce6);
    border-radius: 16px;
    overflow: hidden;
    background: #000;
    box-shadow: 0 24px 70px #0009;
}
.demo-tablet iframe { display: block; width: 100%; height: 100%; border: 0; }

/* ⚠A TALL FRAME ON A PHONE IS WORSE THAN A SHORT ONE. At 16:10 inside a 380px column the app is 237px
   tall and nothing in it is usable. Squaring it up gives the interface room without pushing the rest of
   the page off the screen. */
@media (max-width: 640px) {
    .demo-tablet { aspect-ratio: 4 / 3; }
    .demo-poster-cta { padding: 34px 12px 14px; }
    .demo-poster-play { width: 44px; height: 44px; }
}

.demo-frame-note { margin: 10px 0 0; font-size: .9rem; color: var(--qs-ink-3); }
.demo-frame-note strong { color: var(--qs-ink); }

/* The language picker reuses the currency switch's markup and styling deliberately - two controls that
   do the same kind of job should not each be their own small surprise. Only the width differs, because
   an endonym like "繁體中文" is wider than "AUD". */
/* ⭐⭐ANCHORED TO THE RIGHT OF THE NAV ROW. Owner, 2026-09-06: "the language picker needs to be
   moved, its floating too much. anchor to right side please."

   ⛔float: right WAS NEVER DOING ANYTHING HERE, and that is why it looked adrift. The picker reuses
   .ccy-switch, which was written for the pricing page where float is meaningful - but inside .nav-links
   it is a FLEX ITEM, and the spec says float does not apply to flex items. So it sat wherever the flex
   line put it and moved every time .nav-links wrapped.

   ⭐margin-left: auto is the flexbox way: it eats the free space on the left and pins the picker to the
   right edge, after the call to action, on every window width where the bar is one row. */
/* ⛔⛔margin-left: auto BROKE THE WHOLE NAV BAR AND WENT LIVE. MEASURED 2026-09-06 from a screenshot of
   qsync.info: the links stayed on the top row, the QSYNC LOGO dropped to a second row, and the picker
   was stranded on a third. The owner saw it before I did.

   ⚠WHY: .lang-switch is the last flex item inside .nav-links, and margin-left:auto makes it eat all the
   free space on its line. That forced .nav-links to demand the full width of .nav-inner, leaving nothing
   for .nav-brand - which .nav-inner's space-between then wrapped away onto its own row.

   ⭐float: none IS THE WHOLE FIX, and it is all that was ever needed. .nav-inner already right-aligns
   .nav-links via justify-content: space-between, so the picker - sitting last in that group - is
   already at the right edge. The original `float: right` was inert (float does not apply to flex items)
   and removing it changes nothing visually; it is removed because a rule that does nothing invites the
   next person to "fix" it the way I did.

   ⛔DO NOT reintroduce margin-left, width or flex-grow here without a screenshot of the rendered bar at
   1440px AND at the two mobile breakpoints. A nav bar is one of the few things every visitor sees. */
.lang-switch { float: none; }
.lang-switch > summary { min-width: 108px; }
.lang-switch .ccy-menu { min-width: 150px; }
.lang-switch .ccy-menu a { display: block; padding: 6px 12px; }

/* ⭐⭐THE SUPPORTED-LANGUAGES NOTE. Owner, 2026-09-07: 42 languages, each in its own language.
   ⛔A 42-item list is a WALL unless it is set as one. So: inline flex items with a middot separator
     rather than bullets, small and muted, so it reads as a fact about reach and not as navigation -
     which is what the language picker is for, and confusing the two would be worse than omitting it.
   ⚠NO ::after ON THE LAST ITEM. A trailing separator on a 42-item list is the kind of detail nobody
     notices in review and everybody notices on the page. */
.hero-langs { margin: 18px 0 6px; font-size: .9rem; color: var(--qs-ink-2); }
.lang-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; gap: 0;
    font-size: .82rem; color: var(--qs-ink-2); line-height: 1.9;
    max-width: 760px;
}
.lang-list li { white-space: nowrap; }
.lang-list li::after { content: " \00B7 "; opacity: .45; padding: 0 .35em; }
.lang-list li:last-child::after { content: ""; padding: 0; }
/* ⚠A right-to-left NAME inside a left-to-right list needs isolation, or the middot after it renders
   on the wrong side. `unicode-bidi: isolate` is the mechanism for a name; BidiIsolation.cs does the
   equivalent job for whole translated sentences. */
.lang-list li[dir="rtl"] { unicode-bidi: isolate; }

/* ===================== beta feedback (/beta) =====================
   ⚠Only the classes this page actually needs. The page otherwise uses the site's own hero +
   container shell, so it inherits every layout change the rest of the site gets. */
.beta-msg{border-left:3px solid var(--qs-line);padding:2px 0 2px 14px;margin:16px 0}
.beta-msg .who{color:var(--qs-muted);font-size:13px;margin-bottom:2px}
/* ⛔pre-wrap, not a <pre>: a tester's report carries real line breaks and must keep them, but it
   must also WRAP - a pasted log line in a <pre> scrolls the whole page sideways on a phone. */
.beta-msg .body{white-space:pre-wrap;overflow-wrap:anywhere}
.beta-list{list-style:none;margin:16px 0;padding:0}
.beta-row{display:block;width:100%;text-align:left;background:var(--qs-card,#fff);
  border:1px solid var(--qs-line);border-radius:10px;padding:13px 15px;margin:0 0 8px;cursor:pointer;
  font:inherit;color:inherit}
.beta-row:hover{border-color:var(--qs-brand)}
.beta-row .s{display:block;font-weight:600}
.beta-row .m{display:block;color:var(--qs-muted);font-size:13px;margin-top:3px}
.hint{color:var(--qs-muted);font-size:13px;margin:4px 0 0}
.err{color:#b9453c;background:#b9453c14;border:1px solid #b9453c44;border-radius:8px;
  padding:10px 13px;margin:12px 0}

/* ⭐The beta card on /account. Deliberately the only tinted panel on that page: for a tester
   with no licences it is the single thing on the screen that is FOR them. */
.beta-card {
    border: 1px solid var(--accent, #1f8fff);
    background: color-mix(in srgb, var(--accent, #1f8fff) 8%, transparent);
    border-radius: 10px;
    padding: 16px 18px;
    margin: 0 0 22px;
}

/* ⭐The shared beta thread. Capped and scrollable so a long-running conversation does not
   bury the box people type into - the compose form must stay reachable without a long scroll. */
.beta-wall {
    max-height: 60vh;
    overflow-y: auto;
    overscroll-behavior: contain;
    border: 1px solid #8884;
    border-radius: 10px;
    padding: 12px 14px;
    margin: 0 0 20px;
}
.beta-wall .beta-msg + .beta-msg { border-top: 1px solid #8883; margin-top: 12px; padding-top: 12px; }
/* ⚠The body is posted text: preserve the author's line breaks, and break long unbroken
   strings so one pasted path cannot force the whole page to scroll sideways on a phone. */
.beta-wall .body { white-space: pre-wrap; overflow-wrap: anywhere; }

/* ⛔⛔iOS SAFARI ZOOMS THE PAGE ON FOCUS for any field under 16px, and does not zoom back.
   The forum and beta pages use bare <input>/<textarea> with no Bootstrap form class, so they
   inherit the browser default - about 13px in Safari - and every tap jumped the layout.
   16px EXACTLY is the threshold: 15.9px zooms.

   ⭐⭐BUT IT IS A TOUCH-DEVICE HAZARD FIX, NOT A TYPE CHOICE, and it was being paid on every screen.
   16px input text beside 14px labels and 14px buttons is the largest thing in the form, which is what
   the owner meant by "fonts in UI controls are too large".
   ⚠`pointer: coarse` is the precise question - it is true for a finger and false for a mouse - so the
   hazard is still covered on the devices that have it. ⛔An iPad reports coarse at 1024px wide, so
   this must NOT be a width breakpoint: that would reintroduce the zoom on exactly the device the rule
   was written for. */
/* ⛔⛔`:not([class*="dx"])` IS THE COMPONENT BOUNDARY, and it is load-bearing.
   MEASURED 2026-09-27: without it these rules reach INSIDE the DevExpress editor and restyle its
   internal inputs - the paragraph-style combobox came out at 16px against a 12.2px toolbar, with our
   900px max-width, our padding and our border on it. ⚠I spent the morning checking whether their
   theme bled into our pages; it did not. Ours bled into theirs.
   ⭐Our own fields carry no class at all, so excluding classed DevExpress elements costs us nothing. */
.beta-msg ~ textarea,
.container input[type=text]:not([class*="dx"]),
.container input:not([type]):not([class*="dx"]),
.container textarea:not([class*="dx"]) {
    font-size: 16px;
}

/* ⛔⛔THE FORUM'S FIELDS WERE BROWSER-DEFAULT SIZED, WHICH IS ABOUT 20 COLUMNS.
   The owner, on a 1920px screen: "The data entry box will hold about 2 setences" and
   "its not a iphone app. scale to the users screen!"
   ⚠The only rule that had ever touched these was the 16px above, added to stop iOS Safari zooming
   on focus - so the page was tuned for a phone hazard and never once sized for a desktop. A
   rows="7" textarea with no width is a postage stamp on a monitor.
   ⭐They now fill the column they are in. max-width keeps a 1920px screen from producing a line so
   long it cannot be read, which is the opposite failure and just as unpleasant to type into. */
.container input[type=text]:not([class*="dx"]),
.container input[type=email]:not([class*="dx"]),
.container input:not([type]):not([class*="dx"]),
.container textarea:not([class*="dx"]) {
    display: block;
    width: 100%;
    max-width: 900px;
    box-sizing: border-box;
    padding: 10px 12px;
    margin: 4px 0 2px;
    border: 1px solid var(--qs-line);
    border-radius: 8px;
    background: var(--qs-card, #fff);
    color: inherit;
    font-family: inherit;
}

.container textarea:not([class*="dx"]) {
    /* ⚠A support report carries a pasted log. Whatever `rows` says, never less than this. */
    min-height: 190px;
    line-height: 1.5;
    resize: vertical;
}

.container input:not([class*="dx"]):focus,
.container textarea:not([class*="dx"]):focus {
    outline: none;
    border-color: var(--qs-brand);
}

/* ⛔⛔THIS BLOCK MUST STAY BELOW THE 16px RULE IT OVERRIDES, and it spent one deploy above it.
   Both selectors are identical, so specificity cannot separate them and the later one simply wins.
   ⚠MEASURED: the field stayed at 16px while the media query was TRUE - a rule that is live and inert
   at the same time, which nothing in a stylesheet tells you.
   ⭐And the test is "not coarse" rather than "fine": the zoom-on-focus hazard belongs to a COARSE
   pointer, and a desktop reporting `pointer: none` - a kiosk, a remote session - does not zoom
   either. */
@media not all and (pointer: coarse) {
    .container input[type=text]:not([class*="dx"]),
    .container input[type=email]:not([class*="dx"]),
    .container input:not([type]):not([class*="dx"]),
    .container textarea:not([class*="dx"]) {
        font-size: 14.5px;
    }
}

/* ⭐A label needs to look like one, or the form reads as a wall of boxes. */
.container > label, .container label {
    display: block;
    margin-top: 16px;
    font-weight: 600;
    font-size: 14px;
}


/* =============================================================================================
   ⭐⭐THE SUPPORT FORUM: THREAD LIST, POSTS, AND POSTED MARKUP.

   ⛔⛔WHAT THE OWNER SAW ON 2026-09-26 AND WHY EVERY RULE HERE EXISTS:
     "This looks like a cray discords forum (useless, everything on one huge page)"
     "its not a iphone app. scale to the users screen!"
   The page had NO styles of its own at all - it was <p> and <ul> at default sizes inside a 1100px
   column, so a thread list and a thread read identically and neither used the screen.

   ⚠WIDER THAN THE REST OF THE SITE, ON PURPOSE. --qs-content-max is 1100px, which is a READING
   measure for marketing copy. A support thread carries pasted logs and screen grabs of a 1920px
   application window, and wrapping those into 1100px is the complaint above. The forum takes 1360px
   and the prose inside a post is capped separately, so the text still reads at a sane measure while
   an image or a log can use the room.
   ============================================================================================= */
.forum-wide { max-width: calc(1360px + 2 * var(--qs-gutter)); }

/* ── the thread list ────────────────────────────────────────────────────────────────────────── */
/* ⭐A LIST OF ROWS, NOT A WALL. Each row is one button so the whole strip is clickable - a link on
   the subject alone is a 200px target in a 1360px row, and everybody clicks the row. */
.fm-threads { list-style: none; padding: 0; margin: 18px 0 0; border-top: 1px solid var(--qs-line); }
.fm-threads > li { border-bottom: 1px solid var(--qs-line); }
.fm-thread {
    display: flex; align-items: baseline; gap: 18px; width: 100%;
    padding: 15px 8px; background: none; border: 0; cursor: pointer;
    text-align: left; font: inherit; color: inherit;
}
.fm-thread:hover { background: var(--qs-wash, #5b6cff0d); }
.fm-subject { font-weight: 600; font-size: 1.05rem; flex: 1 1 auto; }
.fm-meta { color: var(--qs-muted); font-size: 13px; flex: 0 0 auto; white-space: nowrap; }
/* ⚠The count is on the right where the eye already is after the date, and stays legible when a
   thread has none - "no replies" is information, not an empty cell. */
.fm-count { color: var(--qs-muted); font-size: 13px; flex: 0 0 7.5em; text-align: right; }
.fm-ver {
    font-size: 11px; font-weight: 600; letter-spacing: .02em;
    color: var(--qs-brand); background: var(--qs-wash-2, #5b6cff14); border-radius: 999px;
    padding: 3px 9px; flex: 0 0 auto; white-space: nowrap;
}

/* ── a post ─────────────────────────────────────────────────────────────────────────────────── */
/* ⛔⛔TOKENS, NEVER LITERALS. This card said `background: #fff` for an hour, and on the live site -
   which ships theme-dark.css as an override layer - that is a white card carrying var(--qs-ink) text,
   which the dark theme defines as #e9ebf0. Near-white on near-white: every post body invisible.
   ⚠Nothing in the test suite can see a colour. It was found by photographing the page. */
.fm-post {
    border: 1px solid var(--qs-line); border-radius: 12px;
    padding: 16px 20px; margin: 14px 0; background: var(--qs-card, #fff);
}
/* ⛔⛔AN ANSWER FROM QSYNC MUST NOT LOOK LIKE AN ANSWER FROM ANOTHER CUSTOMER. On a support forum
   "is this official?" is the first question a reader has, and the badge alone is easy to miss in a
   long thread - so the whole card is tinted too. ⚠Driven by authorKind from the SERVICE, which
   derives it from the stored role; a display name can say anything. */
.fm-post-staff { border-color: var(--qs-brand); background: var(--qs-wash, #5b6cff08); }
.fm-post-first { border-width: 2px; }
.fm-who {
    display: flex; align-items: center; gap: 10px;
    font-weight: 600; font-size: 14px; margin-bottom: 10px;
    padding-bottom: 10px; border-bottom: 1px solid var(--qs-line);
}
.fm-when { color: var(--qs-muted); font-weight: 400; font-size: 13px; margin-left: auto; }
.fm-edited { color: var(--qs-muted); font-weight: 400; font-size: 12px; font-style: italic; }
.fm-badge {
    background: var(--qs-brand); color: #fff; font-size: 11px; font-weight: 700;
    letter-spacing: .04em; padding: 2px 8px; border-radius: 999px;
}
.fm-mine-tools { margin-left: 10px; }

/* ── the rendered body of a post ────────────────────────────────────────────────────────────── */
/* ⛔⛔TWO DIFFERENT MEASURES, AND THEY WERE ONE RULE. 72ch is the PROSE measure - running body text
   the full width of a 1360px card is unreadable, which is a different failure from the one being
   fixed rather than a smaller one.
   ⚠But `.fm-body { max-width: 72ch }` capped EVERYTHING inside it, so a 2400x1350 screen grab of the
   Player came out smaller than a thumbnail. MEASURED in the first picture of a How-To that rendered.
   ⭐So the cap goes on the TEXT elements and the figure gets the card. */
.fm-body { max-width: none; }
.fm-body p,
.fm-body ul,
.fm-body ol,
.fm-body blockquote,
.fm-body h2,
.fm-body h3,
.fm-body h4 { max-width: 72ch; }
.fm-body > :first-child { margin-top: 0; }
.fm-body > :last-child { margin-bottom: 0; }
.fm-body p { margin: 0 0 12px; line-height: 1.6; }
.fm-h { margin: 22px 0 8px; line-height: 1.25; }
.fm-body h2.fm-h { font-size: 1.3rem; }
.fm-body h3.fm-h { font-size: 1.12rem; }
.fm-body h4.fm-h { font-size: 1rem; }
.fm-list { margin: 0 0 12px; padding-left: 26px; }
.fm-list li { margin: 4px 0; line-height: 1.6; }
.fm-quote {
    margin: 0 0 12px; padding: 2px 0 2px 14px;
    border-left: 3px solid var(--qs-brand); color: var(--qs-muted);
}

/* ⛔A PASTED LOG IS THE POINT OF A SUPPORT FORUM, and a log line is 200 characters. It scrolls
   INSIDE its own block: `overflow-wrap` on a stack trace makes an unreadable ragged mess, and no
   scroll at all makes the whole PAGE scroll sideways, which is the worse of the two.
   ⚠A code block is dark in BOTH themes - that is what a terminal looks like - but it must not be the
   SAME dark as the page, or it disappears into it. --qs-card-2 is the dark theme's raised surface and
   falls back to the site's own near-black on the light theme. */
.fm-code {
    background: var(--qs-card-2, #0b0d17); color: #e6e8ef; border-radius: 10px;
    padding: 13px 15px; margin: 0 0 12px; overflow-x: auto;
    font-size: 13px; line-height: 1.5; max-width: 100%;
}
.fm-code code { background: none; color: inherit; padding: 0; white-space: pre; }
.fm-inline {
    background: var(--qs-wash-2, #5b6cff14); border-radius: 5px; padding: 1px 5px;
    font-size: .92em; overflow-wrap: anywhere;
}

/* ⭐⭐AN IMAGE IN A POST IS A SCREEN GRAB OF A 1920px WINDOW. It gets the card, not the prose
   measure - which is exactly what "how can I show images inline in a HOW TO" needs.
   ⚠1100px rather than the whole 1360: a screen grab is a figure inside a post, and one that reaches
   both edges of the card stops reading as part of the post and starts reading as the page.
   ⚠A border, because a screenshot of a dark application on a dark page has no edges otherwise. */
.fm-img {
    display: block; max-width: min(100%, 1100px); height: auto;
    margin: 10px 0 16px; border: 1px solid var(--qs-line); border-radius: 10px;
}
/* ⚠An attachment that has gone says so. A broken-image icon sends somebody to check their own
   connection, which is a wrong diagnosis we caused. */
.fm-missing { color: var(--qs-muted); font-style: italic; font-size: 14px; }

/* ── the editor ─────────────────────────────────────────────────────────────────────────────── */
.fm-editor { max-width: 900px; }

/* ⭐THE EDITOR SHOULD LOOK LIKE THE SITE IT IS IN. DevExpress ships its own font stack, which put the
   toolbar in Segoe UI beside a page set in Inter - a small thing that reads as two applications
   stitched together. ⚠Only the FAMILY is overridden; the component keeps its own type scale, which is
   tuned to its own controls and is not ours to second-guess. */
.fm-dx, .fm-dx * { font-family: inherit; }

/* ── button scale ───────────────────────────────────────────────────────────────────────────── */
/* ⛔⛔THE FORUM IS AN APPLICATION SCREEN, NOT A SALES PAGE, and it was using the sales page's buttons.
   MEASURED on the live forum: .btn-primary came out 51px tall with 13px 22px padding and an 11px
   radius, beside the editor's own toolbar buttons at 28px with 4.4px padding. The font was 14px on
   both sides of that gap - it was never the type.

   ⭐SCOPED TO .forum-wide RATHER THAN CHANGED GLOBALLY. The same classes are RIGHT on /pricing, where
   "Register now" is 49px and is a call to action on a page whose job is to be clicked. ⚠Retuning
   .btn-primary site-wide to suit one screen would arrive invisibly on every sales page - the shape of
   the find/replace that once rewrote six product pages by accident.

   ⚠It must also stay BELOW the .btn-primary/.btn-ghost block it overrides. It wins on specificity
   (two classes to one), but this file has already produced one rule today that was live and inert at
   the same time, so order and specificity both. */
.forum-wide .btn-primary,
.forum-wide .btn-ghost {
    padding: 9px 18px;
    border-radius: 9px;
    font-size: 14px;
}

/* ⭐A row action - Edit on your own post - is not a page action and must not look like one. These
   carried the same declarations as an inline `style` attribute until now, which no stylesheet test can
   see and no theme can reach. */
.forum-wide .fm-mini {
    padding: 4px 12px;
    border-radius: 7px;
    font-size: 13px;
    font-weight: 500;
}
/* ⭐THE FORMATTING KEY, ABOVE THE BOX. Owner: "I want to create HTML based posts. Bold, italics,
   where is that?" - a toolbar is a bigger build; telling somebody the four marks they need is the
   same answer today, and it stays true in the plain-text an email or an agent sees. */
.fm-hint { color: var(--qs-muted); font-size: 13px; margin: 6px 0 2px; }
.fm-hint code { background: var(--qs-wash-2, #5b6cff14); border-radius: 4px; padding: 1px 5px;
                font-size: 12px; }
.fm-attach {
    display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
    margin: 10px 0 4px; padding: 11px 13px;
    border: 1px dashed var(--qs-line); border-radius: 10px; background: var(--qs-wash, #5b6cff08);
}
.fm-attach input[type=file] { font-size: 14px; }
/* ⚠The tier tokens are already "the darker end of each hue on white, the lighter end on black",
   which is exactly what a success line needs, so it reuses one rather than pinning a second green. */
.fm-ok { color: var(--qs-tier-basic); font-size: 14px; margin: 8px 0 0; }

/* ⭐A LIVE PREVIEW. Formatting you cannot see is formatting you get wrong, and the first thing
   anybody writes here is a How-To that has to look right. */
.fm-preview { margin: 14px 0 0; border: 1px solid var(--qs-line); border-radius: 10px; padding: 0 14px; }
.fm-preview > summary {
    cursor: pointer; font-weight: 600; font-size: 13px; color: var(--qs-muted);
    padding: 10px 0; list-style: none;
}
.fm-preview > summary::before { content: "\25be  "; }
.fm-preview .fm-body { padding-bottom: 12px; }

/* ── files ──────────────────────────────────────────────────────────────────────────────────── */
/* ── reactions ────────────────────────────────────────────────────────── */
/* ⭐⭐AN ACKNOWLEDGEMENT, NOT A SCORE. Owner, 2026-09-27: "i use the to acknowlwde i read a post."
   ⭐Quiet until used. These sit under every post and most will never be clicked; at full strength they
   would compete with the post itself. ⚠The one YOU chose is the only one allowed to look chosen. */
.fm-reacts { display: flex; gap: 6px; margin: 14px 0 0; flex-wrap: wrap; }
.fm-react {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 3px 9px; border-radius: 999px;
    border: 1px solid var(--qs-line); background: none; cursor: pointer;
    font: inherit; font-size: 13px; line-height: 1.6; color: var(--qs-muted);
    opacity: .55;
}
.fm-react:hover { opacity: 1; border-color: var(--qs-brand); }
/* ⭐The one you chose - brand-tinted rather than merely darker, so it reads as pressed at a glance.
   Telling somebody at a glance that a post has been read is the whole job of this control. */
.fm-react-mine {
    opacity: 1; color: var(--qs-brand);
    border-color: var(--qs-brand); background: var(--qs-wash-2, #5b6cff1f);
}
.fm-react-glyph { font-size: 14px; line-height: 1; }
.fm-react-n { font-weight: 600; font-variant-numeric: tabular-nums; }
/* ⚠A reaction that HAS a count is never faded, even when it is not yours - otherwise the page hides
   the thing it exists to show. */
.fm-react:has(.fm-react-n) { opacity: 1; color: var(--qs-ink); }

/* ⚠Only attachments NOT already shown in the prose reach this list. An image that appears in a
   guide and again underneath it reads as a mistake. */
.fm-files { list-style: none; padding: 0; margin: 6px 0 0; }
.fm-files li { padding: 8px 0; border-bottom: 1px solid var(--qs-line); font-size: 14px; }

/* ── two fields side by side ────────────────────────────────────────────────────────────────── */
.fm-two { display: flex; gap: 22px; flex-wrap: wrap; }
.fm-two > div { flex: 1 1 260px; }

/* ⚠A field inside the editor or a flex column must fill ITS column, not the 900px the page-level
   rule gives it - otherwise the two-up row above collapses to one wide box and one stub. */
.fm-two input, .fm-editor textarea { max-width: none; }

@media (max-width: 760px) {
    .fm-thread { flex-wrap: wrap; gap: 4px 12px; }
    .fm-meta, .fm-count { flex: 0 0 auto; text-align: left; }
    .fm-post { padding: 14px; border-radius: 10px; }
    .fm-body p, .fm-body ul, .fm-body ol, .fm-body blockquote,
    .fm-body h2, .fm-body h3, .fm-body h4 { max-width: none; }
}
