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. */
:root {
    --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; }
.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; }
.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); }

/* ⛔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; }
