/* ---------------------------------------------------------------------
 * kb.php -- the knowledge base served from our own database.
 *
 * Matches the support form's skin rather than the old kb.php, which is a
 * 2000s table layout with expand/collapse arrow images. Same content, same
 * grouping, same information on each row; the customer arriving from a
 * citation should feel they are still on the same site.
 * ------------------------------------------------------------------ */

.kb-page { background: var(--color-bg, #f4f2f9); }

/* The form's own .wrap is 52rem, which is right for a form -- a narrow
   measure keeps the fields from stretching into unusable lengths. An index
   page is not a form, and at 52rem it sat as a column of content in the
   middle of an empty screen.
   
   Measured off the pages we were asked to match: Apple Support runs its
   content to about 1065px, NVIDIA to 1165, the Google store to 1535. 1200
   sits with NVIDIA and gives the article rows room without letting a line
   of body text run past a comfortable reading length. */
/* One source for the page's width and gutter. The hero is laid out from
   the same two numbers, which is what keeps its left edge on the same
   line as the panels below it. */
.kb-page {
    --kb-max: var(--content-max, 1200px);
    --kb-gutter: 2rem;

    /* How far the title sits clear of the id column. The body text is laid
       out from the same value, so an article's text starts under its own
       title rather than under the number beside it. */
    --kb-title-step: 2.5rem;
}

.kb-wrap {
    max-width: var(--kb-max);
    margin: 0 auto;
    padding: 2.5rem var(--kb-gutter) 5rem;
}

/* The navigation lines up with the content, as it does on all three of
   those pages. Scoped to this page so the form keeps its own width. */
body.kb-page .site-header .wrap { max-width: var(--kb-max); }

/* ---------------------------------------------------------------------
 * The hero band.
 *
 * Full width, like the pages we were asked to match. The photograph is
 * pinned to the right at its own height and never stretched -- there is
 * no larger copy of it, so any upscale would show. The black simply fills
 * whatever width is left, which means a wider window gets more black
 * rather than a softer picture. The words are live text over it.
 * ------------------------------------------------------------------ */

.kb-hero {
    position: relative;

    /* Two cells: the black panel and the photograph. The photograph takes
       twice the width of the panel, so the ratio holds at every window
       size instead of drifting with the picture's natural width. */
    display: grid;
    grid-template-columns: 1fr 2fr;
    align-items: stretch;

    /* Inset and rounded, the way the Google store sets its hero, rather
       than running to the window edges. It shares the content's width, so
       its left edge lines up with the panels below it. */
    max-width: calc(var(--kb-max) - var(--kb-gutter) * 2);
    margin: 1.5rem auto 0;
    border-radius: 16px;

    background: #0a0a0a;
    color: #fff;
    overflow: hidden;
}

/* 600:269, which is the shape assets/support-hero.jpg is cropped to.
   
   Marketing supplies the hero as a 22:11 SOCIAL banner, with the
   product lineup pushed into the lower right because the upper left is
   copy space for a headline overlay. We do not use that space -- the
   black SUPPORT panel is already sitting in it -- so dropping the file
   in whole renders 37% empty sky and shrinks the products.
   
   So the file in assets/ is a crop, and this stays at the band shape.
   See design-source/README.md for the crop and how to redo it. */
.kb-hero::after {
    content: "";
    grid-column: 2;
    width: 100%;
    align-self: start;
    aspect-ratio: 600 / 269;
    /* The ?v= is a cache-buster and it is not optional.
       
       Nothing sets Cache-Control on the assets directory, so browsers
       cache images on a heuristic off Last-Modified -- which means a
       returning visitor keeps whatever hero they saw last time, for
       days. Replacing the file is not enough; the URL has to change.
       
       The stylesheet itself is versioned by asset(), so a new name
       here reaches everybody as soon as they load the new CSS.
       
       BUMP THIS WHENEVER YOU REPLACE THE IMAGE. */
    background: url("support-hero.jpg?v=20261014b") center / cover no-repeat;
}

.kb-hero-inner {
    grid-column: 1;
    align-self: center;
    padding: 2rem 1.5rem;
    min-width: 0;
    text-align: center;
}

.kb-hero-title {
    margin: 0 0 .5rem;
    font-size: clamp(1.9rem, 1.2rem + 2vw, 2.9rem);
    font-weight: 400;
    letter-spacing: .02em;
    line-height: 1.05;
    text-transform: uppercase;
}

.kb-hero-sub {
    margin: 0;
    font-size: clamp(.78rem, .7rem + .28vw, .92rem);
    font-weight: 500;
    letter-spacing: .1em;
    line-height: 1.5;
    text-transform: uppercase;
    color: #d6d0e4;
}

/* The word set on its side, as the live page has it -- decoration, so it
   is kept away from screen readers rather than read out sideways. */
/* UNUSED since 28 October. Robert: "Please delete the support banner.
   It interferes with the elegance of the photo. We don't need support
   duplicate on the right." The <span> is gone from every page; these
   rules are left in place because deleting them tells the next person
   nothing, and this one sentence tells them everything. */
.kb-hero-tab {
    position: absolute;
    right: 0;
    bottom: 0;
    transform-origin: 100% 100%;
    transform: rotate(-90deg) translateX(100%);
    background: #fff;
    color: #6b539c;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .22em;
    line-height: 1;
    padding: .45rem 1.1rem;
}

/* Narrower than the full layout, the hero keeps the page's own gutter
   rather than touching the window edges. */
@media (max-width: 1200px) {
    .kb-hero {
        margin-left: var(--kb-gutter);
        margin-right: var(--kb-gutter);
    }
}

/* Under about 900px the black no longer reaches past the photograph and
   the words would sit on top of the laptops. The photograph goes; the
   band keeps working as a band. */
@media (max-width: 56rem) {
    .kb-hero {
        grid-template-columns: 1fr;
        min-height: 180px;
        margin-left: var(--kb-gutter);
        margin-right: var(--kb-gutter);
    }

    .kb-hero::after { display: none; }

    .kb-hero-tab { display: none; }
}


.kb-title { font-size: 2rem; line-height: 1.15; margin: 0 0 .35rem; text-wrap: balance; }

.kb-count { color: var(--color-muted, #6e6e73); margin: 0 0 2rem; font-size: .95rem; }

.kb-group { margin-bottom: .4rem; }
.kb-group[open] { margin-bottom: 1.75rem; }

.kb-group-head {
    font-size: 1rem;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: #1d1d1f;
    margin: 0;
    padding: .3rem 0;
}

.kb-group-head span {
    font-weight: 400;
    opacity: .7;
    margin-left: .35rem;
}

/* Column headings, so the three fields on each row are labelled the way
   the live page labels them. Hidden on narrow screens, where the rows
   stack and the headings would sit over the wrong things. */
.kb-colheads {
    display: flex;
    gap: 1rem;
    align-items: baseline;
    padding: 0 1.1rem .4rem;
    font-size: .78rem;
    color: var(--color-muted, #6e6e73);
    font-weight: 600;
}

.kb-colheads .kb-article-title { font-weight: 600; }

/* Stands in for the caret the rows draw, so ID and POST DATE sit over the
   numbers and dates rather than a caret's width to their left. */
.kb-colheads::before {
    content: "";
    flex: 0 0 auto;
    width: .42rem;
    margin-right: .15rem;
}


/* The article number, as quoted on the phone. Fixed width so titles line
   up down the page rather than starting wherever the id ends. */
.kb-article-id {
    flex: 0 0 3.4rem;
    font-variant-numeric: tabular-nums;
    color: var(--color-muted, #6e6e73);
    font-size: .88rem;
}

/* Where the article came from, as a colour on its number.
 *
 * Two sources feed this page and a reader could not tell them apart --
 * which matters because the id shown is ambiguous: Salesforce ids are
 * stored +1,000,000 and displayed with that stripped off, so a
 * Salesforce article and a TSKB article can both show "1181".
 *
 * COLOUR IS NOT THE ONLY SIGNAL. Each number also carries a title
 * attribute naming its source, so the distinction survives for anyone
 * who cannot separate these two hues -- and the two chosen differ in
 * lightness as well as hue, so they stay apart in greyscale and when
 * printed.
 *
 * Deliberately quiet. This is useful to Sonnet staff and harmless to a
 * customer, but it is not what either came to the page to read, so it
 * should not compete with the title. */
.kb-article-id--salesforce {
    color: #1d6f42;            /* green, darker than the body text */
    font-weight: 600;
}

.kb-article-id--tskb {
    color: #8a6d1f;            /* amber, lighter than the Salesforce green */
    font-weight: 600;
}

/* One article. Closed it is a row; open it is the article. The border
   lives on the <details> so an open one reads as a single block rather
   than a heading with loose text under it. */
.kb-article {
    background: #fff;
    border: 1px solid var(--color-border, #d2d2d7);
    border-radius: 10px;
    margin-bottom: .5rem;
    overflow: hidden;
}

.kb-article > summary {
    display: flex;
    gap: 1rem;
    align-items: baseline;
    padding: .85rem 1.1rem;
    cursor: pointer;
    list-style: none;
}

.kb-article > summary::-webkit-details-marker { display: none; }

/* A caret drawn in CSS rather than an image, so it turns with the state
   and costs no request. */
.kb-article > summary::before {
    content: "";
    flex: 0 0 auto;
    width: .42rem; height: .42rem;
    margin-right: .15rem;
    border-right: 2px solid var(--color-accent, #6b539c);
    border-bottom: 2px solid var(--color-accent, #6b539c);
    transform: rotate(-45deg);
    transition: transform .15s ease;
}

.kb-article[open] > summary::before { transform: rotate(45deg); }

.kb-article > summary:hover { background: #faf8fd; }

/* The title takes whatever room is left, which carries the date column
   out to the panel's right edge.
   
   The indent moves it clear of the id column. Left tight against it, the
   row read as one crowded clump on the left with the date stranded on the
   right; a step in gives the titles their own column. They stay ranged
   left rather than centred -- a list is scanned down its first letters,
   and centring makes every line start somewhere different. */
.kb-article-title {
    flex: 1 1 auto;
    font-weight: 600;
    padding-left: var(--kb-title-step);

    /* A flex item will not shrink below its content width without this,
       so the title would refuse to give up room and force the filename
       to wrap instead. With it, the title wraps and the filename does
       not -- which is the right way round. */
    min-width: 0;
}

/* Sits at the right edge of the row, with the heading and the dates below
   it set against that edge so they all finish on the same line. */
.kb-article-meta {
    flex: 0 0 6rem;
    text-align: right;
    font-size: .82rem;
    color: var(--color-muted, #6e6e73);
    font-variant-numeric: tabular-nums;
}

/* Starts exactly under the title: the row's own padding, then the caret,
   the gap, the id column, the gap again, and the title's step. */
.kb-article-body {
    padding: .9rem 1.1rem 1.1rem
             calc(1.1rem + .42rem + .15rem + 1rem + 3.4rem + 1rem + var(--kb-title-step));
    border-top: 1px solid #ece7f3;
}

.kb-article-body p { margin: 0 0 .9rem; max-width: 64ch; }
.kb-article-body p:last-child { margin-bottom: 0; }

.kb-empty {
    background: #fff;
    border: 1px solid var(--color-border, #d2d2d7);
    border-radius: 10px;
    padding: 1.25rem 1.4rem;
    max-width: 44rem;
}

.kb-foot { margin-top: 2.5rem; font-size: .9rem; }

@media (max-width: 34rem) {
    .kb-article > summary { flex-wrap: wrap; }
    .kb-article-meta { flex-basis: 100%; }
    .kb-article-body { padding-left: 1.1rem; }
    .kb-colheads { display: none; }
    .kb-article-id { flex-basis: auto; }
}

/* The live support page's banner, so somebody arriving from a citation
   sees the same masthead they would on sonnettech.com. Referenced from
   their server rather than copied here -- one image, one owner. */

/* Product Categories.
 *
 * Real text buttons, outlined in the brand purple. They used to be seven
 * GIFs hotlinked from the live site, carrying their own baked-in
 * styling; see the note in kb.php for why those went.
 *
 * Because they are text now they size to their own labels, so all seven
 * sit on one row at desktop width instead of the fixed-216px images'
 * 4 + 3. No width is set here on purpose -- a grid of equal columns
 * would reintroduce exactly the padding the images had, where
 * "Accessories" got the same box as "Media Card Readers".
 */
.kb-cats-head {
    text-align: center;
    font-size: 1.35rem;
    font-weight: 600;
    margin: 0 0 .9rem;
}

/* A GRID, SO THE ROWS NEVER COME OUT UNEVEN.
 *
 * This was flex-wrap. With eight categories it broke 7 + 1 on a normal
 * desktop -- the eight buttons want 1,182px and the box is 1,100px, so
 * "Adapters" fell to a row of its own and sat there alone. The CEO
 * called it awkward on 27 October and he was right: it reads as a
 * mistake rather than a layout.
 *
 * Shrinking the padding until all eight fit was the obvious fix and it
 * is the wrong one -- it holds at 1440px and breaks again at 1366, or
 * the first time TSKB gains a ninth category. The orphan is a property
 * of wrapping content-width items, not of these particular eight.
 *
 * A grid with a fixed column count cannot orphan: four columns give
 * 4 + 4, two give 2 + 2 + 2 + 2, one gives a stack. Any number of
 * categories fills left to right and the last row is short rather than
 * solitary -- and with four columns it takes NINE before a row is less
 * than half full.
 *
 * justify-items: stretch so the cells are equal width. Content-width
 * pills of eight different widths looked arbitrary once they were in
 * rows; equal cells read as a deliberate set. */
.kb-cats {
    display: grid;
    grid-template-columns: 1fr;
    gap: .6rem;
    padding: 1.1rem;
    border: 1px solid var(--color-border, #d2d2d7);
    border-radius: 10px;
    background: #fff;
    margin: 0 0 2.25rem;
}

/* Two from about 34rem, four from about 62rem. Both divide eight
   evenly, and three was deliberately skipped: it would give 3 + 3 + 2,
   which has the same lopsided last row this is here to avoid. */
@media (min-width: 34rem) {
    .kb-cats { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 62rem) {
    .kb-cats { grid-template-columns: repeat(4, 1fr); }
}

/* skin-v12.css carries `body.v12 a:not([class*="btn"])`, which is
   (0,2,1) and outranks a plain `.kb-cats a` -- left at one class these
   came out purple text on a purple fill. Matched in weight rather than
   forced with !important, which is what the skin itself does for its
   own button-shaped anchors. */
/* Outlined rather than filled: these are navigation, not the action of
   the page. Seven filled purple blocks carried more weight than the
   product library they sit above, and read as seven primary buttons
   competing with one another. The border does the work; the fill stays
   white. All three were tried in place before this one was kept.

   The border is the accent lightened, not the accent itself -- at full
   strength seven of them ruled the page about as hard as the fill did.

   #6b539c on white measures 6.4:1, comfortably past AA. */
body.v12 .kb-cats a {
    /* block, not inline-block: the anchor now fills its grid cell, so
       the whole cell is the target rather than just the words. */
    display: block;
    text-align: center;
    padding: .62rem 1.1rem;
    border: 1px solid #cfc4e2;
    border-radius: 6px;
    background: #fff;
    color: var(--color-accent, #6b539c);
    font-size: .875rem;
    font-weight: 600;
    line-height: 1.3;
    text-decoration: none;
    transition: border-color .15s ease, background .15s ease;
}

body.v12 .kb-cats a:hover {
    border-color: var(--color-accent, #6b539c);
    background: rgba(107, 83, 156, .05);
}

/* Keyboard users get the same ring the rest of the page uses, and it
   needs an offset to be visible against a filled button. */
body.v12 .kb-cats a:focus-visible {
    outline: 2px solid var(--color-accent, #6b539c);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    body.v12 .kb-cats a { transition: none; }
}

/* Narrow screens: the row becomes a column of full-width buttons rather
   than a ragged two-and-a-bit, which is easier to hit on a phone. */
@media (max-width: 34rem) {
    .kb-cats { flex-direction: column; }
    body.v12 .kb-cats a { text-align: center; }
}

/* The hardware sits beside the heading, and drops above it when there is
   no room for both. */
.kb-head {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    margin: 0 0 .35rem;
}

/* The window onto the hardware. Its shape is the shape of the measured
   slice, and the image inside is blown up so that slice fills it -- so a
   190px-wide dock in an 897px banner renders at 190px of detail rather
   than at 40. */
.kb-shot {
    flex: 0 0 auto;
    display: block;
    width: clamp(150px, 24%, 230px);
    aspect-ratio: var(--crop) / var(--tall);
    overflow: hidden;
}

.kb-product-img {
    display: block;
    width: calc(100% * var(--nat) / var(--crop));
    max-width: none;
    height: auto;
}

@media (max-width: 34rem) {
    .kb-head { flex-direction: column; align-items: flex-start; gap: .75rem; }

}

/* ---------------------------------------------------------------------
 * The live page draws a border round the instruction bar and the four
 * type groups together, so the article list reads as one panel rather
 * than as four loose lists. It used to open with a grey instruction bar
 * reading "click the arrow to expand or collapse"; the arrow is its own
 * affordance and the sentence has gone, so the legend is now the first
 * thing inside and carries the panel's top padding.
 * ------------------------------------------------------------------ */

.kb-groups {
    border: 1px solid #c9c6d4;
    border-radius: 4px;
    overflow: hidden;
    margin: 0 0 2rem;
}

.kb-groups .kb-group {
    padding-left: 1rem;
    padding-right: 1rem;
}

/* A row is inset twice -- once by its group, once by the row's own padding
   -- so the heads need both to sit over their columns. They were short by
   the second one and printed 18px to the left of everything they label. */
.kb-groups .kb-colheads {
    /* Group padding, plus the row's own padding, plus the 1px border each
       row is drawn with. The heading row has no border of its own, so it
       has to add one on BOTH sides to sit over its columns -- the ID is
       placed from the left edge, the post date from the right. */
    padding-left: calc(2.1rem + 1px);
    padding-right: calc(2.1rem + 1px);
}

.kb-groups .kb-group:last-child { padding-bottom: .75rem; }


@media (max-width: 34rem) {
    .kb-head { flex-direction: column; align-items: flex-start; gap: .9rem; }
    .kb-shot { width: 170px; }
}

/* ---------------------------------------------------------------------
 * Groups open and shut.
 *
 * All four start closed: somebody arriving from a citation or a search
 * wants their own answer, not sixty titles. The column heads only mean
 * anything once there are rows under them, so they wait for the first
 * group to open -- :has() rather than a script, so it also works while
 * the page is still loading.
 * ------------------------------------------------------------------ */

.kb-colheads { display: none; }

.kb-groups:has(details.kb-group[open]) .kb-colheads { display: flex; }

details.kb-group > summary {
    cursor: pointer;
    list-style: none;
    display: flex;
    align-items: center;

    /* A flex row drops the whitespace between the name and its count, so
       the gap has to stand in for the space that used to be there. */
    gap: .4rem;
}

details.kb-group > summary::-webkit-details-marker { display: none; }

/* A solid triangle rather than the hairline caret the articles use: this
   is the control the instruction bar points at, so it reads as a bigger
   thing than the row-level ones. Drawn with borders, so it costs no
   request and turns with the state. */
details.kb-group > summary::before {
    content: "";
    flex: 0 0 auto;
    width: 0;
    height: 0;
    margin-right: .55rem;
    border-left: 11px solid #6b6b70;
    border-top: 7px solid transparent;
    border-bottom: 7px solid transparent;
    transform-origin: 30% 50%;
    transition: transform .15s ease;
}

details.kb-group[open] > summary::before { transform: rotate(90deg); }

details.kb-group > summary:hover::before { border-left-color: var(--color-accent, #6b539c); }

details.kb-group > summary:focus-visible {
    outline: 2px solid var(--color-accent, #6b539c);
    outline-offset: 3px;
    border-radius: 3px;
}

/* An empty group is a label, not a control: no caret, no pointer, and it
   keeps the caret's width so its name lines up with the ones that have
   articles underneath. */
.kb-group-none {
    margin: 0 0 .5rem;
    padding-left: calc(11px + .55rem);
    color: var(--color-muted, #6e6e73);
    font-size: .9rem;
}

/* The key for the article-number colours. Sits above the list, small and
   out of the way -- it explains a secondary signal and should not read as
   a heading. */
.kb-sources {
    margin: 0;
    padding: .9rem 1rem .55rem;
    font-size: .82rem;
    color: var(--color-muted, #6e6e73);
}

.kb-sources span {
    margin-left: .55rem;
}

/* ---------------------------------------------------------------------
 * The download link, in the article row.
 *
 * Positioned where the live site puts its "Download Link" column:
 * between the title and the post date. A reader scanning a list of
 * drivers can see which ones have a file without opening any of them.
 *
 * The title column already flexes to fill, so this needs only a fixed
 * lane of its own and the date keeps its place at the right edge.
 * ------------------------------------------------------------------ */

.kb-article-dl {
    /* Sized by its content, not pinned to a width.
     *
     * It was 11rem, which is narrower than plenty of these filenames --
     * "20190908_Aquantia_AQtion_Win_ver_2.1.18.0.zip" wrapped, dropping
     * ".0.zip" onto a second line and reading like a broken layout. The
     * row has room to give: the title column flexes and will yield the
     * space, which is why it gains min-width below. */
    flex: 0 0 auto;
    text-align: right;
    font-size: .8rem;

    /* The row aligns its cells on the baseline, which would hang the
       filename below the date. This cell stacks internally instead. */
    align-self: flex-start;
}

/* In the heading row it is a label, not a link. */
.kb-colheads .kb-article-dl {
    font-weight: 600;
    color: var(--color-muted, #6e6e73);
}

/* A link, not a filled button.
 *
 * One filled button is a clear call to action; four stacked down a
 * driver list is a column of purple arguing with itself and with the
 * article titles beside it -- the same reason the category rail above
 * is outlined rather than filled.
 *
 * Bold and in the accent colour so it still reads as the thing to
 * click, with the underline held back for hover so a list of them stays
 * calm. The arrow does the work a button shape used to. */
body.v12 .kb-download-btn {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    color: var(--color-accent, #6b539c);
    font-size: .82rem;
    font-weight: 700;
    white-space: nowrap;
    text-decoration: none;
}

body.v12 .kb-download-btn:hover,
body.v12 .kb-download-btn:focus-visible {
    text-decoration: underline;
}

/* Sized in em so it tracks the link's font size, and nudged down a
   fraction because an icon aligned on the text baseline sits high. */
.kb-dl-icon {
    width: 1em;
    height: 1em;
    fill: currentColor;
    flex: 0 0 auto;
    transform: translateY(.1em);
}

body.v12 .kb-download-btn:focus-visible {
    outline: 2px solid var(--color-accent, #6b539c);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    body.v12 .kb-download-btn { transition: none; }
}

/* Name and size, directly under the button.
 *
 * Deliberately quiet: .68rem against the button's .78rem, and muted, so
 * the eye reaches the action first and the detail second. Tabular
 * numerals keep the sizes aligned down a column of drivers. */
.kb-dl-meta {
    display: block;
    margin-top: .3rem;
    line-height: 1.35;
    font-size: .68rem;
    color: var(--color-muted, #6e6e73);
}

.kb-dl-name {
    display: block;

    /* One line. A filename broken across two lines is not a filename
       any more -- it reads as a rendering fault, and the part after the
       break looks like a separate word. The title wraps instead; it is
       prose and survives wrapping, which a filename does not. */
    white-space: nowrap;
}

.kb-dl-size {
    display: block;
    font-variant-numeric: tabular-nums;
}

/* Narrow screens: the lane would squeeze the title, so the link drops
   to its own line under the row. */
@media (max-width: 40rem) {
    .kb-article-dl { width: auto; }
    .kb-colheads .kb-article-dl { display: none; }
}

/* The article-type glyph on a group heading.
 *
 * Muted rather than accent-coloured: it marks the section, it is not
 * something to click. The caret beside it is the control. */
.kb-type-icon {
    width: 1.05em;
    height: 1.05em;
    fill: none;
    color: var(--color-muted, #6e6e73);
    flex: 0 0 auto;
    margin-right: .15rem;
    transform: translateY(.08em);
}

/* Open sections take the text colour, so the one being read looks
   active and the closed ones recede. */
details.kb-group[open] > .kb-group-head .kb-type-icon {
    color: var(--color-accent, #6b539c);
}

/* ---------------------------------------------------------------------
   Category grid -- the products under one of the seven top-level
   categories. The layer that used to be missing, which is why those
   buttons pointed at sonnettech.com.
   --------------------------------------------------------------------- */

.kb-cat-head {
    margin: 8px 0 20px;
    font-size: 21px;
    font-weight: 600;
    letter-spacing: -0.01em;
    color: var(--color-heading);
}

.kb-tiles {
    display: grid;
    /* auto-fill rather than a fixed count: Thunderbolt has 30 products and
       eGPU Solutions has 2, and a fixed four-up leaves the short category
       looking broken. */
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 20px;
    list-style: none;
    margin: 0 0 56px;
    padding: 0;
}

.kb-tile { margin: 0; }

/* The whole tile is the target, not just the words. */
body.v12 .kb-tile a {
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-card);
    padding: 16px;
    text-decoration: none;
    color: var(--color-heading);
    background: var(--color-bg);
}
body.v12 .kb-tile a:hover {
    border-color: var(--color-accent);
    color: var(--color-accent);
}

/* A fixed box so tiles line up whatever shape the photo is -- these are
   product shots with wildly different proportions, and left to themselves
   the names end up at seven different heights across a row. */
.kb-tile-img {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 96px;
    margin-bottom: 12px;
}
.kb-tile-img img {
    max-width: 100%;
    max-height: 96px;
    width: auto;
    height: auto;
}

.kb-tile-name {
    font-size: 14px;
    line-height: 1.4;
    font-weight: 600;
}

@media (max-width: 560px) {
    .kb-tiles { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 14px; }
    .kb-tile-img { height: 72px; }
    .kb-tile-img img { max-height: 72px; }
}

/* ---------------------------------------------------------------------
   The knowledge base front page, with no category chosen.

   Deliberately NOT .kb-empty. That class is the error treatment -- a
   bordered white box -- and it is correct for "no product uses this
   category". Here nothing has failed: somebody has just arrived. A box
   that looks like a warning is the wrong first impression, and it was
   the thing people noticed on this page.
   --------------------------------------------------------------------- */

.kb-start {
    background: var(--color-bg-alt, #f6f4f9);
    border-radius: var(--radius-card, 12px);
    padding: 26px 30px;
    max-width: 46rem;
}

.kb-start-lead {
    margin: 0;
    font-size: 16px;
    line-height: 1.55;
    color: var(--color-text, #333);
}

/* Points at the buttons it is talking about. Decoration, so it is hidden
   from screen readers -- "up arrow Start with a category above" reads as
   a stutter. */
.kb-start-arrow {
    color: var(--color-accent, #6b539c);
    font-weight: 700;
    margin-right: 6px;
}

.kb-start-lead strong { color: var(--color-heading, #1d1d1f); }

/* The second route, quieter than the first: most people arriving here do
   know their product, and this is for the ones who do not. */
.kb-start-alt {
    margin: 14px 0 0;
    padding-top: 14px;
    border-top: 1px solid rgba(0, 0, 0, .08);
    font-size: 15px;
    line-height: 1.55;
    color: var(--color-muted, #6e6e73);
}

@media (max-width: 34rem) {
    .kb-start { padding: 20px 18px; }
}

/* Legacy anchor target for links printed before the move -- product
   barcodes and QR codes resolve to #b<article id>, which the server never
   sees. Offset so the article is not hidden under the sticky header when
   an old scan jumps to it. */
.kb-legacy-anchor {
    display: block;
    height: 0;
    scroll-margin-top: 6rem;
}

/* In BOTH systems. The two source colours mixed, which is what olive is.
   Chosen by Julia, 22 October, over a more separable hue: these colours
   are an internal aid rather than customer-facing information, and the
   relationship between the three reads better than contrast between them
   would. Contrast on white is 5.76:1, so it passes on its own merits
   even though it sits between its two parents. */
.kb-article-id--both {
    color: #546E30;
    font-weight: 600;
}
