/**
 * Quorum · layout: the page frame and the chrome (design spec §4.2, §5).
 *
 *   1. Container, shell, skip link
 *   2. Rows and columns (12-column grid, dead tracks, bands, the hero board frame)
 *   3. Notice strip (Studio top bar + the quorum:strip module)
 *   4. Header: the ruled strip of cells (>= 75rem), the compact ruled header (56.25-75rem),
 *      the mobile bar (< 56.25rem)
 *   5. Header actions: search cell and band, Sign in / Join, bell, account menu
 *   6. Directory drawer (< 56.25rem, the lib's #ts-nav)
 *   7. Mega menu skin (single-class selectors; megamenu.css prints first)
 *   8. Footer: lettered directory, bottom bar, Display tools
 *   9. App mode (body.ts-app) and z-index / sticky rules
 *  10. Documents without the framework: error.php and offline.php (design spec §9)
 *
 * Breakpoints: 40rem sm · 48rem md · 56.25rem nav (drawer below, the library's megamenu.js is
 * fixed at 900px) · 64rem lg (split bands side by side) · 75rem xl (full ruled header).
 * Island safety (contract §2.4): only template classes, no element rules, nothing on the shell
 * that creates a containing block for fixed-position app dialogs.
 *
 * @copyright   Copyright (C) 2026 BulaSikku Technologies Private Limited. All rights reserved.
 * @license     GNU General Public License version 2 or later
 */

/* ==========================================================================
   1. Container, shell, skip link
   ========================================================================== */

.ts-container {
    inline-size: 100%;
    max-inline-size: calc(var(--ec-cw) + 2 * var(--page-gutter));
    margin-inline: auto;
    padding-inline: var(--page-gutter);
}

.ts-shell {
    padding-block: clamp(1.5rem, 3.34vw + 0.2rem, 3rem) 0;
}

.ts-main {
    min-inline-size: 0;
}

.ts-skip-link {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: var(--page-gutter);
    z-index: calc(var(--ts-z-toast) + 1);
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    padding: 0 var(--q-s5);
    background-color: var(--q-inverse-bg);
    color: var(--q-inverse-fg);
    font-weight: 600;
    text-decoration: none;
    transform: translateY(-120%);
    transition: transform var(--ec-dur-1) var(--ec-ease);
}

.ts-skip-link:focus-visible {
    transform: translateY(0);
}

/* ==========================================================================
   2. Rows and columns
   ========================================================================== */

.ts-row {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--ec-grid-gap);
    row-gap: var(--q-s8);
    align-items: start;
    margin-block-end: var(--ec-sect-y);
}

.ts-col {
    grid-column: 1 / -1;
    min-inline-size: 0;
}

/* Below lg every column takes the full row; split bands sit side by side from 64rem
   (design spec §4.2: "below 1024px they stack"). Four-up rows pair up from 48rem. */
@media (width < 48rem) {
    .ts-row {
        grid-template-columns: minmax(0, 1fr);
    }
}

@media (min-width: 48rem) {
    .ts-row > .ts-span-3 {
        grid-column: span 6;
    }
}

@media (min-width: 64rem) {
    .ts-span-1 { grid-column: span 1; }
    .ts-span-2 { grid-column: span 2; }
    .ts-row > .ts-span-3 { grid-column: span 3; }
    .ts-span-4 { grid-column: span 4; }
    .ts-span-5 { grid-column: span 5; }
    .ts-span-6 { grid-column: span 6; }
    .ts-span-7 { grid-column: span 7; }
    .ts-span-8 { grid-column: span 8; }
    .ts-span-9 { grid-column: span 9; }
    .ts-span-10 { grid-column: span 10; }
    .ts-span-11 { grid-column: span 11; }
    .ts-span-12 { grid-column: span 12; }
}

/* Dead tracks: a column whose modules printed nothing, a lone column, an empty row
   (contract §2.11; LayoutRenderer prints no whitespace inside .ts-col). */
.ts-row > .ts-col:empty {
    display: none;
}

.ts-row > .ts-col:only-child,
.ts-row:not(:has(> .ts-col:not(:empty) ~ .ts-col:not(:empty))) > .ts-col:not(:empty) {
    grid-column: 1 / -1;
}

.ts-row:not(:has(> .ts-col:not(:empty))) {
    display: none;
}

/* Row rhythm: sections carry their own 2px rule; rows only space them (80 desktop, 48 phone).
   The welcome strip and breadcrumbs sit close to what follows. */
.ts-row[data-ts-row="welcome"] {
    margin-block-end: var(--q-s6);
}

.ts-row[data-ts-row="breadcrumbs"] {
    margin-block-end: var(--q-s4);
}

/* A trail of one item prints nothing (kit mod_breadcrumbs): no empty band above the page. */
.ts-row[data-ts-row="breadcrumbs"]:not(:has(.ts-breadcrumbs)) {
    display: none;
}

/* The hero board (design spec §4.3, §7.2 rows 2-4): hero-left and hero-right are cells of one
   gap-ruled board under a 2px ink rule; the stats row joins it as the board's last line. */
.ts-row[data-ts-row="hero"] {
    gap: var(--q-hair-w);
    padding: 0 var(--q-hair-w) var(--q-hair-w);
    border-block-start: var(--q-rule-w) solid var(--q-rule);
    background-color: var(--ec-border);
}

.ts-row[data-ts-row="hero"] > .ts-col {
    align-self: stretch;
    background-color: var(--ec-surface);
}

.ts-row[data-ts-row="hero"]:has(+ .ts-row[data-ts-row="stats"]) {
    margin-block-end: 0;
}

.ts-row[data-ts-row="hero"] + .ts-row[data-ts-row="stats"] {
    padding: 0 var(--q-hair-w) var(--q-hair-w);
    background-color: var(--ec-border);
}

/* Full-bleed band (contract §2.4.7): the newsletter row carries the footer's surface-2 ground
   out to the viewport edges and the footer continues it without a seam. */
.ts-row[data-ts-row="newsletter"] {
    margin-block-end: 0;
    padding-block: var(--q-s11) 0;
    background-color: var(--ec-surface-2);
    box-shadow: 0 0 0 100vmax var(--ec-surface-2);
    clip-path: inset(0 -100vmax);
}

.ts-shell:has(> .ts-row[data-ts-row="newsletter"]:last-child, > .ts-main--rows > .ts-row[data-ts-row="newsletter"]:last-child) + .ts-footer {
    padding-block-start: var(--q-s8);
}

/* The shell ends with a section's bottom padding; nothing else adds space before the footer. */
:is(.ts-shell, .ts-main--rows) > .ts-row:last-child:not([data-ts-row="newsletter"]) {
    margin-block-end: var(--ec-sect-y);
}

/* ==========================================================================
   3. Notice strip (Studio top bar)
   Anatomy: [date stamp] | [announcement ........] [link]. The date and link come from the
   quorum:strip module inside the end side; display: contents lets them join the strip's
   flex line in design order.
   ========================================================================== */

.ts-topbar {
    background-color: var(--q-signal);
    color: var(--q-signal-fg);
    font-size: 1rem;
    line-height: 1;
}

.ts-topbar__inner {
    display: flex;
    align-items: center;
    gap: var(--q-s4);
    min-block-size: var(--q-strip-h);
    padding-block: 0; /* the 40px strip (§5.1): the strip links are the line's full height */
}

.ts-topbar__side--start:empty,
.ts-topbar__inner > span:empty {
    display: none;
}

.ts-topbar__side--end,
.ts-topbar__modules,
.ts-topbar__modules > .q-strip {
    display: contents;
}

.ts-topbar__text,
.q-strip__notice {
    order: 1;
    flex: 1 1 auto;
    min-inline-size: 0;
    margin: 0;
    font-weight: 500;
    overflow-wrap: break-word;
}

/* An event notice (quorum:strip) takes the announcement's place. */
.ts-topbar__inner:has(.q-strip__notice) > .ts-topbar__text {
    display: none;
}

.q-strip__date {
    order: -2;
    flex: none;
    font-family: var(--q-font-mono);
    font-size: 0.875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    white-space: nowrap;
}

.q-strip__rule {
    order: -1;
    flex: none;
    inline-size: var(--q-hair-w);
    block-size: 1.125rem;
    background-color: currentColor;
}

.q-strip__date:empty,
.q-strip__date:empty + .q-strip__rule {
    display: none;
}

.q-strip__links {
    order: 2;
    flex: none;
    margin-inline-start: auto;
}

.q-strip__links > :where(p, ul) {
    display: flex;
    gap: var(--q-s4);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ts-topbar a {
    position: relative;
    display: inline-flex;
    align-items: center;
    min-block-size: var(--q-strip-h);
    color: inherit;
    font-weight: 600;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 4px;
    white-space: nowrap;
}

/* The strip is 40px; the link's hit area reaches 44px (contract §2.12) without growing it. */
.ts-topbar a::after {
    content: "";
    position: absolute;
    inset: -0.125rem 0;
    min-block-size: 2.75rem;
}

.ts-topbar .ts-social-links,
.ts-topbar .ts-lang-switch {
    order: 3;
    display: flex;
    gap: var(--q-s2);
}

.ts-topbar :focus-visible {
    outline-color: var(--q-focus-ring-on-signal);
    box-shadow: none;
}

@media (hover: hover) and (pointer: fine) {
    .ts-topbar a:hover {
        text-decoration-thickness: 3px;
    }
}

/* Phones (design spec §5.1 decision): the message wraps to two lines next to the link; the date
   stamp stays on wider screens. */
@media (width < 48rem) {
    .ts-topbar__inner {
        min-block-size: 2.75rem;
        padding-block: 0.625rem;
    }

    .ts-topbar__text,
    .q-strip__notice {
        line-height: 1.35;
    }

    .q-strip__date,
    .q-strip__rule {
        display: none;
    }
}

/* ==========================================================================
   4. Header: a ruled strip of fixed cells under a 2px ink rule (design spec §5.2, §5.3)
   ========================================================================== */

.ts-header {
    position: relative;
    z-index: var(--ts-z-header);
    border-block-end: var(--q-rule-w) solid var(--q-rule);
    background-color: var(--ec-surface);
    color: var(--ec-fg);
}

.ts-header.is-sticky {
    position: sticky;
    inset-block-start: 0;
}

.ts-header__inner {
    display: flex;
    align-items: stretch;
    min-block-size: calc(var(--q-header-h) - var(--q-rule-w));
}

/* Brand cell: wordmark (or logo) with a 4px current bar kept transparent off the home page, so
   nothing shifts when it lights up. */
.ts-header .ts-brand {
    display: flex;
    flex: 0 0 auto;
    align-items: center;
    gap: var(--q-s3);
    min-inline-size: 0;
    padding: var(--q-s1) var(--q-s6) 0 0;
    border-inline-end: var(--q-hair-w) solid var(--ec-border);
    box-shadow: inset 0 calc(-1 * var(--q-current-w)) 0 transparent;
    color: var(--ec-fg);
    text-decoration: none;
    transition: background-color var(--ec-dur-1) var(--ec-ease);
}

.ts-home .ts-header .ts-brand {
    box-shadow: inset 0 calc(-1 * var(--q-current-w)) 0 var(--ec-accent);
}

.ts-brand__text {
    font-family: var(--ec-font-display);
    font-size: 2.125rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    line-height: 1;
    white-space: nowrap;
}

.ts-brand__logo {
    display: block;
    inline-size: auto;
    max-inline-size: 13rem;
    block-size: 2.5rem;
    object-fit: contain;
}

/* Lockup (brand.lockup, the sample data's chainring mark): the mark beside the wordmark, 36px in
   the header, 30px in the compact header, 44px in the footer (design spec §5.2, §5.4, §5.5). */
.ts-brand--lockup .ts-brand__logo {
    flex: none;
    inline-size: 2.25rem;
    block-size: 2.25rem;
}

.ts-footer .ts-brand--lockup {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
}

.ts-footer .ts-brand--lockup .ts-brand__logo {
    inline-size: 2.75rem;
    block-size: 2.75rem;
}

.ts-brand__logo--dark {
    display: none;
}

[data-color-scheme="dark"] .ts-brand--responsive-logo .ts-brand__logo--light {
    display: none;
}

[data-color-scheme="dark"] .ts-brand--responsive-logo .ts-brand__logo--dark {
    display: block;
}

/* The brand cell is as wide as the name needs, never less than the designed 232px column once
   the full header has the room (80rem); the name is never cut. The nav cells give way instead. */
@media (min-width: 80rem) {
    .ts-header .ts-brand {
        min-inline-size: 14.5rem;
    }
}

@media (hover: hover) and (pointer: fine) {
    .ts-header .ts-brand:hover {
        background-color: var(--q-hover);
    }
}

.ts-header .ts-brand:focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

/* Navigation cells (header-nav + menu positions; core mod_menu default layout). The nav takes
   the room between the brand and the actions; each cell is 104px wide where the row has the room
   and gives way down to its label plus 12px a side where it has not (a longer label keeps its own
   width). If even that does not fit (a long menu, a long site name), the cells wrap into a second
   line of the strip: a cell never slides under the actions (design spec §5.2). */
.ts-header__nav {
    display: flex;
    flex: 1 1 auto;
    align-items: stretch;
    min-inline-size: 0;
    margin-inline-end: calc(-1 * var(--q-hair-w)); /* a full row's last hairline is the search cell's */
}

.ts-nav-close,
.ts-header__offcanvas {
    display: none;
}

/* Explicit gap and list resets: some app stylesheets style the core `.nav` class globally. */
.ts-header__nav .mod-menu {
    display: flex;
    flex: 1 1 auto;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

.ts-header__nav .mod-menu > li {
    position: relative;
    display: flex;
    flex: 1 1 0;
    min-inline-size: min-content;
    max-inline-size: 6.5rem;
    margin: 0;
}

.ts-header__nav .mod-menu > li > :is(a, .mod-menu__separator, .mod-menu__heading) {
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    justify-content: center;
    gap: var(--q-s2);
    padding: 0 var(--q-s3);
    border-inline-end: var(--q-hair-w) solid var(--ec-border);
    color: var(--ec-fg);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--ec-dur-1) var(--ec-ease), box-shadow var(--ec-dur-1) var(--ec-ease);
}

.ts-header__nav .mod-menu > li:is(.current, .active) > a {
    box-shadow: inset 0 calc(-1 * var(--q-current-w)) 0 var(--ec-accent);
}

.ts-header__nav .mod-menu > li > a:focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

@media (hover: hover) and (pointer: fine) {
    .ts-header__nav .mod-menu > li > a:hover {
        background-color: var(--q-hover);
    }
}

/* Dropdowns (desktop): surface panel with a 2px ink top rule and a hairline frame, 44px rows. */
.ts-header__nav .mod-menu__sub {
    margin: 0;
    padding: 0;
    list-style: none;
}

@media (min-width: 56.25rem) {
    .ts-header__nav .mod-menu > li > .mod-menu__sub {
        position: absolute;
        inset-block-start: 100%;
        inset-inline-start: 0;
        z-index: var(--ts-z-dropdown);
        display: none;
        min-inline-size: 15rem;
        border: var(--q-hair-w) solid var(--ec-border);
        border-block-start: var(--q-rule-w) solid var(--q-rule);
        background-color: var(--ec-surface);
    }

    .ts-header__nav .mod-menu > li:is(:hover, :focus-within) > .mod-menu__sub,
    .ts-header__nav .mod-menu__sub.show-menu {
        display: block;
    }

    .ts-header__nav .mod-menu__sub li + li {
        border-block-start: var(--q-hair-w) solid var(--ec-border);
    }

    .ts-header__nav .mod-menu__sub a {
        display: flex;
        align-items: center;
        min-block-size: 2.75rem;
        padding: 0 var(--q-s4);
        color: var(--ec-fg);
        text-decoration: none;
    }

    .ts-header__nav .mod-menu__sub :is(.current, .active) > a {
        box-shadow: inset var(--q-current-w) 0 0 var(--ec-accent);
    }

    .ts-header__nav .mod-menu__sub a:focus-visible {
        outline-offset: -4px;
    }
}

@media (min-width: 56.25rem) and (hover: hover) and (pointer: fine) {
    .ts-header__nav .mod-menu__sub a:hover {
        background-color: var(--q-hover);
    }
}

/* Actions: header-actions modules, then the nav toggle. Every action is a flush cell with a
   hairline on its inline-start side. */
.ts-header__actions {
    display: flex;
    flex: none;
    align-items: stretch;
    margin-inline-start: auto;
}

.ts-header__modules {
    display: flex;
    align-items: stretch;
}

.ts-header__actions .ts-action {
    display: inline-flex;
    flex: 0 0 4.5rem;
    align-items: center;
    justify-content: center;
    gap: var(--q-s2);
    padding: 0;
    border: 0;
    border-inline-start: var(--q-hair-w) solid var(--ec-border);
    background: transparent;
    color: var(--ec-fg);
    font: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--ec-dur-1) var(--ec-ease);
}

.ts-header__actions .ts-action > svg {
    inline-size: 1.375rem;
    block-size: 1.375rem;
    stroke-width: 1.75;
}

.ts-header__actions .ts-action:focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

@media (hover: hover) and (pointer: fine) {
    .ts-header__actions .ts-action:hover {
        background-color: var(--q-hover);
    }
}

/* The Directory toggle (F3 labelled toggle): a text cell; the icon is not part of Quorum's
   design, the visible label is the accessible name (WCAG 2.5.3). */
.ts-header__actions .ts-nav-toggle {
    display: none;
}

.ts-header__actions .ts-nav-toggle--labelled {
    flex: 0 0 auto;
    padding: 0 var(--q-s4);
    font-size: 1rem;
    font-weight: 600;
}

.ts-header__actions .ts-nav-toggle--labelled > svg {
    display: none;
}

/* Compact ruled header, 56.25-75rem: the same cells, tighter (design spec §4.2 "compact header"). */
@media (width < 75rem) {
    .ts-header__inner {
        min-block-size: calc(var(--q-header-h-compact) - var(--q-rule-w));
    }

    .ts-header .ts-brand {
        gap: var(--q-s2);
        padding-inline-end: var(--q-s4);
    }

    .ts-brand__text {
        font-size: 1.75rem;
    }

    .ts-brand__logo {
        block-size: 2.125rem;
    }

    .ts-header .ts-brand--lockup .ts-brand__logo {
        inline-size: 1.875rem;
        block-size: 1.875rem;
    }

    .ts-header__nav .mod-menu > li {
        flex: 0 0 auto;
        max-inline-size: none;
    }

    .ts-header__actions .ts-action {
        flex-basis: 3.5rem;
    }
}

/* Mobile bar, below 56.25rem: brand then cells to the edge; the nav becomes the drawer. */
@media (width < 56.25rem) {
    .ts-header__inner {
        padding-inline-end: 0;
    }

    .ts-header .ts-brand {
        flex: 1 1 auto;
        padding-inline-end: var(--q-s3);
        border-inline-end: 0;
    }

    /* The name takes the room the bar's cells leave. Where a long name (or user text spacing,
       WCAG 1.4.12) needs more, it wraps onto a second line (between words first) instead of being
       cut: the bar grows rather than hiding letters (WCAG 1.4.10). */
    .ts-header .ts-brand__text {
        min-inline-size: 0;
        overflow-wrap: anywhere;
        text-wrap: balance;
        white-space: normal;
    }

    .ts-header__actions .ts-nav-toggle {
        display: inline-flex;
    }

    /* The drawer marks the current page; the brand keeps no bar on the phone bar. */
    .ts-home .ts-header .ts-brand {
        box-shadow: none;
    }
}

@media (width < 30rem) {
    .ts-brand__text {
        font-size: 1.625rem;
    }
}

/* ==========================================================================
   5. Header actions from modules: search cell + band (kit mod_finder "header"),
      account (mod_studio_community "account")
   ========================================================================== */

.ts-header__actions .ts-search--header {
    display: flex;
}

/* The search cell shows the current bar while its band is open. */
.ts-header__actions .ts-search__toggle[aria-expanded="true"] {
    background-color: var(--q-hover);
    box-shadow: inset 0 calc(-1 * var(--q-current-w)) 0 var(--ec-accent);
}

/* Search band under the header (design spec §6.4): 56px field with the glyph inside, a primary
   Search button and a close cell. template.js opens it and hides the panel while closed. */
.ts-header__actions .ts-search--header .ts-search__panel {
    position: absolute;
    inset-block-start: 100%;
    inset-inline: 0;
    z-index: var(--ts-z-dropdown);
    border-block-start: var(--q-rule-w) solid var(--q-rule);
    border-block-end: var(--q-rule-w) solid var(--q-rule);
    margin-block-start: calc(-1 * var(--q-rule-w));
    background-color: var(--ec-surface);
}

.ts-search--header .ts-search__form {
    display: flex;
    align-items: stretch;
    gap: var(--q-s3);
    inline-size: 100%;
    max-inline-size: calc(var(--ec-cw) + 2 * var(--page-gutter));
    margin-inline: auto;
    padding: var(--q-s4) var(--page-gutter);
}

.ts-search__field {
    position: relative;
    display: flex;
    flex: 1 1 auto;
    min-inline-size: 0;
}

/* Smart Search autosuggest wraps the input in div.awesomplete: let it take the field's width. */
.ts-search__field > .awesomplete {
    display: flex;
    flex: 1 1 auto;
    min-inline-size: 0;
}

.ts-search__icon {
    position: absolute;
    z-index: 1;
    inset-block: 0;
    inset-inline-start: var(--q-s4);
    display: flex;
    align-items: center;
    color: var(--ec-fg-2);
    pointer-events: none;
}

.ts-search__icon > svg {
    inline-size: 1.375rem;
    block-size: 1.375rem;
    stroke-width: 1.75;
}

.ts-search .ts-search__input {
    flex: 1 1 auto;
    inline-size: 100%;
    min-inline-size: 0;
    min-block-size: 3rem;
    padding: 0 0.875rem 0 3rem;
    border: var(--q-hair-w) solid var(--ts-control);
    border-radius: var(--ec-btn-radius);
    background-color: var(--ec-surface);
    color: var(--ec-fg);
    font-family: var(--ec-font-body);
    font-size: 1.0625rem;
}

.ts-search--header .ts-search__input {
    min-block-size: 3.5rem;
    font-size: 1.125rem;
}

.ts-search .ts-search__input::placeholder {
    color: var(--ec-fg-2);
    opacity: 1;
}

.ts-search .ts-search__input:focus {
    border-color: var(--ts-control-focus-border);
    box-shadow: inset 0 0 0 1px var(--ts-control-focus-border);
}

.ts-search .ts-search__input:focus-visible {
    outline: var(--ts-focus-width, 2px) solid var(--ts-focus);
    outline-offset: 2px;
    box-shadow: inset 0 0 0 1px var(--ts-control-focus-border), 0 0 0 2px var(--ts-focus-gap);
}

.ts-search--header .ts-search__submit {
    flex: none;
    min-block-size: 3.5rem;
}

.ts-header__actions .ts-search--header .ts-search__close {
    flex: 0 0 3.5rem;
    border: var(--q-hair-w) solid var(--ec-border);
}

/* Account (guest): "Sign in" text cell and the Join button (design spec §5.2 cells 5-6). */
.ts-header__actions .ts-account {
    display: flex;
    align-items: stretch;
}

.ts-header__actions .ts-account__login {
    display: inline-flex;
    align-items: center;
    padding: 0 var(--q-s6);
    border-inline: var(--q-hair-w) solid var(--ec-border);
    color: var(--ec-fg);
    font-weight: 600;
    text-decoration: none;
    white-space: nowrap;
    transition: background-color var(--ec-dur-1) var(--ec-ease);
}

.ts-header__actions .ts-account__login > svg {
    display: none;
}

.ts-header__actions .ts-account__login:focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

.ts-header__actions .ts-account__join,
.ts-header__actions .ts-account__primary {
    align-self: center;
    margin-inline-start: var(--q-s4);
    white-space: nowrap;
}

@media (hover: hover) and (pointer: fine) {
    .ts-header__actions .ts-account__login:hover {
        background-color: var(--q-hover);
    }
}

/* Account (member): bell cell with a square unread dot, the avatar cell and its menu. */
.ts-header__actions .ts-account__bell {
    position: relative;
    display: inline-flex;
    flex: 0 0 4.5rem;
    align-items: center;
    justify-content: center;
    border-inline-start: var(--q-hair-w) solid var(--ec-border);
    color: var(--ec-fg);
    transition: background-color var(--ec-dur-1) var(--ec-ease);
}

.ts-header__actions .ts-account__bell > svg {
    inline-size: 1.375rem;
    block-size: 1.375rem;
    stroke-width: 1.75;
}

.ts-header__actions .ts-account__count {
    position: absolute;
    inset-block-start: calc(50% - 0.8125rem);
    inset-inline-start: calc(50% + 0.375rem);
    inline-size: 0.625rem;
    block-size: 0.625rem;
    overflow: hidden;
    border: 2px solid var(--ec-surface);
    background-color: var(--ec-accent);
    color: transparent;
    font-size: 0;
}

.ts-header__actions .ts-account__menu {
    position: relative;
    display: flex;
}

.ts-header__actions .ts-account__toggle {
    display: flex;
    flex: 0 0 4.5rem;
    align-items: center;
    justify-content: center;
    border-inline-start: var(--q-hair-w) solid var(--ec-border);
    list-style: none;
    cursor: pointer;
    transition: background-color var(--ec-dur-1) var(--ec-ease);
}

.ts-header__actions .ts-account__toggle::-webkit-details-marker {
    display: none;
}

.ts-header__actions .ts-account__toggle > .ts-avatar {
    inline-size: 2.25rem;
    block-size: 2.25rem;
    font-size: 0.8125rem;
    font-weight: 700;
}

.ts-header__actions .ts-account__toggle > .ts-avatar::after {
    display: none;
}

.ts-header__actions .ts-account__toggle > :is(.ts-account__name, .ts-account__points),
.ts-header__actions .ts-account__toggle > .ts-icon {
    position: absolute;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.ts-header__actions :is(.ts-account__bell, .ts-account__toggle):focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

.ts-header__actions .ts-account__panel {
    position: absolute;
    inset-block-start: calc(100% + var(--q-rule-w));
    inset-inline-end: 0;
    z-index: var(--ts-z-dropdown);
    min-inline-size: 15rem;
    border: var(--q-hair-w) solid var(--ec-border);
    border-block-start: var(--q-rule-w) solid var(--q-rule);
    background-color: var(--ec-surface);
}

@media (hover: hover) and (pointer: fine) {
    .ts-header__actions :is(.ts-account__bell, .ts-account__toggle):hover {
        background-color: var(--q-hover);
    }
}

@media (width < 75rem) {
    .ts-header__actions :is(.ts-account__bell, .ts-account__toggle) {
        flex-basis: 3.5rem;
    }

    .ts-header__actions .ts-account__toggle > .ts-avatar {
        inline-size: 2.125rem;
        block-size: 2.125rem;
    }

    .ts-header__actions .ts-account__login {
        padding-inline: var(--q-s4);
    }

    /* "Start a thread" lives in the page (hero, forum section) below the full header. */
    .ts-header__actions .ts-account__primary {
        display: none;
    }
}

/* Between 900px and 1100px the compact header keeps Join for guests (design spec §5.3): the nav
   cells, search and Sign in tighten so the row still fits one line. */
@media (min-width: 56.25rem) and (width < 68.75rem) {
    .ts-header .ts-brand {
        padding-inline-end: var(--q-s3);
    }

    .ts-header__nav .mod-menu > li > :is(a, .mod-menu__separator, .mod-menu__heading) {
        padding: 0 0.5rem;
    }

    .ts-header__actions .ts-action {
        flex-basis: 2.75rem;
    }

    .ts-header__actions .ts-account__login {
        padding-inline: 0.625rem;
    }

    .ts-header__actions .ts-account__join {
        margin-inline-start: var(--q-s3);
        padding-inline: 0.625rem;
    }
}

/* Phones: guests keep search + Sign in (Join lives in the drawer); members keep the bell and
   the account cell (search lives in the drawer, design spec §8). */
@media (width < 56.25rem) {
    .ts-header__actions .ts-account__join {
        display: none;
    }

    .ts-header__actions .ts-account__login {
        border-inline-end: 0;
        padding-inline: 0.875rem;
    }

    .ts-member .ts-header__actions .ts-search--header {
        display: none;
    }
}

@media (width < 24rem) {
    .ts-header__actions .ts-account__login {
        display: none;
    }
}

@media (width < 22.5rem) {

    .ts-header__actions :is(.ts-action, .ts-account__bell, .ts-account__toggle) {
        flex-basis: 3rem;
    }

    .ts-header__actions .ts-nav-toggle--labelled {
        padding-inline: 0.625rem;
    }

    .ts-brand__text {
        font-size: 1.5rem;
    }
}

/* The site name is never cut on a phone (WCAG 1.4.10): below 420px the bar's cells tighten and
   the name steps down; below 384px a guest's Sign in moves to the drawer, and below 352px the
   Directory toggle keeps its name for assistive technology but shows only its icon. A name longer than the room left wraps (see above). */
@media (max-width: 26.25rem) {
    .ts-brand__text {
        font-size: 1.5rem;
    }

    .ts-header__actions :is(.ts-action, .ts-account__bell, .ts-account__toggle) {
        flex-basis: 3rem;
    }

    .ts-header__actions :is(.ts-nav-toggle--labelled, .ts-account__login) {
        padding-inline: 0.625rem;
    }
}

@media (width < 22rem) {
    .ts-header__actions .ts-nav-toggle--labelled {
        flex: 0 0 3rem;
        justify-content: center;
        padding-inline: 0;
    }

    .ts-header__actions .ts-nav-toggle--labelled > svg {
        display: block;
    }

    .ts-header__actions .ts-nav-toggle--labelled > .ts-nav-toggle__label {
        position: absolute;
        inline-size: 1px;
        block-size: 1px;
        overflow: hidden;
        clip-path: inset(50%);
        white-space: nowrap;
    }
}

/* ==========================================================================
   6. Directory drawer (< 56.25rem): the lib's #ts-nav slides in from the inline end over a
      scrim; template.js traps focus, sets inert/aria-hidden, Escape and focus return.
   ========================================================================== */

@media (width < 56.25rem) {
    .ts-nav-open {
        position: fixed;
        inset-block-start: var(--ts-scroll-lock, 0);
        inline-size: 100%;
        overscroll-behavior: none;
    }

    .ts-nav-open::before {
        content: "";
        position: fixed;
        inset: 0;
        z-index: var(--ts-z-scrim);
        background-color: var(--ts-scrim);
    }

    .ts-header__nav {
        position: fixed;
        inset-block: 0;
        inset-inline-end: 0;
        z-index: var(--ts-z-header);
        display: flex;
        flex-direction: column;
        align-items: stretch;
        inline-size: min(100%, 30rem);
        padding-block-start: var(--q-header-h-compact);
        overflow-y: auto;
        overscroll-behavior: contain;
        background-color: var(--ec-bg);
        visibility: hidden;
        transform: translateX(100%);
        transition: transform var(--ec-dur-2) var(--ec-ease), visibility 0s linear var(--ec-dur-2);
    }

    [dir="rtl"] .ts-header__nav {
        transform: translateX(-100%);
    }

    .ts-nav-open .ts-header__nav {
        visibility: visible;
        transform: none;
        transition: transform var(--ec-dur-2) var(--ec-ease), visibility 0s;
    }

    /* Top row: a replica of the header bar with a 2px ink rule; the close cell sits at its end. */
    .ts-header__nav::before {
        content: "";
        position: absolute;
        inset-block-start: 0;
        inset-inline: 0;
        block-size: var(--q-header-h-compact);
        border-block-end: var(--q-rule-w) solid var(--q-rule);
        background-color: var(--ec-surface);
    }

    .ts-nav-close {
        position: absolute;
        inset-block-start: 0;
        inset-inline-end: 0;
        z-index: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: var(--q-s2);
        block-size: calc(var(--q-header-h-compact) - var(--q-rule-w));
        padding: 0 0.875rem;
        border: 0;
        border-inline-start: var(--q-hair-w) solid var(--ec-border);
        background: transparent;
        color: var(--ec-fg);
        font: inherit;
        font-weight: 600;
        cursor: pointer;
    }

    /* The toggle's visible label switches to the (translated) close label. */
    .ts-nav-close::before {
        content: attr(aria-label);
    }

    /* The bar's title: the navigation's own (translated) name as a mono agenda label. */
    .ts-header__nav::after {
        content: attr(aria-label);
        position: absolute;
        inset-block-start: 0;
        inset-inline-start: var(--q-s4);
        display: flex;
        align-items: center;
        block-size: calc(var(--q-header-h-compact) - var(--q-rule-w));
        color: var(--ec-fg-2);
        font-family: var(--q-font-mono);
        font-size: 0.875rem;
        font-weight: 600;
        letter-spacing: 0.02em;
        text-transform: uppercase;
    }

    .ts-nav-close > svg {
        inline-size: 1.375rem;
        block-size: 1.375rem;
    }

    .ts-nav-close:focus-visible {
        outline-offset: -4px;
        box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
    }

    /* The menu as a poster-style directory: 56px rows in Big Shoulders, a chevron, a 4px accent
       bar on the current item; children indented underneath. */
    .ts-header__nav .mod-menu {
        flex-direction: column;
        border-block-end: var(--q-hair-w) solid var(--ec-border);
        background-color: var(--ec-surface);
    }

    .ts-header__nav .mod-menu > li {
        flex-direction: column;
        border-block-start: var(--q-hair-w) solid var(--ec-border);
    }

    .ts-header__nav .mod-menu > li:first-child {
        border-block-start: 0;
    }

    .ts-header__nav .mod-menu > li > :is(a, .mod-menu__separator, .mod-menu__heading) {
        justify-content: space-between;
        min-block-size: 3.5rem;
        padding: 0 var(--q-s4);
        border-inline-end: 0;
        font-family: var(--ec-font-display);
        font-size: 1.5rem;
        font-weight: 700;
    }

    .ts-header__nav .mod-menu > li > a::after {
        content: "";
        flex: none;
        inline-size: 1.25rem;
        block-size: 1.25rem;
        background-color: currentColor;
        -webkit-mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m9 6 6 6-6 6'/%3e%3c/svg%3e") center / contain no-repeat;
        mask: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m9 6 6 6-6 6'/%3e%3c/svg%3e") center / contain no-repeat;
    }

    [dir="rtl"] .ts-header__nav .mod-menu > li > a::after {
        transform: scaleX(-1);
    }

    .ts-header__nav .mod-menu > li:is(.current, .active) > a {
        box-shadow: inset var(--q-current-w) 0 0 var(--ec-accent);
    }

    .ts-header__nav .mod-menu__sub a {
        display: flex;
        align-items: center;
        min-block-size: 3rem;
        padding: 0 var(--q-s4) 0 var(--q-s8);
        border-block-start: var(--q-hair-w) solid var(--ec-border);
        color: var(--ec-fg);
        text-decoration: none;
    }

    .ts-header__nav .mod-menu__sub :is(.current, .active) > a {
        box-shadow: inset var(--q-current-w) 0 0 var(--ec-accent);
    }

    .ts-header__nav :is(.mod-menu a, .mod-menu__sub a):focus-visible {
        outline-offset: -4px;
    }

    /* Drawer extras (F2 offcanvas position): search, account, Display tools. */
    .ts-header__offcanvas {
        display: flex;
        flex-direction: column;
        gap: var(--q-s6);
        padding: var(--q-s6) var(--q-s4) var(--q-s8);
    }

    .ts-header__offcanvas .ts-search--drawer .ts-search__label {
        display: block;
        margin-block-end: var(--q-s2);
        font-weight: 600;
    }

    .ts-header__offcanvas .ts-search__row {
        display: flex;
        gap: 0;
    }

    .ts-header__offcanvas .ts-search__submit {
        flex: none;
        margin-inline-start: calc(-1 * var(--q-hair-w));
    }

    .ts-header__offcanvas .ts-account {
        display: flex;
        flex-direction: column;
        gap: var(--q-s3);
    }

    .ts-header__offcanvas .ts-account__login,
    .ts-header__offcanvas .ts-account__join,
    .ts-header__offcanvas .ts-account__primary {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: var(--q-s2);
        inline-size: 100%;
        min-block-size: 3rem;
    }

    .ts-header__offcanvas .ts-account__login {
        border: var(--ts-btn-border-width, 2px) solid var(--q-rule);
        color: var(--ec-fg);
        font-weight: 600;
        text-decoration: none;
    }

    .ts-header__offcanvas .ts-account__bell {
        display: flex;
        align-items: center;
        gap: var(--q-s3);
        min-block-size: 3rem;
        padding: 0 var(--q-s4);
        border: var(--q-hair-w) solid var(--ec-border);
        background-color: var(--ec-surface);
        color: var(--ec-fg);
        font-weight: 600;
        text-decoration: none;
    }

    /* The bell's name ("3 unread notifications") becomes its visible drawer label. */
    .ts-header__offcanvas .ts-account__bell::after {
        content: attr(aria-label);
    }

    .ts-header__offcanvas .ts-account__count {
        display: none;
    }

    .ts-header__offcanvas .ts-account__panel {
        border-block-start: var(--q-hair-w) solid var(--ec-border);
    }

    .ts-header__offcanvas .ts-account__menu {
        border: var(--q-hair-w) solid var(--ec-border);
        background-color: var(--ec-surface);
    }

    .ts-header__offcanvas .ts-account__toggle {
        display: flex;
        align-items: center;
        gap: var(--q-s3);
        min-block-size: 3.5rem;
        padding: 0 var(--q-s4);
        font-weight: 600;
        list-style: none;
        cursor: pointer;
    }

    .ts-header__offcanvas .ts-account__toggle::-webkit-details-marker {
        display: none;
    }

    .ts-header__offcanvas .ts-account__toggle > .ts-icon {
        margin-inline-start: auto;
    }

    .ts-header__offcanvas .ts-account__points {
        color: var(--ec-fg-2);
        font-family: var(--q-font-mono);
        font-size: 0.875rem;
    }
}

/* Account menu list (header dropdown and drawer): 44px rows, hairline separators. */
.ts-account__links {
    margin: 0;
    padding: 0;
    list-style: none;
}

.ts-account__links > li + li,
.ts-account__logout {
    border-block-start: var(--q-hair-w) solid var(--ec-border);
}

.ts-account__logout {
    margin: 0;
}

.ts-account__item {
    display: flex;
    align-items: center;
    gap: var(--q-s3);
    inline-size: 100%;
    min-block-size: 2.75rem;
    padding: 0 var(--q-s4);
    border: 0;
    background: transparent;
    color: var(--ec-fg);
    font: inherit;
    text-align: start;
    text-decoration: none;
    cursor: pointer;
}

.ts-account__item:focus-visible {
    outline-offset: -4px;
}

@media (hover: hover) and (pointer: fine) {
    .ts-account__item:hover {
        background-color: var(--q-hover);
    }
}

/* ==========================================================================
   7. Mega menu skin (lib megamenu.css prints first; single-class selectors win by order)
   ========================================================================== */

.ts-mega {
    border: var(--q-hair-w) solid var(--ec-border);
    border-block-start: var(--q-rule-w) solid var(--q-rule);
    border-radius: 0;
    background-color: var(--ec-surface);
    box-shadow: none;
}

.ts-mega__title {
    margin: 0 0 var(--q-s3);
    color: var(--ec-fg);
    font-size: 1.125rem;
    font-weight: 700;
}

.ts-mega__links a {
    color: var(--ec-fg);
    text-decoration: none;
}

.ts-mega__badge {
    border-radius: 0;
    background-color: var(--q-signal);
    color: var(--q-signal-fg);
}

.ts-mega-toggle {
    border-radius: 0;
}

/* ==========================================================================
   8. Footer: surface-2 band, lettered directory, bottom bar with the Display tools
   ========================================================================== */

.ts-footer {
    padding-block: var(--q-s11) var(--q-s2);
    background-color: var(--ec-surface-2);
    color: var(--ec-fg);
}

.ts-footer > .ts-container {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
        "cols cols"
        "bottom main";
}

.ts-footer__cols {
    grid-area: cols;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    counter-reset: q-dir;
    border-block-start: var(--q-hair-w) solid var(--ec-border);
}

.ts-footer__col {
    display: flex;
    flex-direction: column;
    gap: var(--q-s4);
    grid-column: span 3;
    min-inline-size: 0;
    min-block-size: 14.5rem;
    padding: var(--q-s7) var(--q-s8);
    border-inline-start: var(--q-hair-w) solid var(--ec-border);
}

.ts-footer__col.ts-footer__brand {
    gap: var(--q-s5);
    padding-inline-start: 0;
    border-inline-start: 0;
}

.ts-footer .ts-brand {
    display: inline-flex;
    align-items: center;
    gap: var(--q-s3);
    align-self: flex-start;
    color: var(--ec-fg);
    text-decoration: none;
}

.ts-footer .ts-brand__text {
    font-size: 2.25rem;
}

.ts-footer .ts-brand__logo {
    block-size: 2.75rem;
}

.ts-footer__blurb {
    margin: 0;
    color: var(--ec-fg-2);
    line-height: 1.5;
}

/* Guests: the primary "Join <site>" under the blurb (§5.4; partials/footer.php). */
.q-footer-join {
    margin: auto 0 0;
}

/* Column heading: the mono accent letter (A, B, C… from a counter) before the title. */
.ts-footer__col > :is(h2, h5) {
    display: flex;
    align-items: baseline;
    gap: var(--q-s3);
    margin: 0;
    color: var(--ec-fg);
    font-family: var(--ec-font-body);
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.2;
}

.ts-footer__col > :is(h2, h5)::before {
    counter-increment: q-dir;
    content: counter(q-dir, upper-alpha);
    color: var(--ts-link);
    font-family: var(--q-font-mono);
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1;
}

.ts-footer__col > ul {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* 44px targets at every width (contract §2.12, design spec footer: "every text link
   min-height 44px"); the list has no gap, so the pitch stays 44px. */
.ts-footer__col > ul a,
.ts-footer__col .ts-module a {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    line-height: 1.2;
    color: var(--ec-fg);
    text-decoration: none;
    text-underline-offset: 3px;
}

@media (hover: hover) and (pointer: fine) {
    .ts-footer__col > ul a:hover,
    .ts-footer__col .ts-module a:hover {
        color: var(--ts-link-hover);
        text-decoration: underline;
        text-decoration-thickness: 1px;
    }
}

.ts-footer .ts-social-links {
    display: flex;
    gap: var(--q-s1);
}

.ts-footer .ts-social {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border: var(--q-hair-w) solid var(--q-rule);
    color: var(--ec-fg);
}

/* Bottom bar (72px): © and legal links left, the Display tools (footer position) right. */
.ts-footer__bottom,
.ts-footer__main {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    min-block-size: 4.5rem;
    border-block-start: var(--q-hair-w) solid var(--ec-border);
}

.ts-footer__bottom {
    grid-area: bottom;
    gap: var(--q-s2) var(--q-s6);
    color: var(--ec-fg-2);
}

.ts-footer__main {
    grid-area: main;
    justify-content: flex-end;
    gap: var(--q-s4);
    padding-block: var(--q-s3);
}

.ts-footer__copy {
    margin: 0;
}

/* "Last updated 28 SEP 2026" (partial footer). */
.q-footer-updated {
    margin: 0;
    color: var(--ec-fg-2);
    font-family: var(--q-font-mono);
    font-size: 0.875rem;
}

.ts-footer__legal,
.ts-footer__pay {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--q-s6);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ts-footer__legal a {
    display: inline-flex;
    align-items: center;
    min-block-size: 2.75rem;
    color: var(--ec-fg);
    text-underline-offset: 3px;
}

.ts-footer__pay > li {
    font-family: var(--q-font-mono);
    font-size: 0.875rem;
    font-weight: 600;
}

@media (hover: hover) and (pointer: fine) {
    .ts-footer__legal a:hover {
        color: var(--ts-link-hover);
        text-decoration-thickness: 2px;
    }
}

/* Display tools in the footer and the drawer: the segmented Light / Dark / Auto control and
   the language list (design spec §5.4). */
.ts-tools {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--q-s4);
}

.ts-tools__scheme {
    display: flex;
    border: var(--q-hair-w) solid var(--q-rule);
}

.ts-tools__scheme-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--q-s2);
    min-block-size: 2.75rem;
    padding: 0 0.875rem;
    border: 0;
    background: transparent;
    color: var(--ec-fg);
    font-family: var(--ec-font-body);
    font-size: 0.9375rem;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background-color var(--ec-dur-1) var(--ec-ease), color var(--ec-dur-1) var(--ec-ease);
}

.ts-tools__scheme-button + .ts-tools__scheme-button {
    border-inline-start: var(--q-hair-w) solid var(--q-rule);
}

.ts-tools__scheme-button > svg {
    inline-size: 1rem;
    block-size: 1rem;
    stroke-width: 1.75;
}

.ts-tools__scheme-button[aria-pressed="true"] {
    background-color: var(--q-inverse-bg);
    color: var(--q-inverse-fg);
}

.ts-tools__scheme-button:focus-visible {
    outline-offset: -4px;
    box-shadow: inset 0 0 0 4px var(--ts-focus-gap);
}

@media (hover: hover) and (pointer: fine) {
    .ts-tools__scheme-button:not([aria-pressed="true"]):hover {
        background-color: var(--q-hover);
    }
}

.ts-tools__language-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--q-s2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.ts-tools__language {
    display: inline-flex;
    align-items: center;
    gap: var(--q-s2);
    min-block-size: 2.75rem;
    padding: 0 0.875rem;
    border: var(--q-hair-w) solid var(--q-rule);
    color: var(--ec-fg);
    font-size: 0.9375rem;
    font-weight: 600;
    text-decoration: none;
}

.ts-tools__language--active {
    background-color: var(--q-inverse-bg);
    color: var(--q-inverse-fg);
}

.ts-tools__language > svg {
    inline-size: 1rem;
    block-size: 1rem;
    stroke-width: 1.75;
}

.ts-header__offcanvas .ts-tools {
    flex-direction: column;
    align-items: stretch;
}

.ts-header__offcanvas .ts-tools__scheme-button {
    flex: 1 1 0;
}

/* Phones (design spec §5.5): brand row under a 2px rule, three lettered columns, then legal and
   the Display tools stacked full width. */
@media (width < 64rem) {
    .ts-footer__col {
        grid-column: span 4;
        min-block-size: 0;
        padding: var(--q-s4) var(--q-s3) var(--q-s2);
    }

    .ts-footer__col.ts-footer__brand {
        grid-column: 1 / -1;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--q-s3) var(--q-s6);
        padding: var(--q-s5) 0 var(--q-s6);
        border-block-start: var(--q-rule-w) solid var(--q-rule);
    }

    .ts-footer__cols {
        border-block-start: 0;
    }

    .ts-footer__col.ts-footer__brand + .ts-footer__col {
        padding-inline-start: 0;
        border-inline-start: 0;
    }

    .ts-footer__col:not(.ts-footer__brand) {
        border-block-start: var(--q-hair-w) solid var(--ec-border);
    }

    .ts-footer__col:last-child {
        padding-inline-end: 0;
    }

    .ts-footer__col > :is(h2, h5) {
        gap: 0.375rem;
        padding-block-end: var(--q-s1);
        font-size: 1rem;
    }

    .ts-footer__col > :is(h2, h5)::before {
        font-size: 1.125rem;
    }

    .ts-footer__col > ul {
        gap: 0;
    }

    .ts-footer__col > ul a {
        min-block-size: 2.75rem;
        line-height: 1.25;
    }
}

@media (width < 48rem) {
    .ts-footer {
        padding-block: var(--q-s8) var(--q-s4);
    }

    .ts-footer > .ts-container {
        grid-template-columns: minmax(0, 1fr);
        grid-template-areas:
            "cols"
            "bottom"
            "main";
    }

    .ts-footer__brand .ts-brand__text {
        font-size: 1.875rem;
    }

    .ts-footer__bottom {
        justify-content: space-between;
        min-block-size: 0;
        padding-block-start: var(--q-s3);
    }

    .ts-footer__main {
        justify-content: stretch;
        min-block-size: 0;
        padding-block: var(--q-s4) 0;
        border-block-start: 0;
    }

    .ts-footer__main .ts-tools,
    .ts-footer__main .ts-module {
        inline-size: 100%;
    }

    .ts-footer__main .ts-tools__scheme {
        inline-size: 100%;
    }

    .ts-footer__main .ts-tools__scheme-button {
        flex: 1 1 0;
    }
}

/* Very narrow phones: two lettered columns instead of three. */
@media (width < 23.5rem) {
    .ts-footer__col {
        grid-column: span 6;
    }

    .ts-footer__col.ts-footer__brand + .ts-footer__col + .ts-footer__col + .ts-footer__col {
        padding-inline-start: 0;
        border-inline-start: 0;
    }
}

/* ==========================================================================
   9. App mode and stacking (contract §2.2, §2.11)
   Pages hosting an extension app: a static header (no sticky bar, no stacking context), the
   content row at full width with main first and the sidebars stacked below it.
   ========================================================================== */

.ts-app .ts-header,
.ts-app .ts-header.is-sticky {
    position: relative;
    z-index: auto;
}

.ts-app .ts-row[data-ts-row="content"] > .ts-col {
    grid-column: 1 / -1;
}

.ts-app .ts-row[data-ts-row="content"] > .ts-pos-main {
    order: -1;
}

.ts-app .ts-main {
    max-inline-size: none;
}

/* ==========================================================================
   10. Error and offline documents (design spec §9: "This page isn't on the board").
   A container-centred ruled board: the status code as a poster numeral in a tint panel
   (5 of 12 columns, cropped by the cell edge like the course panels), the copy and actions
   in the cell beside it, the search (or the offline sign-in) in the cell below.
   ========================================================================== */

.ts-error-page,
.ts-offline-page {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-block-size: 100svh;
}

.ts-error {
    inline-size: 100%;
    max-inline-size: calc(var(--ec-cw) + 2 * var(--page-gutter));
    margin-inline: auto;
    padding: clamp(1.5rem, 4.5vw, 5rem) var(--page-gutter);
}

.ts-error__card {
    position: relative;
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: var(--q-hair-w);
    padding: 0 var(--q-hair-w) var(--q-hair-w);
    border-block-start: var(--q-rule-w) solid var(--q-rule);
    background-color: var(--ec-border);
}

/* The tint panel is the card's first cell; the numeral is drawn into it. */
.ts-error-page .ts-error__card::before {
    content: "";
    grid-column: 1 / span 5;
    grid-row: 1 / span 2;
    background-color: var(--q-tint);
}

.ts-error__main,
.ts-error__aside {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--q-s4);
    min-inline-size: 0;
    background-color: var(--ec-surface);
}

.ts-error__main {
    grid-column: 6 / -1;
    grid-row: 1;
    padding: clamp(1.75rem, 3vw, 2.75rem) clamp(1.25rem, 3.4vw, 3rem) var(--q-s8);
}

.ts-error__aside {
    grid-column: 6 / -1;
    grid-row: 2;
    align-items: stretch;
    padding: var(--q-s7) clamp(1.25rem, 3.4vw, 3rem) clamp(1.75rem, 3vw, 2.75rem);
}

.ts-error__code {
    position: absolute;
    inset-block: 0 var(--q-hair-w);
    inset-inline-start: var(--q-hair-w);
    display: flex;
    align-items: flex-end;
    inline-size: calc((100% - 2 * var(--q-hair-w) - 11 * var(--q-hair-w)) * 5 / 12 + 4 * var(--q-hair-w));
    margin: 0;
    padding-inline-start: var(--q-s5);
    overflow: clip;
    color: var(--ec-accent);
    font-family: var(--ec-font-display);
    font-size: clamp(9.375rem, 12vw, 15rem);
    font-weight: 900;
    line-height: 0.75;
    font-synthesis: none;
    user-select: none;
}

.ts-error__code::after {
    content: "";
    flex: none;
    block-size: 0.18em;
}

.ts-error__brand {
    margin: 0;
}

/* The same lockup as the header and footer (brand.lockup): the chainring mark beside the name. */
.ts-error__brand .ts-brand--lockup {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
}

.ts-error__brand .ts-brand__text {
    font-size: inherit;
}

.ts-error__brand,
.ts-error__brand a {
    color: var(--ec-fg);
    font-family: var(--ec-font-display);
    font-size: 1.75rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    line-height: 1;
    text-decoration: none;
}

.ts-error__label,
.ts-error__path {
    margin: 0;
    color: var(--ec-fg-2);
    font-family: var(--q-font-mono);
    font-size: 0.875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    overflow-wrap: anywhere;
}

.ts-error__path {
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
}

.ts-error__title {
    margin: 0;
    color: var(--ec-fg);
    font-family: var(--ec-font-display);
    font-size: clamp(2.5rem, 2.3vw + 1.9rem, 3.5rem);
    font-weight: 700;
    line-height: 1;
    text-wrap: balance;
    font-synthesis: none;
}

.ts-error__copy {
    display: flex;
    flex-direction: column;
    gap: var(--q-s2);
    max-inline-size: 42rem;
}

.ts-error__copy > p {
    margin: 0;
    color: var(--ec-fg-2);
    font-size: 1.125rem;
    line-height: 1.5;
}

.ts-error__copy > .ts-error__detail {
    font-size: 1rem;
}

.ts-error__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--q-s3);
}

.ts-error__search {
    display: flex;
    flex-direction: column;
    gap: var(--q-s2);
    margin: 0;
}

.ts-error__search-label {
    color: var(--ec-fg);
    font-weight: 600;
}

.ts-error__search-row {
    display: flex;
    gap: var(--q-s3);
}

.ts-error__search-input {
    flex: 1 1 auto;
    min-inline-size: 0;
}

.ts-error__module,
.ts-error__debug {
    min-inline-size: 0;
    overflow-wrap: anywhere;
}

.ts-error__debug {
    margin-block-start: var(--q-s6);
    font-family: var(--q-font-mono);
    font-size: 0.875rem;
}

/* Offline: the hi-vis notice strip heads the board instead of a numeral; the sign-in form
   sits in the cell beside the message. */
.ts-offline .ts-error__card {
    grid-template-rows: auto;
}

.ts-offline .ts-error__main {
    grid-column: 1 / span 7;
}

.ts-offline .ts-error__aside {
    grid-column: 8 / -1;
    grid-row: 1;
    padding-block-start: clamp(1.75rem, 3vw, 2.75rem);
}

.ts-offline .ts-error__label {
    order: -1;
    align-self: stretch;
    margin: calc(-1 * clamp(1.75rem, 3vw, 2.75rem)) calc(-1 * clamp(1.25rem, 3.4vw, 3rem)) var(--q-s2);
    padding: var(--q-s3) clamp(1.25rem, 3.4vw, 3rem);
    background-color: var(--q-signal);
    color: var(--q-signal-fg);
}

.ts-offline__media img {
    display: block;
    max-block-size: 12rem;
    inline-size: auto;
}

.ts-offline__form,
.ts-offline__fieldset {
    display: flex;
    flex-direction: column;
    gap: var(--q-s4);
    min-inline-size: 0;
    margin: 0;
    padding: 0;
    border: 0;
}

.ts-offline__legend {
    margin: 0 0 var(--q-s2);
    padding: 0;
    color: var(--ec-fg);
    font-family: var(--ec-font-display);
    font-size: 1.5rem;
    font-weight: 700;
    line-height: 1;
}

.ts-offline .ts-check {
    flex-direction: row;
    align-items: center;
    gap: var(--q-s3);
    min-block-size: 2.75rem;
}

.ts-offline .ts-check > .form-check-input {
    inline-size: 1.5rem;
    block-size: 1.5rem;
    margin: 0;
    accent-color: var(--ec-accent);
}

.ts-offline .ts-error__aside joomla-alert,
.ts-offline .ts-error__aside .alert {
    margin: 0;
}

@media (width < 48rem) {
    .ts-error__card {
        grid-template-columns: minmax(0, 1fr);
    }

    .ts-error-page .ts-error__card::before {
        grid-column: 1;
        grid-row: 1;
        block-size: 10rem;
    }

    .ts-error__main,
    .ts-error__aside,
    .ts-offline .ts-error__main,
    .ts-offline .ts-error__aside {
        grid-column: 1;
        grid-row: auto;
    }

    .ts-error-page .ts-error__main {
        grid-row: 2;
    }

    .ts-error__code {
        inset-block: 0 auto;
        inline-size: calc(100% - 2 * var(--q-hair-w));
        block-size: 10rem;
        font-size: 9.375rem;
    }

    .ts-error__search-row {
        flex-direction: column;
    }
}

/* ==========================================================================
   System messages on the designed home: the first thing in <main> (partials/rows.php), in the
   page flow above the welcome strip and the hero, so they never cover the header or a focused
   control (WCAG 2.4.11) and scroll away with the page.
   ========================================================================== */
.ts-home .ts-main--rows > #system-message-container:not(:empty) {
    margin-block-end: var(--q-s6);
}
