/* ---- palette ----
 *
 * One variable per shade the dark theme already used, so dark is unchanged to
 * the byte and light is the same ramp inverted. Naming is by role and depth
 * rather than by colour: --surface-4 is "a bit above the card", not "#282828".
 *
 * Three states, in this order, and the order is what makes them work:
 *   :root                    dark, the default
 *   @media light + :not(.dark)   follow the system, unless the reader pinned dark
 *   html.light               the reader pinned light, beats the media query
 *
 * style.css is on the server and cannot be edited, so the handful of colours it
 * sets are overridden at the bottom of this block rather than at their source.
 */
:root {
    --bg: #121212;
    --bg-alt: #181818;
    --surface: #1e1e1e;
    --line-faint: #1c1c1c;
    --surface-2: #232323;
    --surface-3: #262626;
    --surface-4: #282828;
    --surface-5: #2a2a2a;
    --border: #2b2b2b;
    --line: #2c2c2c;
    --surface-6: #333;
    --text-6: #3d3d3d;
    --scroll: #4a4a4a;
    --text-5: #4e4e4e;
    --text-4: #5f5f5f;
    --text-3: #6a6a6a;
    --text-3b: #858585;
    --text-2b: #8a8a8a;
    --text-2: #9a9a9a;
    --text-1c: #b8b8b8;
    --text-1b: #d0d0d0;
    --text-1d: #d2d2d2;
    --text: #E0E0E0;
    --text-0b: #e6e6e6;
    --text-0: #f0f0f0;
    --text-max: #fff;
    --accent: #3f9fff;
    --on-accent: #00121f;
    --shadow-text: rgba(0, 0, 0, .75);
    --scrim: rgba(0, 0, 0, .55);
    --shadow: rgba(0, 0, 0, .45);
    --flag-op: .45;
}

@media (prefers-color-scheme: light) {
    html:not(.dark) {
        --bg: #ffffff;
        --bg-alt: #f3f4f6;
        --surface: #f4f5f7;
        --line-faint: #ebedf0;
        --surface-2: #eceef1;
        --surface-3: #e8eaee;
        --surface-4: #e2e5e9;
        --surface-5: #dde0e5;
        --border: #d5d9df;
        --line: #e0e3e7;
        --surface-6: #ced3da;
        --text-6: #b4bac2;
        --scroll: #c0c6cd;
        --text-5: #a3aab3;
        --text-4: #949ba5;
        --text-3: #868e99;
        --text-3b: #78808b;
        --text-2b: #727a85;
        --text-2: #646c77;
        --text-1c: #4a515b;
        --text-1b: #373e48;
        --text-1d: #353c46;
        --text: #22282f;
        --text-0b: #1b212a;
        --text-0: #141a22;
        --text-max: #0a0f16;
        --accent: #0b63d6;
        --on-accent: #ffffff;
        --shadow-text: rgba(255, 255, 255, .85);
        --scrim: rgba(15, 20, 28, .38);
        --shadow: rgba(15, 20, 28, .16);
        --flag-op: .72;
    }
}

html.light {
    --bg: #ffffff;
    --bg-alt: #f3f4f6;
    --surface: #f4f5f7;
    --line-faint: #ebedf0;
    --surface-2: #eceef1;
    --surface-3: #e8eaee;
    --surface-4: #e2e5e9;
    --surface-5: #dde0e5;
    --border: #d5d9df;
    --line: #e0e3e7;
    --surface-6: #ced3da;
    --text-6: #b4bac2;
    --scroll: #c0c6cd;
    --text-5: #a3aab3;
    --text-4: #949ba5;
    --text-3: #868e99;
    --text-3b: #78808b;
    --text-2b: #727a85;
    --text-2: #646c77;
    --text-1c: #4a515b;
    --text-1b: #373e48;
    --text-1d: #353c46;
    --text: #22282f;
    --text-0b: #1b212a;
    --text-0: #141a22;
    --text-max: #0a0f16;
    --accent: #0b63d6;
    --on-accent: #ffffff;
    --shadow-text: rgba(255, 255, 255, .85);
    --scrim: rgba(15, 20, 28, .38);
    --shadow: rgba(15, 20, 28, .16);
    --flag-op: .72;
}

/* style.css hardcodes these and loads first. */
html, body {
    background-color: var(--bg);
    color: var(--text);
}

a {
    color: var(--accent);
}

hr {
    background-color: var(--line);
}

.left-col {
    background-color: var(--surface);
}

::-webkit-scrollbar-track {
    background: var(--surface-2);
}

::-webkit-scrollbar-thumb {
    background: var(--scroll);
}

/* Navigation rail, year cards, and the problem list.
 *
 * The only stylesheet we control: style.css lives on the server, predates this
 * repo and cannot be edited, so anything of its that has to change is
 * overridden from here. That is why some selectors below are heavier than they
 * would otherwise need to be, and why nav.css must stay after style.css in the
 * <head>.
 *
 * There is no top bar: the rail is the only chrome. It is pinned open above
 * 1200px and becomes a drawer over a dimmed page below that.
 *
 * Driven by a hidden checkbox, so no JavaScript is involved. The collapse
 * control lives inside the rail; a floating control appears over the content
 * when the rail is away. Both are <label for="nav">, as is the scrim, so
 * clicking outside the drawer closes it.
 *
 * The rail starts closed at every width, so the checkbox means one thing
 * throughout: checked is open. Above 1200px an open rail is pinned beside the
 * content; below, it floats over a dimmed page.
 *
 * No version to bump: build.py content-hashes this file into the URL it links,
 * so editing it busts the cache by itself. Only style.css, which lives on the
 * server, still needs STYLE_CSS_VERSION moved by hand.
 */

body {
    margin: 0;
}

.navtoggle {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

/* ---- controls ---- */

.iconbtn {
    cursor: pointer;
    width: 38px;
    height: 38px;
    border-radius: 9px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-2);
    font-size: 17px;
    transition: background .15s, color .15s;
    user-select: none;
    flex: none;
}

.iconbtn:hover {
    background: var(--surface-3);
    color: var(--text);
}

/* Panel-left glyph. Font Awesome 5 Free has no sidebar-toggle icon, so this is
   drawn here: a rounded rectangle with a divider. Masked rather than inlined so
   it picks up currentColor and follows the hover state. */
.iconbtn::before {
    content: "";
    width: 20px;
    height: 20px;
    background: currentColor;
    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='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2.5'/%3E%3Cline x1='9.5' y1='4' x2='9.5' y2='20'/%3E%3C/svg%3E") center / 20px no-repeat;
    -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='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='16' rx='2.5'/%3E%3Cline x1='9.5' y1='4' x2='9.5' y2='20'/%3E%3C/svg%3E") center / 20px no-repeat;
}

/* Floating control over the content, shown whenever the rail is not there. */
.opener {
    position: fixed;
    top: 12px;
    left: 12px;
    z-index: 130;
    background: var(--surface);
    border: 1px solid var(--line);
    display: flex;
}

/* ---- the rail ---- */

aside.rail {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    width: 260px;
    z-index: 110;
    background: var(--bg);
    border-right: 1px solid var(--line);
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
    transform: translateX(-100%);
    transition: transform .22s ease;
}

.navtoggle:checked ~ .layout aside.rail {
    transform: none;
}

.railtop {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    /* Sits inside .railinner's 8px gutter, so 10px here puts the wordmark on
       the same 18px line as the item text below. */
    padding: 6px 0 4px 10px;
    flex: none;
}

/* Set all caps. The lowercase glyphs in this face are small capitals at 87% of
   cap height, so mixed case leaves a 19% step between the leading O and the
   letter beside it. Unaccented, unlike the main site's OLÍMPICOS -- these pages
   are in English and it matches the domain. */
.brandmark {
    color: var(--text);
    display: block;
    height: 22px;
    width: auto;
    flex: none;
}



/* Brand row, search and the switcher are all inside the scrolling area and all
   stick to the top of it as ONE block. Splitting them -- brand pinned outside,
   the rest sticky inside -- is what made them shift against each other while
   scrolling. One sticky element also avoids hard-coding each one's height as
   the next one's offset. */
.railhead {
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--bg);
}

/* Fixed width so the links do not re-wrap while the width animates. */
.railinner {
    width: 100%;
    flex: 1;
    min-height: 0;
    overflow-y: auto;
    padding: 0 8px;
}

.scrim {
    position: fixed;
    inset: 0;
    z-index: 100;
    background: var(--scrim);
    opacity: 0;
    pointer-events: none;
    transition: opacity .22s ease;
    cursor: pointer;
}

.navtoggle:checked ~ .scrim {
    opacity: 1;
    pointer-events: auto;
}

/* ---- rail contents ---- */

.subjbar {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 6px;
    padding: 2px 8px 16px;
}

/* Left aligned rather than centred: with the icons all one width the labels
   start on a common vertical line, so the column reads as a list instead of
   eight separately centred blobs. */
.subjbar .subj {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: 8px;
    white-space: nowrap;
    font-size: 14px;
    padding: 8px 10px;
    border-radius: 8px;
    background: var(--surface);
    color: var(--text-2);
}

/* Solid weight, which the page already pays for: .railsearch::before pulls in
   fa-solid-900 for the magnifier, so these cost no extra request. Set on the
   pill rather than a wrapper element so the markup stays a bare span. */
.subjbar .subj::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 12px;
    width: 14px;
    text-align: center;
    flex: none;
}

.subjbar .subj[data-subj="Physics"]::before { content: "\f5d2"; }
.subjbar .subj[data-subj="Mathematics"]::before { content: "\f698"; }
.subjbar .subj[data-subj="Informatics"]::before { content: "\f121"; }
.subjbar .subj[data-subj="Chemistry"]::before { content: "\f0c3"; }
.subjbar .subj[data-subj="Biology"]::before { content: "\f471"; }
.subjbar .subj[data-subj="Astronomy"]::before { content: "\f753"; }
.subjbar .subj[data-subj="Science"]::before { content: "\f610"; }
.subjbar .subj[data-subj="Linguistics"]::before { content: "\f1ab"; }
.subjbar .subj[data-subj="Economics"]::before { content: "\f201"; }
.subjbar .subj[data-subj="Geography"]::before { content: "\f57d"; }

.subjbar .subj:hover {
    background: var(--surface-3);
}

.subjbar .subj.on {
    background: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

.subjbar .subj.soon {
    color: var(--text-5);
    cursor: pointer;
}

.subjbar .subj.soon:hover {
    color: var(--text-2);
}

/* The 16px below has to match .subjbar's padding-bottom: the field should sit
   the same distance from the buttons above it as from the first category
   heading below. .cath has line-height 1 and no padding-top, so its box top is
   the cap line and the two numbers can be compared directly. */
.railsearch {
    --field: 36px;
    position: relative;
    padding: 0 8px 16px;
}

/* The header used to add its own 10px on top of the search block's, so the gap
   below the field was double the one above. Spacing between groups now lives on
   the group.

   It has to hang off the sibling combinator rather than :first-of-type. Every
   block opens with the .subjname div, so the first .catgrp is never the first
   div among its siblings and :first-of-type -- which keys on element type, not
   class -- matched nothing at all: the first group kept its 10px. */
.catgrp + .catgrp {
    padding-top: 13px;
}

.railsearch::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    content: "\f002";
    position: absolute;
    left: 21px;
    /* Centred on the field, not on .railsearch: the block carries the 16px gap
       below it as padding, so a 50% offset lands well under the input. Matching
       the field's own height and centring inside that keeps the glyph put no
       matter what the spacing below becomes. */
    top: 0;
    height: var(--field);
    display: flex;
    align-items: center;
    font-size: 13px;
    color: var(--text-3);
    pointer-events: none;
}

/* Native search fields bring their own inner shadow and cancel button, which
   look wrong on a dark rail, so the appearance is reset and rebuilt. */
.railsearch input {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    font-family: inherit;
    font-size: 15px;
    line-height: 1.2;
    box-sizing: border-box;
    height: var(--field);
    padding: 0 12px 0 32px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text);
    border: 1px solid transparent;
    transition: background .15s, border-color .15s;
}

.railsearch input::placeholder {
    color: var(--text-3);
}

.railsearch input:hover {
    background: var(--surface-4);
}

.railsearch input:focus {
    outline: none;
    background: var(--bg);
    border-color: var(--accent);
}

.railsearch input::-webkit-search-cancel-button {
    -webkit-appearance: none;
    height: 13px;
    width: 13px;
    background: var(--text-3);
    cursor: pointer;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 4l8 8M12 4l-8 8' stroke='black' stroke-width='2'/%3E%3C/svg%3E") center/11px no-repeat;
}

.catgrp .cath {
    padding: 0 10px 4px;
    font-size: 11.5px;
    line-height: 1;
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--text-3);
}

/* Inset rounded rows rather than full-bleed with a left rule. */
aside.rail .exam {
    display: block;
    margin: 1px 0;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: 15.5px;
    color: var(--text);
}

aside.rail a.exam:hover {
    background: var(--surface-3);
    color: var(--text);
}

aside.rail a.exam.on {
    background: var(--surface-5);
    color: var(--text-max);
    font-weight: 600;
}

aside.rail .exam.soon {
    color: var(--text-5);
    cursor: default;
}

aside.rail .exam.soon::after {
    content: " soon";
    font-size: 12px;
    color: var(--text-6);
}

/* Every subject is in the DOM but hidden, so search can reach across them
   without an index. The subject name is redundant when a single block is
   showing, so it only appears while searching. */
.subjname {
    display: none;
    padding: 10px 10px 2px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--accent);
    opacity: .8;
}

.raillist.searching .subjname {
    display: block;
}

.norail {
    padding: 10px 10px 12px;
    color: var(--text-3);
    font-size: 14px;
}

/* ---- page header ---- */
header.col-12 {
    margin: 30px 0 26px;
}

/* Bootstrap's hr margins are inherited; set them so the rhythm between year
   blocks is deliberate rather than whatever 1rem happens to give. */
main.col-12 hr {
    margin: 20px 0;
}

/* The 6px bottom margin here was holding the tagline off the title; with that
   gone it just made the block sit 8px lower than it looked. */
header.col-12 h1 {
    font-size: 34px;
    font-weight: 600;
    margin: 0;
}

/* A sentence or two saying what the competition is. Capped at a readable
   measure rather than the full container: centred text running the width of a
   1400px page is a paragraph nobody finishes. Quiet enough that the year cards
   below stay the thing the eye lands on -- this is context, not content. */
header.col-12 .about {
    max-width: 60ch;
    margin: 12px auto 0;
    font-size: 15px;
    line-height: 1.55;
    color: var(--text-2);
}

header.col-12 .about a {
    color: var(--text-1c);
    text-decoration: underline;
    text-underline-offset: 2px;
}

header.col-12 .about a:hover {
    color: var(--text-max);
}

/* Two lines: where the files came from, then who made it. The source stays on
   its own line because a few olympiads carry a sentence of explanation there. */
footer.col-12 {
    margin: 26px 0 30px;
}

footer .source,
footer .credits {
    font-size: 15px;
    line-height: 1.6;
    color: var(--text-2b);
}

footer .credits {
    margin-top: 2px;
}

footer .credits span {
    color: var(--text-5);
    padding: 0 3px;
}

/* ---- narrow: drawer ---- */
@media (max-width: 1199px) {
    .layout {
        display: block;
    }

    /* The floating control is the only way in, so it stays until the drawer
       is open. */
    .navtoggle:checked ~ .opener {
        display: none;
    }

    .content {
        padding-top: 10px;
    }
}

/* ---- wide: pinned ----
   A pinned rail cannot collapse with display:none, which is not animatable, so
   the width is animated instead and the content reflows alongside it. */
@media (min-width: 1200px) {
    .layout {
        display: flex;
        align-items: flex-start;
    }

    .content {
        flex: 1;
        min-width: 0;
    }

    /* Closed by default, same as narrow. The rail is pushed to zero width
       rather than display:none, which is not animatable. */
    aside.rail {
        position: sticky;
        top: 0;
        height: 100vh;
        width: 0;
        opacity: 0;
        border-right-color: transparent;
        transform: none;
        will-change: width;
        transition: width .26s cubic-bezier(.4, 0, .2, 1),
                    opacity .18s ease,
                    border-color .26s ease;
    }

    .navtoggle:checked ~ .layout aside.rail,
    html.nav-open .layout aside.rail {
        width: 260px;
        opacity: 1;
        border-right-color: var(--line);
    }

    .railinner {
        width: 260px;
    }

    .scrim {
        display: none;
    }

    .navtoggle:checked ~ .opener,
    html.nav-open .opener {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    aside.rail,
    .scrim {
        transition: none !important;
    }
}

/* ---- search filters ----
   A filter button beside the field, a popover to set the filters in, and the
   applied ones as removable pills under the field. The pills are the state:
   the search starts scoped to this page's olympiad and says so with a pill
   rather than silently, and removing it widens the search to the subject.

   The popover is not in here. .railinner scrolls, and a box with one axis set
   to auto computes the other to auto as well, so anything absolutely
   positioned inside it is either clipped or grows a horizontal scrollbar. The
   script puts the popover on <body> and positions it against the button. */
.railsearch {
    display: flex;
    align-items: flex-start;
    gap: 6px;
}

.railsearch input {
    flex: 1 1 auto;
    min-width: 0;
}

/* Same field, square: same height, same surface, same focus treatment. The
   glyph is the one Font Awesome already loads for the magnifier, so it costs
   no request. */
.fbtn {
    flex: none;
    width: var(--field);
    height: var(--field);
    padding: 0;
    border: 1px solid transparent;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-3);
    cursor: pointer;
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    line-height: 1;
    transition: background .15s, border-color .15s, color .15s;
}

.fbtn::before {
    content: "\f0b0";
}

.fbtn:hover {
    background: var(--surface-4);
    color: var(--text);
}

.fbtn.on {
    background: var(--bg);
    border-color: var(--accent);
    color: var(--text);
}

.fbtn:focus-visible,
.railfilters .fpill:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* The pills sit in the sticky header under the field, and the block is not a
   sibling of .raillist any more, so `.raillist.searching ~ ...` cannot reveal
   it; the script sets .ready on the same condition. The negative pull eats the
   field block's bottom padding, which is spacing before the list rather than
   before this. */
.railfilters {
    display: none;
    margin-top: -10px;
    padding: 0 8px 10px;
}

.railfilters.ready {
    display: flex;
    flex-wrap: wrap;
    gap: 4px;
}

.railfilters .fpill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    max-width: 100%;
    padding: 2px 7px 2px 8px;
    border: 1px solid var(--border);
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--text-1b);
    font: inherit;
    font-size: 11.5px;
    line-height: 1.6;
    cursor: pointer;
    transition: background .15s, border-color .15s, color .15s;
}

.railfilters .fpill span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The × is the whole point of the pill -- it is a control, not a label -- so
   it brightens with the pill rather than sitting quietly beside it. */
.railfilters .fpill::after {
    content: "\00d7";
    font-size: 13px;
    line-height: 1;
    color: var(--text-4);
}

.railfilters .fpill:hover {
    border-color: var(--text-4);
    color: var(--text-max);
}

.railfilters .fpill:hover::after {
    color: var(--text-1b);
}

/* The scope pill nobody asked for. It is applied before the reader has
   touched anything, so it is drawn as a default rather than as a choice: no
   fill, dashed edge. It solidifies the moment the olympiad is set by hand. */
.railfilters .fpill.auto {
    background: none;
    border-style: dashed;
    color: var(--text-3);
}

/* ---- the filter popover ----
   On <body>, fixed, positioned against the button by the script. Above the
   rail's own z-index of 110. */
.fpop {
    position: fixed;
    z-index: 130;
    width: 268px;
    max-width: calc(100vw - 24px);
    box-sizing: border-box;
    padding: 12px;
    display: grid;
    gap: 10px;
    background: var(--bg-alt);
    border: 1px solid var(--border);
    border-radius: 10px;
    box-shadow: 0 10px 30px var(--shadow);
}

.fpop[hidden] {
    display: none;
}

/* The drawer form: in the rail, after the header, pushing the results down
   instead of sitting on top of them. Static, so nothing here is at risk of
   being clipped by .railinner's scrolling. */
.fpop.inline {
    position: static;
    width: auto;
    max-width: none;
    margin: 0 8px 12px;
    box-shadow: none;
}

.fpop label {
    display: grid;
    gap: 4px;
    min-width: 0;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .06em;
    color: var(--text-3);
}

.fpop .frow {
    display: flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
}

/* Native selects and number inputs bring a platform arrow and a spinner that
   look wrong here, the same objection .railsearch input already answers for
   the search field. Reset and rebuilt to match it: one height, one radius,
   one focus treatment across all three. */
.fpop select,
.fpop input {
    -webkit-appearance: none;
    -moz-appearance: textfield;
    appearance: none;
    box-sizing: border-box;
    width: 100%;
    min-width: 0;
    height: 32px;
    padding: 0 10px;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--text);
    font-family: inherit;
    font-size: 13.5px;
    line-height: 1.2;
    letter-spacing: normal;
    text-transform: none;
    transition: background .15s, border-color .15s;
}

.fpop select {
    padding-right: 26px;
}

.fpop select:hover,
.fpop input:hover {
    background: var(--surface-4);
}

.fpop select:focus,
.fpop input:focus {
    outline: none;
    background: var(--bg);
    border-color: var(--accent);
}

.fpop input::-webkit-outer-spin-button,
.fpop input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* The chevron the reset removed, drawn back on the wrapper. Masked rather than
   coloured into the SVG so it follows the theme, the technique the search
   field's cancel button already uses. */
.fsel {
    position: relative;
    display: block;
    min-width: 0;
}

.fsel::after {
    content: "";
    position: absolute;
    right: 10px;
    top: 50%;
    width: 9px;
    height: 6px;
    transform: translateY(-50%);
    background: var(--text-3);
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E") center/9px no-repeat;
    pointer-events: none;
}

.fpop .fdash {
    color: var(--text-4);
    flex: none;
}

/* ---- problem results ----
   The olympiad list answers "where do I go"; this answers "where is that
   problem". Loaded on the first keystroke, never on page load. */
.railprobs {
    display: none;
    border-top: 1px solid var(--line);
    margin-top: 10px;
    padding-top: 4px;
}

.raillist.searching ~ .railprobs {
    display: block;
}

.railprobs .ph {
    padding: 10px 10px 3px;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: .08em;
    color: var(--text-3);
}

.railprobs a {
    display: block;
    margin: 1px 0;
    padding: 6px 10px;
    border-radius: 8px;
    color: var(--text);
}

.railprobs a:hover {
    background: var(--surface-3);
}

.railprobs .pt {
    display: block;
    font-size: 15px;
    line-height: 1.3;
}

.railprobs .pm {
    display: block;
    font-size: 12.5px;
    color: var(--text-3);
    margin-top: 1px;
}

/* The line of the problem's own text that matched, shown only when the hit was
   on the text rather than on the title -- otherwise the reader is looking at a
   title that has nothing to do with what they typed. Clamped to two lines: it
   is evidence, not the problem. */
.railprobs .px {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    font-size: 12.5px;
    line-height: 1.35;
    color: var(--text-2);
    margin-top: 2px;
}

/* Brightness and weight, not a highlighter block -- the same idiom the topic
   switch uses for its active state. Both halves have to do work: in the
   excerpt the surrounding text is already dimmed, so lifting the match to
   --text is contrast enough, but a title is drawn at --text already and the
   weight would be carrying it alone. A title matched end to end -- searching
   "black box" and finding "Black Box" -- would then be indistinguishable from
   a title that happened to be bold. */
/* padding:0 because Bootstrap pads mark by .1875em, which is invisible on one
   highlight and absurd on several: a subsequence match on "blkbx" draws four
   of them, and "Blackbody" rendered as "Bl ac kb ody". */
.railprobs mark {
    background: none;
    padding: 0;
    color: var(--text);
    font-weight: 600;
}

.railprobs .pt mark {
    color: var(--text-max);
}

.railprobs .pn {
    padding: 8px 10px 12px;
    color: var(--text-3);
    font-size: 13.5px;
}

/* ---- scrollbars ----
 * style.css sets a global 10px bar with a #3c3c3c track, lighter than the rail
 * it sits on. Styling ::-webkit-scrollbar at all also opts out of the overlay
 * scrollbars macOS would otherwise use, so the bar permanently occupies width
 * and the content shifts when it appears.
 *
 * The gutter is kept a constant width so nothing ever moves; only the thumb's
 * colour changes, so it fades in when you are actually using the area.
 */
.railinner {
    scrollbar-width: thin;
    scrollbar-color: transparent transparent;
}

.railinner:hover,
.railinner:focus-within {
    scrollbar-color: var(--scroll) transparent;
}

.railinner::-webkit-scrollbar {
    width: 8px;
}

.railinner::-webkit-scrollbar-track {
    background: transparent;
}

/* The transparent border plus background-clip leaves the thumb inset from the
   edge and rounded, rather than a full-width block. */
.railinner::-webkit-scrollbar-thumb {
    background: transparent;
    border: 2px solid transparent;
    border-radius: 999px;
    background-clip: content-box;
    transition: background-color .18s ease;
}

.railinner:hover::-webkit-scrollbar-thumb,
.railinner:focus-within::-webkit-scrollbar-thumb {
    background: var(--scroll);
    background-clip: content-box;
}

.railinner::-webkit-scrollbar-thumb:hover {
    background: var(--text-3);
    background-clip: content-box;
}

/* The page's own bar, toned down from the light track style.css gives it. */
body::-webkit-scrollbar {
    width: 10px;
}

body::-webkit-scrollbar-track {
    background: transparent;
}

body::-webkit-scrollbar-thumb {
    background: var(--surface-6);
    border: 2px solid transparent;
    border-radius: 999px;
    background-clip: content-box;
}

body::-webkit-scrollbar-thumb:hover {
    background: var(--scroll);
    background-clip: content-box;
}

/* ---- year cards (variant K) + flag chips (F5B) ----
 *
 * Desktop only. Below 992px the year panel is a full-width block above its
 * problems and the card framing would just add nesting to a narrow column, so
 * mobile keeps exactly what style.css already gives it.
 *
 * style.css lives only on the server and predates this repo, so these are
 * written here rather than edited there. That means overriding a few of its
 * declarations, which is why the specificity looks heavier than it needs to.
 */
@media (min-width: 992px) {
    /* The card edge separates the years now, so the rule between them is one
       divider too many. */
    main section > hr {
        display: none;
    }

    main article.row {
        background: var(--bg-alt);
        border-radius: 12px;
        margin-bottom: 22px;
        padding: 10px 0;
    }

    /* One step lighter than the card it sits in, so the year reads as inset
       rather than as another slab beside the problems. */
    main article.row .left-col {
        background-color: var(--surface-2);
        position: relative;
        overflow: hidden;
    }

    /* The year sits at the top of the panel, level with the first line of
       problems, and the flag has the bottom of the panel to itself. Centring
       it was fine while a year was a tall stack of full-width blocks; the grid
       made the cards short enough that the flag was crossing the country name.
       The floor is what a flag needs to sit under two lines of text: a year
       with one line of problems is shorter than that on its own. */
    main article.row .left-col {
        justify-content: flex-start;
        padding-top: 16px;
        min-height: 180px;
    }

    /* Above the flag, and legible even where a pale one sits behind them. */
    main article.row .left-col > p {
        position: relative;
        z-index: 1;
        text-shadow: 0 1px 6px var(--shadow-text);
    }

    /* Peeking out of the bottom-right corner, tilted.
       width:auto is deliberate: flags keep their natural aspect ratio, and
       Switzerland is square.
       clip-path is load-bearing rather than decorative -- at fractional pixel
       widths the SVG leaves a pale antialiased fringe along its edge, which is
       obvious against the dark panel. Insetting by a pixel eats the fringe,
       and the matching radius keeps the corners the border-radius rounded. */
    main article.row .left-col img.flag {
        position: absolute;
        right: -18px;
        bottom: -12px;
        /* Fixed, deliberately. Capping it against the panel height was tried
           and reverted: card heights vary with problem count, so a percentage
           made the same page show flags at several different sizes -- obvious
           on IJSO, where years have very different numbers of problems. One
           size everywhere reads as consistent, even where it fills a short
           card like a banner. */
        height: 100px;
        width: auto;
        border-radius: 10px;
        clip-path: inset(1px round 10px);
        transform: rotate(-8deg);
        opacity: var(--flag-op);
        box-shadow: 0 4px 16px var(--shadow);
    }
}

/* ---- the problem list ----
 *
 * One band per round, in two views over ONE DOM. Cards makes a round a third
 * of the row; list makes it a full-width row with every title shown. Rendering
 * two markups would double the page weight, and the pages being tiny is what
 * makes the rail's prefetching feel instant, so the views are pure CSS over the
 * same elements and the switch only changes a class on <html>.
 *
 * style.css lives only on the server and predates this repo, so several rules
 * below override it from here rather than being fixed at the source. Those are
 * called out individually. This block must stay after the year-card block
 * above, which it also overrides at the same specificity.
 */

/* One type scale for both views. The grid and the bands had drifted -- 19px
   against 18px headings, and links a size apart. */
.rlabel {
    font-size: 18px;
    font-weight: 600;
    color: var(--text-0);
    line-height: 1.3;
    margin: 0;
}

.rcount {
    font-size: 13.5px;
    color: var(--text-3b);
    margin: 0;
}

/* min-width:0 so a long run of links can shrink instead of pushing the title
   out of its cell. */
.rlinks {
    display: flex;
    gap: 20px;
    align-items: baseline;
    flex-wrap: wrap;
    min-width: 0;
}

.rlinks a {
    font-size: 16px;
    white-space: nowrap;
}

.rlinks a::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    margin-right: 8px;
    font-size: 14px;
}

.rlinks a.problem::before {
    content: "\f15c";
}

.rlinks a.solution::before {
    content: "\f0eb";
}

.none {
    color: var(--text-4);
    font-size: 14px;
    font-style: italic;
    white-space: nowrap;
}

/* ---- the year panel ----
 *
 * The min-height of 180px above gives a 100px flag room to sit under two lines
 * of text. That floor is what made a year with one row of problems render as a
 * 340px article holding 100px of content: the panel, not the problems, was
 * setting the height. The row is now as short as its content.
 *
 * The flag is NOT resized here, deliberately. It keeps the fixed 100px above at
 * every card height, so one page never shows two flag sizes. On a card only as
 * tall as its year and country the flag fills the panel like a banner and the
 * text sits over it -- that is the decided behaviour, not a bug, and the
 * text-shadow on .left-col > p is what keeps it legible. Do not "fix" it by
 * shrinking the flag or by sizing it as a percentage; both have been tried and
 * reverted. */
@media (min-width: 992px) {
    main article.row .left-col {
        min-height: 0;
    }

    /* The panel above also pins its text to the top with padding-top:16px,
       against the 5px style.css puts under .country. On a tall panel that reads
       as the year sitting level with the first line of problems; on a panel
       only as tall as its own text it read as a block shoved upwards against a
       big top margin. Matching the bottom padding to the top centres the short
       case without moving the year on the tall one. */
    main article.row .left-col {
        padding-bottom: 16px;
    }

    main article.row .left-col .country {
        padding-bottom: 0;
    }

    /* Symmetric: the card above has padding:10px 0, which put the first row of
       cells almost against the top edge. The bottom looked bigger than the top
       only because the 180px floor was padding the article out below the last
       row; with that gone this is even on both sides. */
    main article.row {
        padding: 14px 0;
    }
}

/* ---- the sitting a set of bands belongs to ---- */

/* A stage is a level above a round, so it cannot be styled like one: OPhO's
   "Open" and "Invitational" sit over "Theory" and "Experimental", and at the
   same weight the four read as one list of four. Small caps and a rule running
   to the edge say "everything under here", where a band heading says "these
   problems". Only stages the data names are drawn -- one sitting emits nothing
   and the edition is laid out exactly as it was. */
.stage {
    flex: 0 0 100%;
    max-width: 100%;
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 30px 0 0;
    /* The bands below keep a 14px right gutter, and without the same one here
       the rule ran 14px past the "Solutions" link it sits over -- ending in
       empty space instead of level with the block it heads. */
    padding-right: 14px;
}

.stage:first-child {
    margin-top: 2px;
}

.stage::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: var(--surface-2);
}

.slabel {
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.09em;
    color: var(--text-3b);
    white-space: nowrap;
}

/* The divider already draws the line this band's border would repeat, and two
   rules 13px apart read as a mistake. Cards cannot borrow this -- the band is
   display:contents there -- and buys its spacing in margins instead.
   The padding is larger than the 13px the border took with it, not smaller: a
   stage heading sits over a round heading, and with the two nearly touching the
   eye reads "INVITATIONAL Part I" as one line of two weights rather than as one
   thing inside another. Above the divider is larger still, so it stays nearer
   what it opens than what it closes. */
.stage + .band {
    border-top: 0;
    padding-top: 15px;
}

/* A round of one is stated twice -- title on the heading, and the row it is --
   and each view drops one of them. List drops the heading's copy: a round of
   one is a round like any other here, and printing its title on the heading
   while the round above it lists its two in rows made one paper look like a
   different kind of thing from the paper beside it. The count stays, so the
   heading reads "Experimental 1 problem" exactly as its neighbour reads
   "Experimental 2 problems".
   The links go with the title: the row carries them, as every other
   per-problem row does. Cards does the opposite of all this, further down.
   Only .per, because a shared round states its links once for the round and
   its rows never carry any.

   Written :not(.v-cards) rather than as a plain rule cards overrides, for the
   reason the narrow-width block gives further down: it is the form that still
   holds if the boot script never runs and no class is set at all. Hiding the
   links unconditionally and putting them back in cards also cost the round of
   one its Problem and Solution links for one build -- the card carries them on
   the heading, because the row that would carry them is the thing cards
   hides. */
html:not(.v-cards) .band.one > .bandhead > .rcount.solo {
    display: none;
}

html:not(.v-cards) .band.per.one > .bandhead > .rlinks {
    display: none;
}

/* ---- list: bands, every title shown ---- */
.band {
    flex: 0 0 100%;
    max-width: 100%;
    min-width: 0;
    border-top: 1px solid var(--surface-2);
    padding: 13px 14px 11px 0;
}

.band:first-child {
    border-top: 0;
    padding-top: 2px;
}

.band:last-child {
    padding-bottom: 0;
}

.bandhead {
    display: flex;
    align-items: baseline;
    gap: 13px;
    flex-wrap: wrap;
    min-width: 0;
    text-align: left;
}

.bandhead .rlinks {
    margin: 0 0 0 auto;
    justify-content: flex-end;
}

/* text-align:left is load-bearing: style.css centres this whole column, so a
   wrapped title centres its second line under its first. */
.cols,
.rows {
    list-style: none;
    margin: 9px 0 0;
    padding: 0;
    text-align: left;
}

.cols {
    /* List view means one problem per row. Shared-paper rounds used to keep
       their old three-column layout here, which made long IJSO titles cramped
       and visually inconsistent with every per-problem round around them. */
    columns: 1;
    column-gap: 0;
}

.cols li {
    font-size: 16px;
    line-height: 1.4;
    color: var(--text-1d);
    padding: 7px 0;
    border-top: 1px solid var(--line-faint);
    break-inside: avoid;
    /* Reserve the link column before sizing the title. A flex row could
       shrink .rlinks below the width of its anchor, which then overflowed
       into the next column when a neighbouring title was long. */
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr) auto;
    align-items: baseline;
    gap: 14px;
}

.cols li:first-child {
    border-top: 0;
}

/* A title under a shared paper has no links of its own to show, so the span is
   there only for the case where it does -- IOAA's Data Analysis, which shares a
   paper and publishes a solution per problem. */
.cols li .rlinks {
    display: none;
}

.cols li.own .rlinks {
    display: flex;
    margin-left: 0;
}

.rows li {
    display: flex;
    align-items: baseline;
    gap: 14px;
    padding: 7px 0;
    border-top: 1px solid var(--line-faint);
    min-width: 0;
    font-size: 16px;
}

.rows li:first-child {
    border-top: 0;
}

/* "Q1", not "1", everywhere the number is shown. On its own in a column a bare
   numeral is ambiguous -- it could be a year, a count, a rank -- and the letter
   costs one character. Cards hides the number entirely, so this lands on list
   view and on the rows a topic filter brings back. In CSS, not the data: the id
   underneath stays a plain number for sorting and anchors. */
.cols .n::before,
.rows .n::before {
    content: "Q";
}

.cols .n,
.rows .n {
    color: var(--text-3);
    /* 19px fitted two digits; the Q needs the room. */
    min-width: 26px;
    text-align: right;
    flex: none;
    font-size: 14px;
    font-variant-numeric: tabular-nums;
}

.cols .t,
.rows .t {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--text-0b);
    line-height: 1.35;
}

.rows .rlinks {
    margin: 0 0 0 auto;
    flex: none;
    justify-content: flex-end;
}

/* ---- cards: a real grid, not wrapped flex ----
 *
 * Flex wrapping sized each cell independently, so in a row of three a two-line
 * title pushed its own links down while its neighbours' stayed up. Grid rows are
 * one height by definition, and margin-top:auto then drops every cell's links
 * onto the same baseline.
 *
 * The gaps are row-gap/column-gap rather than per-cell padding, which is what
 * removes the dead strip under the last row: padding applies to every cell, a
 * gap only applies between them.
 *
 * display:contents on the band lets a per-problem round's rows become grid
 * cells themselves instead of stacking inside one third.
 */
html.v-cards .probs {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    column-gap: 26px;
    row-gap: 24px;
    margin: 0;
    align-items: stretch;
}

html.v-cards .cols {
    display: none;
}

html.v-cards .band {
    display: contents;
}

/* .band is display:contents here, so its cells are children of the grid and a
   divider dropped between them would take one cell of three and sit beside the
   problems it is meant to head. */
html.v-cards .stage {
    grid-column: 1 / -1;

    /* The 24px row-gap falls above AND below this row, so the divider sat
       equidistant between the block it closes and the block it opens and read
       as belonging to neither. A grid has no per-row gap, and .stage + .band's
       padding cannot close the one below because that band is display:contents
       here, so the margins do it: +14 makes the gap above 38px, and the -14
       shortens this row's track by as much, pulling the first cell of the next
       one to 10px below the rule. */
    margin: 14px 0 -14px;

    /* Both gutters list needs are wrong here, at every width, and this has to
       go on outranking the narrow-width one below. .probs keeps none of the
       bootstrap row's negative side margins in cards, and a band is
       display:contents so its own gutter goes with its box -- the grid's edges
       already carry the inset, and padding here would push the rule 15px
       inside the cells it heads rather than level with them. */
    padding: 0;
}

/* .stage:first-child above is outranked by this section's selector, and
   without repeating it the first divider took the full 14px and started the
   problems 12px below the year sitting beside them. */
html.v-cards .stage:first-child {
    margin-top: 2px;
}

html.v-cards .bandhead {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    padding: 0;
    max-width: none;
}

/* A per-problem round's heading would only repeat what the cells beside it
   already say, and it would eat a third of the row to do it. Not so for a round
   of one, whose heading has absorbed the cell: hiding it there left the round
   unnamed and its problem looking like a loose one. */
html.v-cards .band.per:not(.one) > .bandhead {
    display: none;
}

/* ---- the round tag (cards only) ----
 *
 * Which paper a card came from, printed on the card. With the heading hidden
 * above, a band is display:contents and its cells just join the flow -- so
 * IPhO's experimental problems sat in the same grid as the theory ones with
 * nothing between them, and IPhO 2025 put two experimental cells beside a
 * theory round of one, three abreast, all reading as peers. build.py emits it
 * only where the edition has a second round of problems to be confused with.
 *
 * Deliberately not a heading: smaller and quieter than .rlabel, and quieter
 * than .slabel too, since a round sits inside a stage. The title stays the
 * biggest thing in the cell -- the tag says which paper, the title says which
 * problem, and it is the problem you came for.
 *
 * List gets none of it: there the band heading is one line above the rows.
 */
.rtag {
    display: none;
}

html.v-cards .rows li .rtag,
html.v-cards .band.tagged.one > .bandhead > .rlabel {
    display: block;
    font-size: 11.5px;
    font-weight: 650;
    letter-spacing: 0.07em;
    color: var(--text-3b);
    line-height: 1.3;
}

/* A tagged cell hangs from the top instead of centring over its links, and the
   tags across a row then sit on one line -- which is the point of them: two
   cards reading EXPERIMENTAL side by side say "this is the other paper" at a
   glance, where two tags 12px apart (the leftover height of a neighbour's
   second title line, split in two) just look misaligned. The centring below
   stays for untagged cells, where the title is alone in the cell and there is
   nothing for it to line up with. */
html.v-cards .rows li .rtag,
html.v-cards .rows li .rtag + .t,
html.v-cards .band.tagged.one > .bandhead > .rlabel {
    margin-top: 0;
}

/* A round of one keeps its heading in cards, and that heading is now the same
   two lines as a tagged cell: round above, title below. It read the other way
   round -- the round name at 18px bold over the title at 13.5px muted -- which
   put the paper above the problem in the one place where that problem's title
   is the only one on screen, and made IPhO's one-experimental years look
   nothing like its two-experimental ones. */
html.v-cards .band.one > .bandhead > .rcount.solo {
    font-size: 18px;
    font-weight: 400;
    color: var(--text-0);
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Cards takes the other half of the round-of-one deal above: the heading has
   absorbed the cell, so the title shows there and the row would only be a
   second cell repeating it. .rows is display:contents here, so the row has to
   be hidden outright or it comes back as a grid cell of its own.
   The row stays in the markup either way -- the topic filter decides what to
   hide by asking whether a band :has(li[data-t~="..."]), and a band with no
   rows at all answers no to every topic and disappears whenever a filter is
   on. */
html.v-cards .band.one > .rows,
html.v-cards .band.one > .cols {
    display: none;
}

/* "1 problem" is the least informative thing this slot can hold when the
   problem's own title is what the cell is for. */
html.v-cards .band.one > .bandhead > .rcount:not(.solo) {
    display: none;
}

html.v-cards .rows {
    display: contents;
}

html.v-cards .rows li {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    border-top: 0;
    padding: 0;
    font-size: inherit;
}

html.v-cards .rows li .n {
    display: none;
}

/* Same size and colour as .rlabel beside it, but NOT bold, and the weight is
   the whole distinction: a bold cell is a heading over several problems --
   "Theory", "Written Response Test", "Problems - 6 problems" -- and a regular
   one is a single problem's own name. In a row that mixes the two, that is the
   only cue telling you whether "Greenhouse Effect" names a paper or a problem.
   Cards only; in list a title sits in a row of its own and needs no such
   signal. */
html.v-cards .rows li .t {
    font-size: 18px;
    font-weight: 400;
    color: var(--text-0);
    line-height: 1.3;
    flex: 0 0 auto;
}

/* margin-top:auto is the whole trick: it pins links to the bottom of a cell
   that grid has already stretched to the row height. */
html.v-cards .bandhead .rlinks,
html.v-cards .rows li .rlinks {
    margin: auto 0 0;
    padding-top: 10px;
    justify-content: flex-start;
}

/* ...and a second auto margin above the title splits the leftover height in
   two: half above the title, half between the title and the links. A one-line
   title in a row whose tallest cell wraps to two therefore centres in the space
   over its links instead of hanging from the top with a hole under it.
   Do NOT swap this for justify-content:center -- auto margins are resolved
   before justify-content, so the links' margin-top:auto would eat all the free
   space and nothing would centre. */
html.v-cards .rows li .t,
html.v-cards .bandhead .rlabel {
    margin-top: auto;
}

/* Two lines, then an ellipsis. style.css gets this from .name being a
   fixed-height box, which these elements replaced and so lost. build.py pairs
   the clamp with a title attribute (TOOLTIP_OVER), which only makes sense
   because the visible text is clipped -- so the full title stays in the markup
   for Ctrl+F and for search. Cards only: in list view a title has the whole row
   and should not be cut. */
html.v-cards .rows li .t,
html.v-cards .bandhead .rlabel {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Links must not wrap inside a card. Wrapping was added to stop them
   overflowing into the neighbouring cell, but it trades an overlap for a taller
   cell: iGeo's "Problems + Marking scheme" wrapped while its neighbours did not,
   so the whole grid row lost its alignment. Keeping them on one line with a
   tighter gap fits every label we have. */
html.v-cards .rlinks {
    flex-wrap: nowrap;
    gap: 16px;
}

html.v-cards .rlinks a {
    white-space: nowrap;
}

/* ---- narrow: one column ----
 *
 * Below 992px the year panel is already a full-width block above its problems,
 * so the problems have the page width and nothing else. Three columns at that
 * width is ~110px a cell, and .rlinks a is white-space:nowrap, so the links
 * cannot fit and cannot wrap: measured at a 430px viewport with this override
 * removed, the document lays out 490px wide with 118 elements past the right
 * edge and "Problem"/"Solution" printed on top of each other. Both views
 * collapse to one column, which is also what makes them differ only in how much
 * they show rather than in how they fit.
 */
@media (max-width: 991px) {
    html.v-cards .probs {
        grid-template-columns: 1fr;
    }

    .cols {
        columns: 1;
    }

    /* .probs is a bootstrap .row, so it carries -15px side margins that a
       column's own 15px padding normally cancels. A band is not a column and
       has no such padding, which on a wide card only means the band starts
       level with the row -- but on a phone the row IS the page, so every title
       sat hard against the screen edge while the year panel above it was inset
       by 15px. Putting the gutter back on the band lines the two up. A stage
       divider is no more a column than a band is and needs the same gutter at
       the same width, or its label starts 15px left of every title under it. */
    .band,
    .stage {
        padding-left: 15px;
        padding-right: 15px;
    }
}

/* On a phone a title and its links no longer fit side by side, so the links
 * drop onto their own line under the title. Nothing overflows either way -- the
 * row shrinks the title instead -- but the links are a fixed cost repeated on
 * every row, and at 430px on iGeo they take 62% of it: "Fieldwork Exercise 1"
 * is squeezed into ~120px and breaks after "Fieldwork", while the two link
 * labels beside it sit on one comfortable line. The titles are the content.
 *
 * Not applied over the whole narrow range: a tablet row is wide enough for
 * both, and wrapping there only makes every problem twice as tall.
 *
 * The rule must not add a horizontal margin next to the 100% basis, hence
 * cancelling the auto margin .rows .rlinks carries for the wide case: a basis
 * of 100% plus a margin is wider than the row, and one element sticking out
 * sets the width of the whole document.
 *
 * List only, hence the :not(). In cards a row IS the cell -- flex-direction is
 * column there -- so wrapping it wraps along the vertical axis and starts a
 * second COLUMN, which put a cell's links to the right of its own title.
 * :not(.v-cards) rather than .v-list so it still holds if the boot script never
 * ran and no view class was set, which is the case list already renders. */
@media (max-width: 575px) {
    html:not(.v-cards) .rows li {
        flex-wrap: wrap;
    }

    html:not(.v-cards) .rows .rlinks {
        margin-left: 0;
        flex-basis: 100%;
    }
}

/* ---- the view switch ---- */
.viewbar {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: 10px;
    margin: 0 0 16px;
}

/* The theme control. Round rather than a pill, so it reads as one button next
   to the two-position switch instead of a third view. The glyph is a mask on
   currentColor, like the rail's, which means the hover state colours it without
   a second copy of the icon. */
.modesw {
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border);
    border-radius: 99px;
    background: none;
    color: var(--text-2b);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    transition: background .15s, color .15s;
}

.modesw:hover {
    background: var(--surface-3);
    color: var(--text);
}

.modesw:focus {
    outline: none;
}

.modesw:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.modesw::before {
    /* Font Awesome 5 Free, the same library the subject chips and the
       problem/solution links already use, rather than a hand-drawn glyph that
       sits at a slightly different weight from everything around it. A sun
       while the page is dark and a moon while it is light: the icon is the
       destination, not the current state. */
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    line-height: 1;
    content: "\f185";
}

/* Same pairing as the palette: the media query covers "following a light
   system", html.light after it covers "pinned light". */
@media (prefers-color-scheme: light) {
    html:not(.dark) .modesw::before {
        content: "\f186";
    }
}

html.light .modesw::before {
    content: "\f186";
}

/* Cards hides the per-problem titles of a shared round, which is right until a
   filter is on: the card then says the year matched without saying what
   matched, and "which question is it" is the whole question a filter asks.
   While filtering, the matching rows come back in the space the count just
   vacated -- number and title, the same answer list gives, at a smaller size
   because the round heading above it is still the thing being read. */
html[class*="t-"].v-cards .cols {
    display: block;
    columns: 1;
    margin: 3px 0 0;
    padding: 0;
    list-style: none;
}

html[class*="t-"].v-cards .cols li {
    display: flex;
    gap: 8px;
    align-items: baseline;
    padding: 0;
    font-size: 13.5px;
    line-height: 1.45;
}

html[class*="t-"].v-cards .cols li .n {
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
    flex: none;
}

html[class*="t-"].v-cards .cols li .t {
    color: var(--text-2);
    /* Not clamped: a filtered card shows one or two rows, so there is room, and
       a truncated answer to "which question" is not much of an answer. */
    -webkit-line-clamp: none;
    display: block;
}


/* ---- topic filter ----
 *
 * One topic at a time, set as a class on <html>, so this is pure CSS: rows that
 * do not carry the topic go, then any band left with none of them, then any
 * year left with no bands. :has() is what makes the last two possible without
 * JavaScript walking the tree.
 *
 * A problem with no data-t is untagged and disappears under every filter, which
 * is the honest behaviour: we do not know what it is, so we cannot claim it is
 * the thing you asked for.
 */
.topicsw {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 20px;
    margin-right: auto;
}

/* Words, not pills. Two rows of bordered chips was more chrome than a control
   most visitors never touch deserves, sitting directly between the title and
   the content. Weight and brightness carry the active state, which needs no
   extra colour and survives both themes unchanged. */
.topicsw button {
    font: inherit;
    font-size: 13.5px;
    padding: 5px 0;
    color: var(--text-4);
    background: none;
    border: 0;
    cursor: pointer;
    letter-spacing: .01em;
    transition: color .15s;
}

.topicsw button:hover {
    color: var(--text-1b);
}

.topicsw button:focus {
    outline: none;
}

/* Offset outwards: with no border or padding the ring would otherwise sit on
   the letters themselves. */
.topicsw button:focus-visible,
.subsw button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 3px;
}

.topicsw button.on {
    color: var(--text-max);
    font-weight: 650;
}

/* The round header counts every problem in the round, which stops being true
   the moment a filter hides some of them. Rather than recount in JavaScript,
   the count goes away while filtering: the rows on screen are the answer.
   Not the solo title, which borrows .rcount's slot but is not a count: its row
   is hidden, so taking it away left a round of one showing its round name, its
   links, and no problem at all -- exactly the case the filter was asked about. */
html[class*="t-"] .rcount:not(.solo) {
    display: none;
}

html.t-mechanics .rows li:not([data-t~="mechanics"]),
html.t-mechanics .cols li:not([data-t~="mechanics"]) {
    display: none;
}

html.t-mechanics .band:not(:has(li[data-t~="mechanics"])),
html.t-mechanics article.row:not(:has(li[data-t~="mechanics"])) {
    display: none;
}

html.t-electromagnetism .rows li:not([data-t~="electromagnetism"]),
html.t-electromagnetism .cols li:not([data-t~="electromagnetism"]) {
    display: none;
}

html.t-electromagnetism .band:not(:has(li[data-t~="electromagnetism"])),
html.t-electromagnetism article.row:not(:has(li[data-t~="electromagnetism"])) {
    display: none;
}

html.t-thermodynamics .rows li:not([data-t~="thermodynamics"]),
html.t-thermodynamics .cols li:not([data-t~="thermodynamics"]) {
    display: none;
}

html.t-thermodynamics .band:not(:has(li[data-t~="thermodynamics"])),
html.t-thermodynamics article.row:not(:has(li[data-t~="thermodynamics"])) {
    display: none;
}

html.t-waves-optics .rows li:not([data-t~="waves-optics"]),
html.t-waves-optics .cols li:not([data-t~="waves-optics"]) {
    display: none;
}

html.t-waves-optics .band:not(:has(li[data-t~="waves-optics"])),
html.t-waves-optics article.row:not(:has(li[data-t~="waves-optics"])) {
    display: none;
}

html.t-modern-quantum .rows li:not([data-t~="modern-quantum"]),
html.t-modern-quantum .cols li:not([data-t~="modern-quantum"]) {
    display: none;
}

html.t-modern-quantum .band:not(:has(li[data-t~="modern-quantum"])),
html.t-modern-quantum article.row:not(:has(li[data-t~="modern-quantum"])) {
    display: none;
}

html.t-relativity .rows li:not([data-t~="relativity"]),
html.t-relativity .cols li:not([data-t~="relativity"]) {
    display: none;
}

html.t-relativity .band:not(:has(li[data-t~="relativity"])),
html.t-relativity article.row:not(:has(li[data-t~="relativity"])) {
    display: none;
}

html.t-astrophysics .rows li:not([data-t~="astrophysics"]),
html.t-astrophysics .cols li:not([data-t~="astrophysics"]) {
    display: none;
}

html.t-astrophysics .band:not(:has(li[data-t~="astrophysics"])),
html.t-astrophysics article.row:not(:has(li[data-t~="astrophysics"])) {
    display: none;
}

/* Year rules are siblings of the articles, so hiding an unmatched article does
   not hide the rule before it. A narrow filter could otherwise leave a stack
   of separators with no content between them. */
html[class*="t-"] main section > hr {
    display: none;
}

/* Cards makes a band display:contents so its heading is a grid cell of its own,
   which split heading from titles across two columns. A shared band that has a
   match becomes one cell again. Written per topic and gated on :has() so it
   cannot outrank the rule that hides a band with no match -- which is exactly
   what the earlier, broader version did. */
html.t-mechanics.v-cards .band:not(.per):has(li[data-t~="mechanics"]) {
    display: flex;
    flex-direction: column;
}

html.t-electromagnetism.v-cards .band:not(.per):has(li[data-t~="electromagnetism"]) {
    display: flex;
    flex-direction: column;
}

html.t-thermodynamics.v-cards .band:not(.per):has(li[data-t~="thermodynamics"]) {
    display: flex;
    flex-direction: column;
}

html.t-waves-optics.v-cards .band:not(.per):has(li[data-t~="waves-optics"]) {
    display: flex;
    flex-direction: column;
}

html.t-modern-quantum.v-cards .band:not(.per):has(li[data-t~="modern-quantum"]) {
    display: flex;
    flex-direction: column;
}

html.t-relativity.v-cards .band:not(.per):has(li[data-t~="relativity"]) {
    display: flex;
    flex-direction: column;
}

html.t-astrophysics.v-cards .band:not(.per):has(li[data-t~="astrophysics"]) {
    display: flex;
    flex-direction: column;
}

/* Order inside a filtered card: heading, what matched, then the links -- which
   is where the unfiltered card already puts them, pinned to the bottom.
   The links live inside .bandhead, so they cannot be reordered against .cols
   from where they sit. Making .bandhead `display: contents` while filtering
   dissolves it, promoting the label and the links to children of the band,
   which is the flex container doing the ordering. No markup change needed.
   .rcount is already hidden while filtering, so it takes no order. */
/* .band carries a rule and 13px of padding for list view, where bands stack and
   need separating. In cards that is inert, because the band is display:contents
   and has no box -- until filtering gives it one, at which point the list-view
   padding came back and pushed the second band in a row 11px below the first,
   labels, links and all. Cards never wants either. */
html[class*="t-"].v-cards .band:not(.per) {
    border-top: 0;
    padding: 0;
}

html[class*="t-"].v-cards .band:not(.per) > .bandhead {
    display: contents;
}

html[class*="t-"].v-cards .band:not(.per) > .bandhead > .rlabel {
    order: 0;
}

html[class*="t-"].v-cards .band:not(.per) > .cols {
    order: 1;
}

html[class*="t-"].v-cards .band:not(.per) > .bandhead > .rlinks {
    order: 2;
    margin-top: auto;
    padding-top: 6px;
}


/* ---- subtopic chips ----
 *
 * Rendered once, all of them, and revealed by CSS under their own topic. That
 * keeps topic switching free of DOM work, and means the second row simply does
 * not exist until a topic narrows it to something worth reading.
 *
 * The hiding rules mirror the topic ones exactly -- rows, then empty bands,
 * then empty years -- because a subtopic filter is the same operation on a
 * different attribute. */
.subsw {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 16px;
    margin: 2px 0 16px;
}

/* A step quieter than the topics, and the active one takes the accent rather
   than more weight: at this size bold alone did not read as chosen. */
.subsw button {
    display: none;
    font: inherit;
    font-size: 12.5px;
    padding: 3px 0;
    color: var(--text-5);
    background: none;
    border: 0;
    cursor: pointer;
    transition: color .15s;
}

.subsw button:hover {
    color: var(--text-2);
}

.subsw button:focus {
    outline: none;
}

.subsw button.on {
    color: var(--accent);
    font-weight: 600;
}

html.t-mechanics .subsw button[data-of="mechanics"] { display: inline-flex; }
html.t-electromagnetism .subsw button[data-of="electromagnetism"] { display: inline-flex; }
html.t-thermodynamics .subsw button[data-of="thermodynamics"] { display: inline-flex; }
html.t-waves-optics .subsw button[data-of="waves-optics"] { display: inline-flex; }
html.t-modern-quantum .subsw button[data-of="modern-quantum"] { display: inline-flex; }
html.t-relativity .subsw button[data-of="relativity"] { display: inline-flex; }
html.t-astrophysics .subsw button[data-of="astrophysics"] { display: inline-flex; }

html.s-kinematics .rows li:not([data-s~="kinematics"]),
html.s-kinematics .cols li:not([data-s~="kinematics"]) {
    display: none;
}

html.s-kinematics .band:not(:has(li[data-s~="kinematics"])),
html.s-kinematics article.row:not(:has(li[data-s~="kinematics"])) {
    display: none;
}

html.s-kinematics.v-cards .band:not(.per):has(li[data-s~="kinematics"]) {
    display: flex;
    flex-direction: column;
}

html.s-dynamics .rows li:not([data-s~="dynamics"]),
html.s-dynamics .cols li:not([data-s~="dynamics"]) {
    display: none;
}

html.s-dynamics .band:not(:has(li[data-s~="dynamics"])),
html.s-dynamics article.row:not(:has(li[data-s~="dynamics"])) {
    display: none;
}

html.s-dynamics.v-cards .band:not(.per):has(li[data-s~="dynamics"]) {
    display: flex;
    flex-direction: column;
}

html.s-rigid-body .rows li:not([data-s~="rigid-body"]),
html.s-rigid-body .cols li:not([data-s~="rigid-body"]) {
    display: none;
}

html.s-rigid-body .band:not(:has(li[data-s~="rigid-body"])),
html.s-rigid-body article.row:not(:has(li[data-s~="rigid-body"])) {
    display: none;
}

html.s-rigid-body.v-cards .band:not(.per):has(li[data-s~="rigid-body"]) {
    display: flex;
    flex-direction: column;
}

html.s-fluids .rows li:not([data-s~="fluids"]),
html.s-fluids .cols li:not([data-s~="fluids"]) {
    display: none;
}

html.s-fluids .band:not(:has(li[data-s~="fluids"])),
html.s-fluids article.row:not(:has(li[data-s~="fluids"])) {
    display: none;
}

html.s-fluids.v-cards .band:not(.per):has(li[data-s~="fluids"]) {
    display: flex;
    flex-direction: column;
}

html.s-gravitation .rows li:not([data-s~="gravitation"]),
html.s-gravitation .cols li:not([data-s~="gravitation"]) {
    display: none;
}

html.s-gravitation .band:not(:has(li[data-s~="gravitation"])),
html.s-gravitation article.row:not(:has(li[data-s~="gravitation"])) {
    display: none;
}

html.s-gravitation.v-cards .band:not(.per):has(li[data-s~="gravitation"]) {
    display: flex;
    flex-direction: column;
}

html.s-statics .rows li:not([data-s~="statics"]),
html.s-statics .cols li:not([data-s~="statics"]) {
    display: none;
}

html.s-statics .band:not(:has(li[data-s~="statics"])),
html.s-statics article.row:not(:has(li[data-s~="statics"])) {
    display: none;
}

html.s-statics.v-cards .band:not(.per):has(li[data-s~="statics"]) {
    display: flex;
    flex-direction: column;
}

html.s-electrostatics .rows li:not([data-s~="electrostatics"]),
html.s-electrostatics .cols li:not([data-s~="electrostatics"]) {
    display: none;
}

html.s-electrostatics .band:not(:has(li[data-s~="electrostatics"])),
html.s-electrostatics article.row:not(:has(li[data-s~="electrostatics"])) {
    display: none;
}

html.s-electrostatics.v-cards .band:not(.per):has(li[data-s~="electrostatics"]) {
    display: flex;
    flex-direction: column;
}

html.s-circuits .rows li:not([data-s~="circuits"]),
html.s-circuits .cols li:not([data-s~="circuits"]) {
    display: none;
}

html.s-circuits .band:not(:has(li[data-s~="circuits"])),
html.s-circuits article.row:not(:has(li[data-s~="circuits"])) {
    display: none;
}

html.s-circuits.v-cards .band:not(.per):has(li[data-s~="circuits"]) {
    display: flex;
    flex-direction: column;
}

html.s-magnetostatics .rows li:not([data-s~="magnetostatics"]),
html.s-magnetostatics .cols li:not([data-s~="magnetostatics"]) {
    display: none;
}

html.s-magnetostatics .band:not(:has(li[data-s~="magnetostatics"])),
html.s-magnetostatics article.row:not(:has(li[data-s~="magnetostatics"])) {
    display: none;
}

html.s-magnetostatics.v-cards .band:not(.per):has(li[data-s~="magnetostatics"]) {
    display: flex;
    flex-direction: column;
}

html.s-induction .rows li:not([data-s~="induction"]),
html.s-induction .cols li:not([data-s~="induction"]) {
    display: none;
}

html.s-induction .band:not(:has(li[data-s~="induction"])),
html.s-induction article.row:not(:has(li[data-s~="induction"])) {
    display: none;
}

html.s-induction.v-cards .band:not(.per):has(li[data-s~="induction"]) {
    display: flex;
    flex-direction: column;
}

html.s-em-waves .rows li:not([data-s~="em-waves"]),
html.s-em-waves .cols li:not([data-s~="em-waves"]) {
    display: none;
}

html.s-em-waves .band:not(:has(li[data-s~="em-waves"])),
html.s-em-waves article.row:not(:has(li[data-s~="em-waves"])) {
    display: none;
}

html.s-em-waves.v-cards .band:not(.per):has(li[data-s~="em-waves"]) {
    display: flex;
    flex-direction: column;
}

html.s-heat-transfer .rows li:not([data-s~="heat-transfer"]),
html.s-heat-transfer .cols li:not([data-s~="heat-transfer"]) {
    display: none;
}

html.s-heat-transfer .band:not(:has(li[data-s~="heat-transfer"])),
html.s-heat-transfer article.row:not(:has(li[data-s~="heat-transfer"])) {
    display: none;
}

html.s-heat-transfer.v-cards .band:not(.per):has(li[data-s~="heat-transfer"]) {
    display: flex;
    flex-direction: column;
}

html.s-gas-laws .rows li:not([data-s~="gas-laws"]),
html.s-gas-laws .cols li:not([data-s~="gas-laws"]) {
    display: none;
}

html.s-gas-laws .band:not(:has(li[data-s~="gas-laws"])),
html.s-gas-laws article.row:not(:has(li[data-s~="gas-laws"])) {
    display: none;
}

html.s-gas-laws.v-cards .band:not(.per):has(li[data-s~="gas-laws"]) {
    display: flex;
    flex-direction: column;
}

html.s-entropy .rows li:not([data-s~="entropy"]),
html.s-entropy .cols li:not([data-s~="entropy"]) {
    display: none;
}

html.s-entropy .band:not(:has(li[data-s~="entropy"])),
html.s-entropy article.row:not(:has(li[data-s~="entropy"])) {
    display: none;
}

html.s-entropy.v-cards .band:not(.per):has(li[data-s~="entropy"]) {
    display: flex;
    flex-direction: column;
}

html.s-kinetic-theory .rows li:not([data-s~="kinetic-theory"]),
html.s-kinetic-theory .cols li:not([data-s~="kinetic-theory"]) {
    display: none;
}

html.s-kinetic-theory .band:not(:has(li[data-s~="kinetic-theory"])),
html.s-kinetic-theory article.row:not(:has(li[data-s~="kinetic-theory"])) {
    display: none;
}

html.s-kinetic-theory.v-cards .band:not(.per):has(li[data-s~="kinetic-theory"]) {
    display: flex;
    flex-direction: column;
}

html.s-phase-transitions .rows li:not([data-s~="phase-transitions"]),
html.s-phase-transitions .cols li:not([data-s~="phase-transitions"]) {
    display: none;
}

html.s-phase-transitions .band:not(:has(li[data-s~="phase-transitions"])),
html.s-phase-transitions article.row:not(:has(li[data-s~="phase-transitions"])) {
    display: none;
}

html.s-phase-transitions.v-cards .band:not(.per):has(li[data-s~="phase-transitions"]) {
    display: flex;
    flex-direction: column;
}

html.s-statistical-mechanics .rows li:not([data-s~="statistical-mechanics"]),
html.s-statistical-mechanics .cols li:not([data-s~="statistical-mechanics"]) {
    display: none;
}

html.s-statistical-mechanics .band:not(:has(li[data-s~="statistical-mechanics"])),
html.s-statistical-mechanics article.row:not(:has(li[data-s~="statistical-mechanics"])) {
    display: none;
}

html.s-statistical-mechanics.v-cards .band:not(.per):has(li[data-s~="statistical-mechanics"]) {
    display: flex;
    flex-direction: column;
}

html.s-oscillations .rows li:not([data-s~="oscillations"]),
html.s-oscillations .cols li:not([data-s~="oscillations"]) {
    display: none;
}

html.s-oscillations .band:not(:has(li[data-s~="oscillations"])),
html.s-oscillations article.row:not(:has(li[data-s~="oscillations"])) {
    display: none;
}

html.s-oscillations.v-cards .band:not(.per):has(li[data-s~="oscillations"]) {
    display: flex;
    flex-direction: column;
}

html.s-wave-propagation .rows li:not([data-s~="wave-propagation"]),
html.s-wave-propagation .cols li:not([data-s~="wave-propagation"]) {
    display: none;
}

html.s-wave-propagation .band:not(:has(li[data-s~="wave-propagation"])),
html.s-wave-propagation article.row:not(:has(li[data-s~="wave-propagation"])) {
    display: none;
}

html.s-wave-propagation.v-cards .band:not(.per):has(li[data-s~="wave-propagation"]) {
    display: flex;
    flex-direction: column;
}

html.s-sound .rows li:not([data-s~="sound"]),
html.s-sound .cols li:not([data-s~="sound"]) {
    display: none;
}

html.s-sound .band:not(:has(li[data-s~="sound"])),
html.s-sound article.row:not(:has(li[data-s~="sound"])) {
    display: none;
}

html.s-sound.v-cards .band:not(.per):has(li[data-s~="sound"]) {
    display: flex;
    flex-direction: column;
}

html.s-interference .rows li:not([data-s~="interference"]),
html.s-interference .cols li:not([data-s~="interference"]) {
    display: none;
}

html.s-interference .band:not(:has(li[data-s~="interference"])),
html.s-interference article.row:not(:has(li[data-s~="interference"])) {
    display: none;
}

html.s-interference.v-cards .band:not(.per):has(li[data-s~="interference"]) {
    display: flex;
    flex-direction: column;
}

html.s-diffraction .rows li:not([data-s~="diffraction"]),
html.s-diffraction .cols li:not([data-s~="diffraction"]) {
    display: none;
}

html.s-diffraction .band:not(:has(li[data-s~="diffraction"])),
html.s-diffraction article.row:not(:has(li[data-s~="diffraction"])) {
    display: none;
}

html.s-diffraction.v-cards .band:not(.per):has(li[data-s~="diffraction"]) {
    display: flex;
    flex-direction: column;
}

html.s-geometrical-optics .rows li:not([data-s~="geometrical-optics"]),
html.s-geometrical-optics .cols li:not([data-s~="geometrical-optics"]) {
    display: none;
}

html.s-geometrical-optics .band:not(:has(li[data-s~="geometrical-optics"])),
html.s-geometrical-optics article.row:not(:has(li[data-s~="geometrical-optics"])) {
    display: none;
}

html.s-geometrical-optics.v-cards .band:not(.per):has(li[data-s~="geometrical-optics"]) {
    display: flex;
    flex-direction: column;
}

html.s-polarization .rows li:not([data-s~="polarization"]),
html.s-polarization .cols li:not([data-s~="polarization"]) {
    display: none;
}

html.s-polarization .band:not(:has(li[data-s~="polarization"])),
html.s-polarization article.row:not(:has(li[data-s~="polarization"])) {
    display: none;
}

html.s-polarization.v-cards .band:not(.per):has(li[data-s~="polarization"]) {
    display: flex;
    flex-direction: column;
}

html.s-atomic .rows li:not([data-s~="atomic"]),
html.s-atomic .cols li:not([data-s~="atomic"]) {
    display: none;
}

html.s-atomic .band:not(:has(li[data-s~="atomic"])),
html.s-atomic article.row:not(:has(li[data-s~="atomic"])) {
    display: none;
}

html.s-atomic.v-cards .band:not(.per):has(li[data-s~="atomic"]) {
    display: flex;
    flex-direction: column;
}

html.s-nuclear .rows li:not([data-s~="nuclear"]),
html.s-nuclear .cols li:not([data-s~="nuclear"]) {
    display: none;
}

html.s-nuclear .band:not(:has(li[data-s~="nuclear"])),
html.s-nuclear article.row:not(:has(li[data-s~="nuclear"])) {
    display: none;
}

html.s-nuclear.v-cards .band:not(.per):has(li[data-s~="nuclear"]) {
    display: flex;
    flex-direction: column;
}

html.s-particle .rows li:not([data-s~="particle"]),
html.s-particle .cols li:not([data-s~="particle"]) {
    display: none;
}

html.s-particle .band:not(:has(li[data-s~="particle"])),
html.s-particle article.row:not(:has(li[data-s~="particle"])) {
    display: none;
}

html.s-particle.v-cards .band:not(.per):has(li[data-s~="particle"]) {
    display: flex;
    flex-direction: column;
}

html.s-solid-state .rows li:not([data-s~="solid-state"]),
html.s-solid-state .cols li:not([data-s~="solid-state"]) {
    display: none;
}

html.s-solid-state .band:not(:has(li[data-s~="solid-state"])),
html.s-solid-state article.row:not(:has(li[data-s~="solid-state"])) {
    display: none;
}

html.s-solid-state.v-cards .band:not(.per):has(li[data-s~="solid-state"]) {
    display: flex;
    flex-direction: column;
}

html.s-quantum-mechanics .rows li:not([data-s~="quantum-mechanics"]),
html.s-quantum-mechanics .cols li:not([data-s~="quantum-mechanics"]) {
    display: none;
}

html.s-quantum-mechanics .band:not(:has(li[data-s~="quantum-mechanics"])),
html.s-quantum-mechanics article.row:not(:has(li[data-s~="quantum-mechanics"])) {
    display: none;
}

html.s-quantum-mechanics.v-cards .band:not(.per):has(li[data-s~="quantum-mechanics"]) {
    display: flex;
    flex-direction: column;
}

html.s-special-relativity .rows li:not([data-s~="special-relativity"]),
html.s-special-relativity .cols li:not([data-s~="special-relativity"]) {
    display: none;
}

html.s-special-relativity .band:not(:has(li[data-s~="special-relativity"])),
html.s-special-relativity article.row:not(:has(li[data-s~="special-relativity"])) {
    display: none;
}

html.s-special-relativity.v-cards .band:not(.per):has(li[data-s~="special-relativity"]) {
    display: flex;
    flex-direction: column;
}

html.s-general-relativity .rows li:not([data-s~="general-relativity"]),
html.s-general-relativity .cols li:not([data-s~="general-relativity"]) {
    display: none;
}

html.s-general-relativity .band:not(:has(li[data-s~="general-relativity"])),
html.s-general-relativity article.row:not(:has(li[data-s~="general-relativity"])) {
    display: none;
}

html.s-general-relativity.v-cards .band:not(.per):has(li[data-s~="general-relativity"]) {
    display: flex;
    flex-direction: column;
}

html.s-stellar .rows li:not([data-s~="stellar"]),
html.s-stellar .cols li:not([data-s~="stellar"]) {
    display: none;
}

html.s-stellar .band:not(:has(li[data-s~="stellar"])),
html.s-stellar article.row:not(:has(li[data-s~="stellar"])) {
    display: none;
}

html.s-stellar.v-cards .band:not(.per):has(li[data-s~="stellar"]) {
    display: flex;
    flex-direction: column;
}

html.s-orbital-mechanics .rows li:not([data-s~="orbital-mechanics"]),
html.s-orbital-mechanics .cols li:not([data-s~="orbital-mechanics"]) {
    display: none;
}

html.s-orbital-mechanics .band:not(:has(li[data-s~="orbital-mechanics"])),
html.s-orbital-mechanics article.row:not(:has(li[data-s~="orbital-mechanics"])) {
    display: none;
}

html.s-orbital-mechanics.v-cards .band:not(.per):has(li[data-s~="orbital-mechanics"]) {
    display: flex;
    flex-direction: column;
}

html.s-cosmology .rows li:not([data-s~="cosmology"]),
html.s-cosmology .cols li:not([data-s~="cosmology"]) {
    display: none;
}

html.s-cosmology .band:not(:has(li[data-s~="cosmology"])),
html.s-cosmology article.row:not(:has(li[data-s~="cosmology"])) {
    display: none;
}

html.s-cosmology.v-cards .band:not(.per):has(li[data-s~="cosmology"]) {
    display: flex;
    flex-direction: column;
}

html.s-observational .rows li:not([data-s~="observational"]),
html.s-observational .cols li:not([data-s~="observational"]) {
    display: none;
}

html.s-observational .band:not(:has(li[data-s~="observational"])),
html.s-observational article.row:not(:has(li[data-s~="observational"])) {
    display: none;
}

html.s-observational.v-cards .band:not(.per):has(li[data-s~="observational"]) {
    display: flex;
    flex-direction: column;
}

.viewsw {
    display: inline-flex;
    border: 1px solid var(--border);
    border-radius: 99px;
    overflow: hidden;
}

.viewsw button {
    font: inherit;
    font-size: 12.5px;
    padding: 5px 15px;
    color: var(--text-2b);
    background: none;
    border: 0;
    cursor: pointer;
    letter-spacing: .02em;
    border-radius: 99px;
}

.viewsw button:hover {
    color: var(--text-1b);
}

/* The browser's default focus ring is a rectangle, so on click it drew a blue
   box inside the pill. :focus-visible keeps the ring for keyboard users, where
   it is the only way to see where you are, and drops it for a mouse click,
   which does not need it. The radius above makes it follow the pill. */
.viewsw button:focus {
    outline: none;
}

.viewsw button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -3px;
}

/* Keyed off the class BOOT sets on <html> before first paint, not off a class
   the view script adds afterwards: the switch must never render with neither
   side selected, because there is always a view on screen.
   :not(.v-cards) for the List side, mirroring the layout rule above, so the
   two agree even when the boot script never runs and the page falls back to
   List with no class on <html> at all. */
html.v-cards .viewsw button[data-v="cards"],
html:not(.v-cards) .viewsw button[data-v="list"] {
    background: var(--border);
    color: var(--text-max);
}

/* ---- generated titles ----
 *
 * 36% of problems are published with a slot number and no name, so the title
 * shown for them is ours. The mark says so. It is deliberately quiet: the point
 * is that a reader who cares can tell, not that every row shouts about it.
 *
 * Nudged up and set smaller rather than made a true <sup>, which would push the
 * line box taller and break the tight rhythm of the card lists. Not selectable,
 * so copying a title copies the title.
 */
.gen {
    font-size: .8em;
    vertical-align: .35em;
    line-height: 0;
    margin-left: .12em;
    color: var(--text-3);
    -webkit-user-select: none;
    user-select: none;
}

/* In the footer the mark is the subject of the sentence, not an annotation on
 * something else, so it sits on the baseline and takes the text's own colour. */
footer .gen {
    vertical-align: baseline;
    font-size: 1em;
    margin-left: 0;
    color: inherit;
}

/* ---- mobile chrome and layouts ----
 *
 * Narrow screens get a small, always-available header. The existing rail
 * remains the source of truth for navigation and search, but becomes a sheet
 * below it.
 * Content stays deliberately dense: these are archive pages, not landing
 * pages, and a reader should be able to compare several problems at a glance.
 */
.mobilebar {
    display: none;
}

@media (max-width: 991px) {
    :root {
        --mobilebar-h: calc(56px + env(safe-area-inset-top));
    }

    body {
        overflow-x: hidden;
    }

    .mobilebar {
        position: sticky;
        top: 0;
        z-index: 150;
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        height: var(--mobilebar-h);
        margin: 0;
        padding: env(safe-area-inset-top) 15px 0;
        background: color-mix(in srgb, var(--bg) 94%, transparent);
        border-bottom: 1px solid var(--line);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
    }

    .mobilebrand {
        display: block;
        width: 116px;
        height: 24px;
        color: var(--text-max);
        flex: none;
    }

    .mobilebrand .brandmark {
        width: 100%;
        height: 100%;
    }

    .mobileactions {
        display: flex;
        align-items: center;
        gap: 6px;
    }

    .mobileaction {
        position: relative;
        width: 40px;
        height: 40px;
        padding: 0;
        border-radius: 10px;
        border: 0;
        background: transparent;
        color: var(--text-1b);
        cursor: pointer;
        transition: color .15s;
    }

    .mobileaction:hover,
    .mobileaction:focus-visible {
        color: var(--text-max);
    }

    .mobileaction:focus-visible {
        outline: 2px solid var(--accent);
        outline-offset: 2px;
    }

    .mobileaction[aria-expanded="true"] {
        color: var(--accent);
    }

    .mobile-search::before,
    .mobile-menu::before {
        content: "";
        position: absolute;
        inset: 10px;
        background: currentColor;
    }

    .mobile-search::before {
        inset: 10px;
        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'%3E%3Ccircle cx='10.8' cy='10.8' r='6.8'/%3E%3Cpath d='m16 16 5 5'/%3E%3C/svg%3E") center / 22px no-repeat;
        -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'%3E%3Ccircle cx='10.8' cy='10.8' r='6.8'/%3E%3Cpath d='m16 16 5 5'/%3E%3C/svg%3E") center / 22px no-repeat;
    }

    .mobile-menu::before {
        inset: 9px;
        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='1.8' stroke-linecap='round'%3E%3Cpath d='M3 6h18M3 12h18M3 18h18'/%3E%3C/svg%3E") center / 24px no-repeat;
        -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='1.8' stroke-linecap='round'%3E%3Cpath d='M3 6h18M3 12h18M3 18h18'/%3E%3C/svg%3E") center / 24px no-repeat;
    }

    .opener {
        display: none;
    }

    aside.rail {
        top: var(--mobilebar-h);
        width: 100%;
        border-right: 0;
        box-shadow: 0 18px 40px var(--shadow);
        z-index: 130;
    }

    .navtoggle:checked ~ .layout aside.rail {
        transform: none;
    }

    .scrim {
        top: var(--mobilebar-h);
        z-index: 120;
    }

    .railinner {
        width: 100%;
        padding: 14px 15px calc(24px + env(safe-area-inset-bottom));
    }

    .railtop {
        display: none;
    }

    .subjbar {
        gap: 6px;
        padding: 0 0 14px;
    }

    .subjbar .subj {
        min-height: 38px;
        padding: 8px 9px;
        font-size: 13.5px;
    }

    .railsearch {
        padding: 0 0 14px;
    }

    .railfilters {
        padding-left: 0;
        padding-right: 0;
    }

    html:not(.mobile-search) aside.rail .railsearch {
        display: none;
    }

    html:not(.mobile-search) aside.rail .railfilters,
    html:not(.mobile-search) aside.rail .railprobs {
        display: none !important;
    }

    html.mobile-search aside.rail .subjbar,
    html.mobile-search aside.rail .raillist {
        display: none;
    }

    html.mobile-search aside.rail .railsearch {
        --field: 46px;
        display: flex;
        align-items: center;
        gap: 8px;
        padding: 0 0 14px;
    }

    html.mobile-search aside.rail .railsearch::before {
        left: 15px;
        height: var(--field);
        font-size: 15px;
    }

    html.mobile-search aside.rail .railsearch input {
        height: var(--field);
        padding: 0 13px 0 40px;
        border: 1px solid transparent;
        border-radius: 12px;
        background: var(--surface-2);
        font-size: 18px;
        font-weight: 400;
    }

    html.mobile-search aside.rail .railsearch input:focus {
        border-color: var(--accent);
        background: var(--bg);
    }

    html.mobile-search aside.rail .fbtn {
        width: 46px;
        height: 46px;
        border-radius: 12px;
        border: 1px solid var(--line);
        font-size: 15px;
    }

    html.mobile-search aside.rail .railfilters.ready {
        display: flex;
        padding: 0 0 14px;
    }

    html.mobile-search aside.rail .railprobs {
        margin-top: 0;
        padding: 6px 0 0;
        border-top-color: var(--line);
    }

    html.mobile-search aside.rail .railprobs .ph {
        padding: 14px 0 8px;
        font-size: 13px;
    }

    html.mobile-search aside.rail .railprobs a {
        padding: 11px 9px;
        margin: 2px 0;
    }

    html.mobile-search aside.rail .railprobs .pt {
        font-size: 16px;
        line-height: 1.35;
    }

    html.mobile-search aside.rail .railprobs .pm,
    html.mobile-search aside.rail .railprobs .px {
        font-size: 13px;
    }

    /* Compact archive hierarchy: clear groups without turning every datum into
       a large, centred card. */
    .content {
        padding-top: 0;
    }

    header.col-12 {
        margin: 26px 0 22px;
        text-align: left;
    }

    header.col-12 h1 {
        max-width: 14ch;
        font-size: clamp(29px, 8vw, 36px);
        line-height: 1.12;
    }

    header.col-12 .about {
        max-width: 42ch;
        margin: 12px 0 0;
        font-size: 15px;
        line-height: 1.55;
    }

    .viewbar {
        justify-content: flex-end;
        flex-wrap: wrap;
        gap: 10px;
        margin-bottom: 18px;
    }

    .viewbar .topicsw {
        order: 1;
        flex: 0 0 100%;
        width: 100%;
        flex-wrap: nowrap;
        overflow-x: auto;
        justify-content: flex-start;
        gap: 18px;
        margin: 0;
        padding: 2px 0 7px;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }

    .viewbar .topicsw::-webkit-scrollbar {
        display: none;
    }

    .viewbar .topicsw button {
        flex: none;
        font-size: 13px;
    }

    .viewsw {
        display: none;
    }

    .viewbar > .desktopmode {
        display: none;
    }

    .mobile-mode {
        order: 0;
    }

    main article.row {
        margin-left: 0;
        margin-right: 0;
        margin-bottom: 20px;
        border-radius: 12px;
        background: var(--bg-alt);
        overflow: hidden;
    }

    main article.row > [class*="col-"] {
        padding-left: 0;
        padding-right: 0;
    }

    main article.row .left-col {
        align-items: flex-start;
        min-height: 0;
        padding: 16px 18px;
        position: relative;
        border-radius: 12px 12px 0 0;
        background: var(--surface-2);
        text-align: left;
    }

    /* Here the flag becomes a compact country chip beside the year. Only
       reserve the right-hand space when this edition actually has a flag. */
    main article.row .left-col:has(img.flag) {
        padding-right: 100px;
    }

    main article.row .left-col img.flag {
        display: block;
        position: absolute;
        top: 50%;
        right: 26px;
        width: auto;
        height: 38px;
        max-width: 64px;
        border-radius: 6px;
        clip-path: inset(1px round 6px);
        transform: translateY(-50%);
        opacity: .9;
        box-shadow: 0 3px 10px var(--shadow);
    }

    main article.row .left-col .year {
        font-size: 31px;
        line-height: 1;
    }

    main article.row .left-col .country {
        margin-top: 5px;
        padding-bottom: 0;
        font-size: 15px;
        line-height: 1.3;
        color: var(--text-2);
    }

    main article.row > [class*="col-"] + [class*="col-"] {
        margin-top: 0;
    }

    main section > hr {
        display: none;
    }

    html.v-cards .probs {
        display: grid;
        grid-template-columns: 1fr;
        gap: 10px;
        margin: 0;
        padding: 8px 18px 14px;
        background: var(--bg-alt);
    }

    html.v-cards .bandhead,
    html.v-cards .rows li {
        align-items: flex-start;
        width: 100%;
        min-height: 0;
        padding: 15px 0 15px 10px;
        border: 0;
        border-radius: 0;
        background: transparent;
        text-align: left;
    }

    html.v-cards .rows li .rtag {
        font-size: 11px;
    }

    html.v-cards .rows li .t,
    html.v-cards .bandhead .rlabel {
        width: 100%;
        font-size: 18px;
        line-height: 1.3;
        text-align: left;
        margin-top: 0;
    }

    html.v-cards .band.one > .bandhead > .rlabel {
        width: auto;
    }

    html.v-cards .bandhead .rlinks,
    html.v-cards .rows li .rlinks {
        width: 100%;
        justify-content: flex-start;
        gap: 18px;
        margin: 0;
        padding-top: 10px;
    }

    /* List view keeps the compact row treatment already defined above. */
    html:not(.v-cards) .band {
        padding-left: 0;
        padding-right: 0;
    }

    footer.col-12 {
        margin-top: 30px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .mobileaction,
    aside.rail {
        transition: none !important;
    }
}

/* Shared papers in cards: the titles, in the same compact rows a topic filter
   brings back. See band() in build.py. */
html.v-cards .band.titled > .cols,
html.v-cards .band.expanded > .cols {
    display: block;
    grid-column: 1 / -1;
    columns: 2;
    column-gap: 28px;
    margin: 2px 0 0;
    padding: 0;
    list-style: none;
}

html.v-cards .band.titled > .cols li,
html.v-cards .band.expanded > .cols li {
    display: flex;
    gap: 8px;
    align-items: center;
    padding: 3px 0;
    border: 0;
    font-size: 14.5px;
    break-inside: avoid;
}

html.v-cards .band.titled > .cols li .n,
html.v-cards .band.expanded > .cols li .n {
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
    flex: none;
    min-width: 30px;
}

html.v-cards .band.titled > .cols li .t,
html.v-cards .band.expanded > .cols li .t {
    flex: 1 1 auto;
    color: var(--text-1b);
    -webkit-line-clamp: none;
    display: block;
}

.showall {
    display: none;
}

html.v-cards .showall {
    display: inline-block;
    grid-column: 1 / -1;
    justify-self: start;
    margin-top: 4px;
    padding: 3px 12px;
    font: inherit;
    font-size: 13px;
    color: var(--text-1b);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: 99px;
    cursor: pointer;
}

@media (max-width: 760px) {
    html.v-cards .band.titled > .cols,
    html.v-cards .band.expanded > .cols {
        columns: 1;
    }
}

/* ---- lists ----
 *
 * See assets/list.js. On the page, the only mark is a faint "+" at the far end
 * of each problem's links -- the links are the problem's actions, and the end
 * of that row is always clear of the title. */
.add {
    flex: none;
    width: 28px;
    height: 28px;
    padding: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: 99px;
    background: none;
    color: var(--text-4);
    cursor: pointer;
    transition: color .12s, background .12s, border-color .12s;
}

.add::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 11px;
    content: "\f067";
}

.add:hover,
.add:focus-visible {
    color: var(--text-max);
    border-color: var(--border);
    background: var(--surface-3);
    outline: none;
}

li:hover .add,
.bandhead:hover .add {
    color: var(--text-2);
}

/* Hover, and a phone's sticky :hover, must not wash out the tick. */
.add.on,
.add.on:hover,
li:hover .add.on,
.bandhead:hover .add.on {
    color: var(--on-accent);
    background: var(--accent);
    border-color: var(--accent);
}

.add.on::before {
    content: "\f00c";
}

.rlinks > .add {
    margin-left: auto;
    align-self: center;
}

/* Cards: the links row spans the cell, so the "+" lands in its corner. */
html.v-cards .rows li .rlinks,
html.v-cards .band.one > .bandhead > .rlinks {
    align-self: stretch;
}

html.v-cards .band.titled > .cols li,
html.v-cards .band.expanded > .cols li {
    gap: 14px;
}

.listpill {
    position: fixed;
    right: 24px;
    bottom: 22px;
    z-index: 61;
    display: none;
    align-items: center;
    gap: 10px;
    padding: 9px 12px 9px 18px;
    border: 0;
    border-radius: 99px;
    background: var(--accent);
    color: var(--on-accent);
    font: inherit;
    font-weight: 600;
    font-size: 14.5px;
    cursor: pointer;
    box-shadow: 0 8px 26px var(--shadow);
}

.listpill.show {
    display: inline-flex;
}

.listpill::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    content: "\f03a";
}

html.ol-open .listpill::before {
    content: "\f00d";
}

.listpill b {
    min-width: 24px;
    padding: 1px 7px;
    border-radius: 99px;
    text-align: center;
    background: rgba(0, 0, 0, .18);
}

.listpill.bump {
    animation: ol-bump .35s ease-out;
}

@keyframes ol-bump {
    40% { transform: scale(1.08); }
}

/* ---- the list panel: a popup on exam pages, the whole of /list/ ---- */
.ol-pop {
    position: fixed;
    right: 24px;
    bottom: 80px;
    z-index: 60;
    width: 440px;
    max-height: min(680px, calc(100vh - 110px));
    display: flex;
    flex-direction: column;
    border: 1px solid var(--border);
    border-radius: 16px;
    background: var(--surface-5);
    box-shadow: 0 18px 50px var(--shadow);
    color: var(--text);
}

.ol-pop[hidden] {
    display: none;
}

.ol {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

.ol-popup {
    flex: 1 1 auto;
}

.ol-shared {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 22px;
    padding: 14px 16px;
    border-radius: 12px;
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
}

.ol-shared[hidden] {
    display: none;
}

.ol-shared p {
    margin: 0;
    font-size: 14.5px;
}

.ol-shared div {
    display: flex;
    gap: 8px;
}

.ol-head {
    display: flex;
    align-items: flex-start;
    gap: 6px;
}

.ol-headtext {
    flex: 1 1 auto;
    min-width: 0;
}

.ol-popup .ol-head {
    padding: 16px 12px 8px 20px;
}

.ol-name {
    width: 100%;
    margin: 0 0 0 -8px;
    padding: 2px 8px;
    font: inherit;
    font-weight: 700;
    color: var(--text-max);
    background: none;
    border: 1px solid transparent;
    border-radius: 8px;
}

.ol-popup .ol-name {
    font-size: 19px;
}

.ol-page .ol-name {
    margin-left: -10px;
    padding: 4px 10px;
    font-size: 32px;
    line-height: 1.2;
}

.ol-name::placeholder {
    color: var(--text-4);
}

.ol-name:not([readonly]):hover,
.ol-name:focus {
    border-color: var(--border);
    outline: none;
}

.ol-meta {
    margin: 2px 0 0;
    font-size: 13.5px;
    color: var(--text-2);
}

.ol-meta:empty {
    display: none;
}

.ol-icon {
    flex: none;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--text-3b);
    background: none;
    border: 0;
    border-radius: 99px;
    cursor: pointer;
}

.ol-icon:hover {
    color: var(--text-max);
    background: var(--surface-3);
    text-decoration: none;
}

.ol-icon::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
}

.ol-full::before { content: "\f065"; }
.ol-close::before { content: "\f00d"; font-size: 15px; }

.ol-items {
    list-style: none;
    margin: 0;
    padding: 0;
}

.ol-items {
    position: relative;
}

.ol-popup .ol-items {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: 2px 8px 6px;
}

.ol-it {
    display: grid;
    grid-template-columns: 24px 1fr auto;
    align-items: center;
    column-gap: 8px;
    margin: 2px 0;
    padding: 10px 8px 10px 4px;
    border-radius: 12px;
    cursor: grab;
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
    transition: background .12s;
}

.ol-page .ol-it {
    padding: 14px 10px 14px 4px;
    margin: 4px -10px 4px -4px;
}

.ol-it:hover,
.ol-it:focus-visible {
    background: color-mix(in srgb, var(--text) 5%, transparent);
    outline: none;
}

.ol-it .ol-prev {
    cursor: auto;
}

/* Dragging: the others glide, the lifted card follows the pointer with no
   lag, and on release it glides too, into the gap. */
.ol-items.sorting .ol-it {
    transition: transform .18s cubic-bezier(.2, .7, .3, 1), background .12s;
}

.ol-items.sorting .ol-it.lift {
    position: relative;
    z-index: 3;
    cursor: grabbing;
    transition: none;
    background: var(--surface-6);
    box-shadow: 0 12px 32px var(--shadow);
}

.ol-items.sorting .ol-it.lift.settle {
    transition: transform .2s cubic-bezier(.2, .7, .3, 1), box-shadow .2s;
    box-shadow: 0 0 0 transparent;
}

.ol-n {
    font-size: 14px;
    font-variant-numeric: tabular-nums;
    color: var(--text-3b);
    text-align: right;
}

.ol-main {
    min-width: 0;
    padding-left: 6px;
}

.ol-title {
    max-width: 100%;
    padding: 0;
    font: inherit;
    font-size: 15.5px;
    font-weight: 500;
    text-align: left;
    color: var(--text-0);
    background: none;
    border: 0;
    cursor: pointer;
}

.ol-page .ol-title {
    font-size: 17px;
}

.ol-title:hover {
    color: var(--accent);
}

.ol-title::after {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 9px;
    content: "\f078";
    margin-left: 8px;
    color: var(--text-4);
    display: inline-block;
    vertical-align: 2px;
    transition: transform .15s;
}

.ol-it.open .ol-title::after {
    transform: rotate(180deg);
}

.ol-src {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
    font-size: 13px;
    color: var(--text-2);
}

.ol-tag {
    font-size: 11px;
    padding: 0 7px;
    border-radius: 99px;
    color: var(--accent);
    border: 1px solid color-mix(in srgb, var(--accent) 60%, transparent);
}

.ol-rm {
    width: 30px;
    height: 30px;
    padding: 0;
    color: var(--text-4);
    background: none;
    border: 0;
    border-radius: 99px;
    cursor: pointer;
}

.ol-rm::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    content: "\f00d";
}

.ol-rm:hover {
    color: var(--text-max);
    background: var(--surface-3);
}

.ol-prev {
    grid-column: 2 / -1;
    margin: 12px 0 4px;
    -webkit-user-select: text;
    user-select: text;
}

.ol-links {
    display: flex;
    gap: 18px;
    margin-bottom: 10px;
    font-size: 13.5px;
}

.ol-pages {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.ol-zoom {
    max-height: 340px;
    overflow: auto;
    border-radius: 6px;
    background: #fff;
    box-shadow: 0 4px 18px var(--shadow);
}

.ol-page .ol-zoom {
    max-height: 560px;
}

.ol-zoom canvas {
    box-shadow: none !important;
    border-radius: 0 !important;
    max-width: none !important;
}

.ol-pages canvas {
    display: block;
    max-width: 100%;
    border-radius: 4px;
    background: #fff;
    box-shadow: 0 4px 18px var(--shadow);
}

.ol-note {
    margin: 0;
    font-size: 13px;
    color: var(--text-3b);
}

.ol-empty {
    padding: 28px 22px;
    font-size: 14.5px;
    color: var(--text-1c);
}

.ol-empty[hidden] {
    display: none;
}

.ol-empty-h {
    font-size: 17px;
    font-weight: 600;
    color: var(--text-max);
}

.ol-plus::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 10px;
    content: "\f067";
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border-radius: 99px;
    border: 1px solid var(--border);
    vertical-align: 1px;
}

/* Actions. In the popup they sit along the bottom, under the list; on the page,
   between the title and the list. */
.ol-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
}

.ol-actions[hidden] {
    display: none;
}

.ol-popup .ol-actions {
    order: 9;
    display: grid;
    grid-template-columns: 1fr 1fr;
    padding: 10px 14px 14px;
}

.ol-popup .ol-actions > .ol-btn,
.ol-popup .ol-dd > .ol-btn {
    width: 100%;
    justify-content: center;
}

.ol-popup .ol-split {
    grid-column: 1 / -1;
}

.ol-page .ol-actions {
    margin: 18px 0 18px;
}

/* On the page the actions sit above the list, where a long list cannot push
   them out of reach; in the popup they stay at its foot, by the thumb. */
.ol-page .ol-shared { order: -3; }
.ol-page .ol-head { order: -2; }
.ol-page .ol-actions { order: -1; }

.ol-btn {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 7px 13px;
    font: inherit;
    font-size: 13.5px;
    color: var(--text-0b);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: 99px;
    cursor: pointer;
    white-space: nowrap;
}

.ol-btn:hover {
    color: var(--text-max);
    border-color: var(--text-4);
}

.ol-btn:disabled {
    opacity: .75;
    cursor: progress;
}

.ol-btn.ol-primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
    font-weight: 600;
}

.ol-btn::before,
.ol-down::after,
.ol-caret::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 12px;
}

.ol-ic-pdf::before { content: "\f1c1"; }
.ol-ic-share::before { content: "\f1e0"; }
.ol-ic-link::before { content: "\f0c1"; }
.ol-ic-tex::before { content: "\f121"; }
.ol-ic-ai::before { content: "\f0d0"; }

.ol-down::after {
    content: "\f078";
    font-size: 9px;
    color: var(--text-3b);
}

/* Download PDF | v : one pill, two targets. */
.ol-split {
    position: relative;
    display: inline-flex;
}

.ol-popup .ol-split {
    flex: 1 1 100%;
}

.ol-popup .ol-split > [data-a="pdf"] {
    flex: 1 1 auto;
    justify-content: center;
}

.ol-split > [data-a="pdf"] {
    border-radius: 99px 0 0 99px;
    padding-right: 14px;
}

.ol-caret {
    border-radius: 0 99px 99px 0;
    padding: 7px 12px 7px 10px;
    border-left-color: color-mix(in srgb, var(--on-accent) 25%, transparent) !important;
}

.ol-caret::before {
    content: "\f078";
    font-size: 10px;
}

.ol-dd {
    position: relative;
}

.ol-menu {
    position: absolute;
    left: 0;
    top: calc(100% + 6px);
    z-index: 70;
    min-width: 240px;
    padding: 6px;
    border-radius: 12px;
    background: var(--surface-6);
    border: 1px solid var(--border);
    box-shadow: 0 14px 34px var(--shadow);
}

.ol-popup .ol-menu {
    top: auto;
    bottom: calc(100% + 6px);
}

.ol-split .ol-menu,
.ol-popup .ol-dd .ol-menu {
    left: auto;
    right: 0;
}

.ol-menu[hidden] {
    display: none;
}

.ol-menu p {
    margin: 4px 8px 6px;
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text-2);
}

.ol-menu hr {
    margin: 5px 4px;
    border: 0;
    border-top: 1px solid var(--border);
}

.ol-menu button {
    display: block;
    width: 100%;
    padding: 8px 10px 8px 30px;
    font: inherit;
    font-size: 14px;
    text-align: left;
    color: var(--text-0b);
    background: none;
    border: 0;
    border-radius: 8px;
    cursor: pointer;
    position: relative;
}

.ol-menu button:hover {
    background: var(--surface-3);
}

.ol-menu [aria-checked="true"]::before {
    position: absolute;
    left: 10px;
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 11px;
    content: "\f00c";
    color: var(--accent);
}

.ol-toast {
    position: fixed;
    left: 50%;
    bottom: 26px;
    z-index: 80;
    transform: translate(-50%, 20px);
    opacity: 0;
    pointer-events: none;
    padding: 10px 18px;
    border-radius: 99px;
    font-size: 14px;
    color: var(--text-max);
    background: var(--surface-6);
    box-shadow: 0 8px 24px var(--shadow);
    transition: opacity .2s, transform .2s;
}

.ol-toast.on {
    opacity: 1;
    transform: translate(-50%, 0);
}

/* The list page. */
.ol-back {
    font-size: 14px;
    color: var(--text-2);
}

.ol-back::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    content: "\f060";
    margin-right: 8px;
    font-size: 12px;
}

#ol-page {
    max-width: 880px;
    margin: 0 auto;
    padding: 34px 24px 80px;
}

@media (max-width: 760px) {
    .listpill {
        right: 16px;
        bottom: calc(16px + env(safe-area-inset-bottom));
    }

    /* A phone: the panel runs along the bottom, most of the screen tall, over
       a dimmed page. No handle and no dragging: it opens from the button and
       closes from its cross, the button, a tap outside, or Back. */
    .ol-pop {
        left: 0;
        right: 0;
        bottom: 0;
        width: auto;
        max-height: 86vh;
        border-radius: 18px 18px 0 0;
        border-width: 1px 0 0;
        padding-bottom: env(safe-area-inset-bottom);
    }

    html.ol-open body::after {
        content: "";
        position: fixed;
        inset: 0;
        z-index: 55;
        background: var(--scrim);
    }

    html.ol-open .listpill {
        display: none;
    }

    #ol-page {
        padding: 22px 16px 70px;
    }

    .ol-page .ol-name {
        font-size: 26px;
    }

    .ol-page .ol-split {
        flex: 1 1 100%;
    }

    .ol-page .ol-split > [data-a="pdf"] {
        flex: 1 1 auto;
        justify-content: center;
    }

    /* Menus become a sheet of their own at the foot of the screen. Written
       against .ol so it outranks the popup's open-upwards rule above. */
    .ol .ol-menu,
    .ol .ol-split .ol-menu,
    .ol-popup .ol-dd .ol-menu {
        position: fixed;
        left: 12px;
        right: 12px;
        top: auto;
        bottom: calc(12px + env(safe-area-inset-bottom));
        min-width: 0;
    }
}

/* ---- alignment: links in fixed slots ----
 *
 * Links used to be pushed against the right edge as a group, so a row with
 * only a Problem link put it where every other row has its Solution, and band
 * headings -- with no "+" after them -- ended one slot further right than the
 * rows beneath. In list view every row and every heading now has the same
 * three slots: Problem, Solution, "+". A heading leaves the last one empty,
 * which is what lines its links up over the rows'. */
html:not(.v-cards) .rlinks {
    display: grid;
    grid-template-columns: [p] 112px [s] minmax(112px, max-content) [x] 28px [a] 28px;
    column-gap: 10px;
    align-items: center;
    justify-content: end;
    flex: none;
}

html:not(.v-cards) .rlinks > a.problem { grid-column: p; grid-row: 1; }
html:not(.v-cards) .rlinks > a.solution { grid-column: s; grid-row: 1; }
html:not(.v-cards) .rlinks > .add { grid-column: a; grid-row: 1; margin: 0; }
html:not(.v-cards) .rlinks > .xtra { grid-column: x; grid-row: 1; margin: 0; }
html:not(.v-cards) .rlinks > .none { grid-column: p / x; grid-row: 1; justify-self: end; }
/* One file for both: it takes both slots rather than overflowing the first. */
html:not(.v-cards) .rlinks > a.k-combined { grid-column: p / a; justify-self: start; }

/* A shared paper's rows: the "+" in its slot, the links -- the paper's, shown
   on the heading -- left out. A row with files of its own shows them. */
.cols li .rlinks {
    display: grid;
}

.cols li:not(.own) .rlinks > :not(.add):not(.xtra) {
    display: none;
}

html:not(.v-cards) .cols li {
    grid-template-columns: 26px minmax(0, 1fr) auto;
}

@media (max-width: 575px) {
    html:not(.v-cards) .rows .rlinks {
        justify-content: start;
    }
}

/* Cards, a round of one: the heading's links span the cell, so its "+" sits
   in the corner like every other cell's. */
html.v-cards .band.one > .bandhead > .rlinks {
    width: 100%;
}

/* Cards, a shared paper's titles: the same three columns as the cells around
   them, so every "+" on the card falls on one of three vertical lines. */
html.v-cards .band.titled > .cols,
html.v-cards .band.expanded > .cols {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: 26px;
    row-gap: 2px;
    columns: auto;
}

html.v-cards .band.titled > .cols li,
html.v-cards .band.expanded > .cols li {
    display: grid;
    grid-template-columns: 30px minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px;
}

html.v-cards .band.titled > .cols li .rlinks,
html.v-cards .band.expanded > .cols li .rlinks {
    display: flex;
}

html.v-cards .band.titled > .cols li .rlinks > :not(.add):not(.xtra),
html.v-cards .band.expanded > .cols li .rlinks > :not(.add):not(.xtra) {
    display: none;
}

@media (max-width: 991px) {
    html.v-cards .band.titled > .cols,
    html.v-cards .band.expanded > .cols {
        grid-template-columns: 1fr;
    }
}


/* ---- the top bar ----
 * Brand, subject tabs, search, theme: on every page, above the content, the
 * way most sites are laid out. The sidebar below it lists the current
 * subject's olympiads and nothing else. Phones keep their own header. */
.topbar {
    /* style.css gives every <header> a top margin, meant for the page title. */
    margin: 0 !important;
    position: sticky;
    top: 0;
    z-index: 90;
    display: flex;
    align-items: center;
    gap: 14px;
    height: 60px;
    padding: 0 20px 0 12px;
    background: color-mix(in srgb, var(--bg) 88%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
}

.tb-brand {
    display: inline-flex;
    color: var(--text-max);
    flex: none;
}

.tb-brand:hover {
    color: var(--text-max);
}

.tb-brand svg {
    height: 20px;
    width: auto;
}

.tb-tabs {
    display: flex;
    align-items: center;
    gap: 2px;
    margin-left: 10px;
    min-width: 0;
}

.tb-tabs > a,
.tb-more > summary {
    padding: 7px 12px;
    border-radius: 99px;
    font-size: 14px;
    color: var(--text-2);
    white-space: nowrap;
    text-decoration: none;
    cursor: pointer;
    list-style: none;
}

.tb-more > summary::-webkit-details-marker {
    display: none;
}

.tb-more > summary::after {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 9px;
    content: "\f078";
    margin-left: 6px;
    vertical-align: 1px;
}

.tb-tabs > a:hover,
.tb-more > summary:hover {
    color: var(--text-max);
    background: var(--surface-3);
    text-decoration: none;
}

.tb-tabs > a.on,
.tb-more.on > summary {
    color: var(--text-max);
    background: var(--surface-3);
    font-weight: 600;
}

.tb-more {
    position: relative;
}

.tb-menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 95;
    min-width: 180px;
    padding: 6px;
    border-radius: 12px;
    background: var(--surface-6);
    border: 1px solid var(--border);
    box-shadow: 0 14px 34px var(--shadow);
}

.tb-menu a {
    display: block;
    padding: 8px 10px;
    border-radius: 8px;
    font-size: 14px;
    color: var(--text-0b);
}

.tb-menu a:hover,
.tb-menu a.on {
    background: var(--surface-3);
    text-decoration: none;
    color: var(--text-max);
}

.tb-search,
.hub-search {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-left: auto;
    width: min(360px, 32vw);
    padding: 8px 10px 8px 14px;
    font: inherit;
    font-size: 14px;
    text-align: left;
    color: var(--text-3b);
    background: var(--surface-3);
    border: 1px solid var(--border);
    border-radius: 99px;
    cursor: text;
}

.tb-search::before,
.hub-search::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    content: "\f002";
}

.tb-search span,
.hub-search span {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tb-search:hover,
.hub-search:hover {
    border-color: var(--text-4);
    color: var(--text-2);
}

.tb-search kbd,
.hub-search kbd,
.sx kbd {
    font: inherit;
    font-size: 11.5px;
    padding: 1px 6px;
    border-radius: 6px;
    color: var(--text-2b);
    background: var(--bg);
    border: 1px solid var(--border);
    box-shadow: none;
}

.tb-mode {
    flex: none;
}

/* Desktop: the top bar replaces the floating opener, the sidebar's own brand
   row, its subject icons, its search, and the view bar's theme button. The
   markup for the old search stays for now, unused. */
@media (min-width: 992px) {
    .opener,
    aside.rail .railtop,
    aside.rail .subjbar,
    aside.rail .railsearch,
    aside.rail .railfilters,
    aside.rail .railprobs,
    .viewbar > .desktopmode {
        display: none !important;
    }

    aside.rail .raillist {
        padding-top: 18px;
    }

    aside.rail .subjname {
        display: block;
        padding: 0 10px 8px;
        font-size: 12.5px;
        font-weight: 600;
        color: var(--text-3b);
    }
}

@media (max-width: 991px) {
    .topbar {
        display: none;
    }

    /* Search is the header's button now, not a field inside the menu. */
    aside.rail .railsearch,
    aside.rail .railfilters,
    aside.rail .railprobs {
        display: none !important;
    }
}

@media (max-width: 1180px) {
    .tb-search {
        width: 44px;
        padding: 8px 0;
        justify-content: center;
        flex: none;
    }

    .tb-search span,
    .tb-search kbd {
        display: none;
    }
}

/* ---- subject hubs and home ---- */
.hub,
.home {
    max-width: 1100px;
    margin: 0 auto;
    padding: 34px 32px 90px;
}

.hub h1,
.home h1 {
    margin: 0;
    font-size: 38px;
    font-weight: 700;
    color: var(--text-max);
}

.hub-sub {
    margin: 10px 0 24px;
    font-size: 16px;
    color: var(--text-2);
}

.hub .hub-search,
.home .hub-search {
    width: min(640px, 100%);
    margin: 0 0 12px;
    padding: 13px 14px 13px 18px;
    font-size: 16px;
}

.hub h2 {
    margin: 36px 0 14px;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-3b);
}

.hub-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 14px;
}

.hub-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 124px;
    padding: 18px 18px 16px;
    overflow: hidden;
    border-radius: 16px;
    background: var(--surface);
    border: 1px solid var(--line-faint);
    color: var(--text);
    text-decoration: none;
    transition: background .15s, border-color .15s, transform .15s;
}

.hub-card:hover {
    background: var(--surface-3);
    border-color: var(--border);
    text-decoration: none;
    color: var(--text);
    transform: translateY(-1px);
}

.hub-ini {
    font-size: 22px;
    font-weight: 700;
    color: var(--text-max);
}

.hub-name {
    font-size: 14px;
    color: var(--text-1b);
    line-height: 1.35;
    padding-right: 40px;
}

.hub-meta {
    margin-top: auto;
    padding-top: 10px;
    font-size: 12.5px;
    color: var(--text-3b);
}

.hub-flag {
    position: absolute;
    top: 16px;
    right: 16px;
    width: 34px;
    border-radius: 4px;
    opacity: .85;
}

.hub-soon {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.hub-soon span {
    padding: 5px 12px;
    border-radius: 99px;
    font-size: 13px;
    color: var(--text-3b);
    border: 1px dashed var(--border);
}

.home h1 {
    font-size: 46px;
    max-width: 16ch;
    line-height: 1.1;
    margin-top: 30px;
}

.home-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
    gap: 14px;
    margin-top: 34px;
}

.home-tile {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 20px;
    border-radius: 16px;
    background: var(--surface);
    border: 1px solid var(--line-faint);
    color: var(--text);
    text-decoration: none;
    transition: background .15s, border-color .15s;
}

.home-tile:hover {
    background: var(--surface-3);
    border-color: var(--border);
    text-decoration: none;
    color: var(--text);
}

.home-t {
    font-size: 20px;
    font-weight: 700;
    color: var(--text-max);
}

.home-t::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    margin-right: 10px;
    font-size: 16px;
    color: var(--accent);
}

.home-tile[data-subj="Physics"] .home-t::before { content: "\f5d2"; }
.home-tile[data-subj="Mathematics"] .home-t::before { content: "\f698"; }
.home-tile[data-subj="Informatics"] .home-t::before { content: "\f121"; }
.home-tile[data-subj="Chemistry"] .home-t::before { content: "\f0c3"; }
.home-tile[data-subj="Biology"] .home-t::before { content: "\f471"; }
.home-tile[data-subj="Astronomy"] .home-t::before { content: "\f753"; }
.home-tile[data-subj="Science"] .home-t::before { content: "\f610"; }
.home-tile[data-subj="Linguistics"] .home-t::before { content: "\f1ab"; }
.home-tile[data-subj="Economics"] .home-t::before { content: "\f201"; }
.home-tile[data-subj="Geography"] .home-t::before { content: "\f57d"; }

.home-m {
    font-size: 13.5px;
    color: var(--text-2);
}

@media (max-width: 760px) {
    .hub,
    .home {
        padding: 22px 16px 70px;
    }

    .hub h1 {
        font-size: 30px;
    }

    .home h1 {
        font-size: 34px;
        margin-top: 10px;
    }
}

/* ---- the search panel ---- */
.sx-wrap {
    position: fixed;
    inset: 0;
    z-index: 200;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    padding: 9vh 16px 16px;
    background: var(--scrim);
}

.sx-wrap[hidden] {
    display: none;
}

html.sx-open body {
    overflow: hidden;
}

.sx {
    width: min(720px, 100%);
    max-height: 78vh;
    display: flex;
    flex-direction: column;
    border-radius: 18px;
    background: var(--surface-5);
    border: 1px solid var(--border);
    box-shadow: 0 30px 80px var(--shadow);
    color: var(--text);
    overflow: hidden;
}

.sx-head {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 14px 16px 10px 20px;
}

.sx-glass::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 15px;
    content: "\f002";
    color: var(--text-3b);
}

.sx-in {
    flex: 1 1 auto;
    min-width: 0;
    padding: 6px 0;
    font: inherit;
    font-size: 18px;
    color: var(--text-max);
    background: none;
    border: 0;
    outline: none;
}

.sx-in::-webkit-search-cancel-button {
    display: none;
}

.sx-esc {
    font: inherit;
    font-size: 12px;
    padding: 3px 8px;
    border-radius: 7px;
    color: var(--text-2b);
    background: var(--bg);
    border: 1px solid var(--border);
    cursor: pointer;
}

.sx-scope {
    display: flex;
    gap: 6px;
    padding: 0 16px 12px 20px;
}

.sx-chip {
    font: inherit;
    font-size: 13px;
    padding: 4px 12px;
    border-radius: 99px;
    color: var(--text-2);
    background: none;
    border: 1px solid var(--border);
    cursor: pointer;
}

.sx-chip[aria-selected="true"] {
    color: var(--on-accent);
    background: var(--accent);
    border-color: var(--accent);
    font-weight: 600;
}

.sx-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow: auto;
    padding: 4px 8px 8px;
}

.sx-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.sx-h {
    padding: 12px 12px 6px;
    font-size: 12px;
    font-weight: 600;
    color: var(--text-3b);
}

.sx-it {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 10px 9px 12px;
    border-radius: 12px;
    cursor: pointer;
}

.sx-it.on {
    background: color-mix(in srgb, var(--accent) 14%, transparent);
}

.sx-ic {
    flex: none;
    width: 30px;
    height: 30px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 9px;
    background: var(--surface-3);
    color: var(--text-2);
}

.sx-ic::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 12px;
}

.sx-ic-p::before { content: "\f15c"; }
.sx-ic-o::before { content: "\f19d"; }

.sx-main {
    flex: 1 1 auto;
    min-width: 0;
}

.sx-t {
    font-size: 15px;
    font-weight: 500;
    color: var(--text-0);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sx-src {
    font-size: 12.5px;
    color: var(--text-2b);
}

.sx-snip {
    margin-top: 3px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--text-2);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.sx-snip mark {
    padding: 0 2px;
    border-radius: 3px;
    color: var(--text-max);
    background: color-mix(in srgb, var(--accent) 35%, transparent);
}

.sx-pdf {
    flex: none;
    font-size: 12.5px;
    padding: 3px 10px;
    border-radius: 99px;
    border: 1px solid var(--border);
    color: var(--text-1b);
    visibility: hidden;
}

.sx-it.on .sx-pdf,
.sx-it:hover .sx-pdf {
    visibility: visible;
}

.sx-pdf:hover {
    color: var(--text-max);
    text-decoration: none;
    border-color: var(--text-4);
}

.sx-it .add {
    flex: none;
}

.sx-status {
    margin: 0;
    padding: 18px 14px;
    font-size: 14px;
    color: var(--text-2);
}

.sx-status:empty {
    display: none;
}

.sx-status b {
    color: var(--text-1b);
    font-weight: 600;
}

.sx-foot {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 10px 20px;
    font-size: 12px;
    color: var(--text-3b);
    background: color-mix(in srgb, var(--bg) 40%, transparent);
}

.sx-foot kbd {
    margin-right: 3px;
}

.sx-ms {
    margin-left: auto;
}

/* Arriving at a problem from a search result. */
.sx-found {
    animation: sx-found 2.2s ease-out;
    border-radius: 10px;
}

@keyframes sx-found {
    0%, 35% { box-shadow: 0 0 0 3px var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
    100% { box-shadow: 0 0 0 3px transparent; background: transparent; }
}

@media (max-width: 760px) {
    .sx-wrap {
        padding: 0;
    }

    .sx {
        width: 100%;
        max-height: 100vh;
        height: 100%;
        border-radius: 0;
        border: 0;
    }

    .sx-foot {
        display: none;
    }

    .sx-pdf {
        visibility: visible;
    }
}

/* ---- extra materials ----
 * Answer sheets, marking schemes and slides sit behind one small button in a
 * problem's links; a year's results, minutes and instructions are a quiet line
 * in its panel. Problems and solutions stay the only things shouting. */
.xtra {
    position: relative;
    flex: none;
    align-self: center;
}

.rlinks > .xtra {
    margin-left: auto;
}

.rlinks > .xtra ~ .add {
    margin-left: 2px;
}

.xtra > summary {
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 99px;
    color: var(--text-4);
    cursor: pointer;
    list-style: none;
}

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

.xtra > summary::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 12px;
    content: "\f141";
}

li:hover .xtra > summary,
.bandhead:hover .xtra > summary {
    color: var(--text-2);
}

.xtra > summary:hover,
.xtra[open] > summary {
    color: var(--text-max);
    background: var(--surface-3);
}

.xmenu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    z-index: 80;
    min-width: 190px;
    padding: 6px;
    border-radius: 12px;
    background: var(--surface-6);
    border: 1px solid var(--border);
    box-shadow: 0 14px 34px var(--shadow);
    text-align: left;
}

.xmenu .xh {
    display: block;
    padding: 4px 10px 6px;
    font-size: 11.5px;
    font-weight: 600;
    color: var(--text-3b);
}

.xmenu a {
    display: block;
    padding: 7px 10px;
    border-radius: 8px;
    font-size: 14px;
    white-space: nowrap;
    color: var(--text-0b);
}

.xmenu a::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 400;
    font-size: 12px;
    content: "\f1c1";
    margin-right: 9px;
    color: var(--text-3b);
}

.xmenu a:hover {
    background: var(--surface-3);
    color: var(--text-max);
    text-decoration: none;
}

/* Cards clip their cells; an open menu must not be. */
html.v-cards .rows li:has(.xtra[open]),
html.v-cards .band:has(.xtra[open]) > .bandhead {
    z-index: 30;
}

.ymats {
    position: relative;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
    margin: 10px 0 0;
    font-size: 13px;
}

.ymats a {
    color: var(--text-2);
}

.ymats a::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 400;
    font-size: 11px;
    content: "\f15c";
    margin-right: 6px;
    color: var(--text-3b);
}

.ymats a:hover {
    color: var(--text-max);
}

/* Cards: a problem's links already fill its cell, so its "more" button takes
   the cell's top corner instead, beside the round tag, and the title keeps
   clear of it. */
html.v-cards .rows li:has(> .rlinks > .xtra),
html.v-cards .band.one > .bandhead:has(> .rlinks > .xtra) {
    position: relative;
}

html.v-cards .rows li > .rlinks > .xtra,
html.v-cards .band.one > .bandhead > .rlinks > .xtra {
    position: absolute;
    top: -6px;
    right: 0;
    margin: 0;
}

html.v-cards .rows li:has(> .rlinks > .xtra) .t,
html.v-cards .band.one > .bandhead:has(> .rlinks > .xtra) .rcount.solo {
    padding-right: 32px;
}

html.v-cards .rows li > .rlinks > .xtra ~ .add,
html.v-cards .band.one > .bandhead > .rlinks > .xtra ~ .add {
    margin-left: auto;
}

/* ---- year order and back to top ---- */
.sortsw {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 5px 12px;
    font: inherit;
    font-size: 12.5px;
    letter-spacing: .02em;
    color: var(--text-2b);
    background: none;
    border: 1px solid var(--border);
    border-radius: 99px;
    cursor: pointer;
    flex: none;
}

.sortsw::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 11px;
    content: "\f160";
}

.sortsw.old::before {
    content: "\f161";
}

.sortsw:hover {
    color: var(--text-1b);
}

@media (max-width: 991px) {
    .sortsw {
        order: 2;
        margin-right: auto;
    }
}

.totop {
    position: fixed;
    right: 24px;
    bottom: 22px;
    z-index: 49;
    width: 42px;
    height: 42px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--text-1b);
    background: var(--surface-5);
    border: 1px solid var(--border);
    border-radius: 99px;
    box-shadow: 0 8px 24px var(--shadow);
    cursor: pointer;
    opacity: 0;
    transform: translateY(10px);
    pointer-events: none;
    transition: opacity .2s, transform .2s, bottom .2s;
}

.totop::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 14px;
    content: "\f062";
}

.totop.show {
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

.totop:hover {
    color: var(--text-max);
}

html.has-list .totop {
    bottom: 76px;
}

@media (max-width: 760px) {
    .totop {
        right: 16px;
        bottom: calc(16px + env(safe-area-inset-bottom));
    }

    html.has-list .totop {
        bottom: calc(70px + env(safe-area-inset-bottom));
    }
}


/* ---- the list panel on a phone: the list gets the room, the page stays put ----
 * The page behind used to scroll whenever a swipe missed the list itself, and
 * the header and actions left the list a third of the screen. */
@media (max-width: 760px) {
    /* The page behind must not scroll. Hiding its overflow did that but broke
       the sticky header, which jumped. Instead nothing outside the list
       accepts a pan: not the dimmed page, not the panel's own header or
       actions; and the list's own scroll cannot chain out to the page. */
    html.ol-open body::after {
        touch-action: none;
    }

    .ol-pop,
    .ol-pop .ol-head,
    .ol-pop .ol-actions {
        touch-action: none;
    }

    .ol-pop {
        height: 92vh;
        height: 92dvh;
        max-height: none;
        overscroll-behavior: contain;
    }

    .ol-popup .ol-items {
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        touch-action: pan-y;
    }

    .ol-popup .ol-head {
        padding: 14px 10px 4px 18px;
    }

    .ol-popup .ol-name {
        font-size: 18px;
    }

    .ol-popup .ol-it {
        padding: 8px 6px 8px 2px;
    }

    /* Actions: the download on its own row, the other four as one row of
       compact buttons, icon above label. */
    .ol-popup .ol-actions {
        grid-template-columns: repeat(4, 1fr);
        gap: 6px;
        padding: 8px 10px calc(10px + env(safe-area-inset-bottom));
    }

    .ol-popup .ol-actions > .ol-btn,
    .ol-popup .ol-dd > .ol-btn {
        flex-direction: column;
        gap: 3px;
        padding: 7px 2px;
        font-size: 11.5px;
        border-radius: 12px;
    }

    .ol-popup .ol-dd > .ol-btn::after {
        display: none;
    }

    .ol-popup .ol-split > .ol-btn {
        padding-top: 10px;
        padding-bottom: 10px;
        font-size: 14px;
    }
}

.ol-short { display: none; }

@media (max-width: 760px) {
    .ol-popup .ol-long { display: none; }
    .ol-popup .ol-short { display: inline; }
}


/* ---- topic filters in search (physics) ---- */
.sx-topics,
.sx-subs {
    display: flex;
    gap: 6px;
    padding: 0 16px 10px 20px;
    overflow-x: auto;
    scrollbar-width: none;
}

.sx-topics::-webkit-scrollbar,
.sx-subs::-webkit-scrollbar {
    display: none;
}

.sx-subs[hidden] {
    display: none;
}

.sx-topic,
.sx-sub {
    flex: none;
    font: inherit;
    font-size: 12.5px;
    padding: 3px 11px;
    border-radius: 99px;
    color: var(--text-2);
    background: var(--surface-3);
    border: 1px solid transparent;
    cursor: pointer;
    white-space: nowrap;
}

.sx-sub {
    font-size: 12px;
    background: none;
    border-color: var(--border);
}

.sx-topic:hover,
.sx-sub:hover {
    color: var(--text-max);
}

.sx-topic[aria-pressed="true"],
.sx-sub[aria-pressed="true"] {
    color: var(--text-max);
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 18%, transparent);
}

/* ---- hub logos ---- */
.hub-logo {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 40px;
    height: 40px;
    border-radius: 10px;
    object-fit: cover;
}

/* ---- the year's own materials: a menu in the corner of its panel ---- */
.xtra.ymore {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 3;
}

.xtra.ymore > summary {
    color: var(--text-3b);
}

.xtra.ymore .xmenu {
    right: auto;
    left: 0;
}

main article.row .left-col {
    position: relative;
}

main article.row:has(.ymore[open]) {
    position: relative;
    z-index: 40;
}

@media (max-width: 991px) {
    .xtra.ymore {
        top: 50%;
        right: 88px;
        transform: translateY(-50%);
    }

    .xtra.ymore .xmenu {
        left: auto;
        right: 0;
    }
}

/* ---- the home page: title on one line, search across the row ---- */
.home h1 {
    max-width: none;
    white-space: nowrap;
    font-size: clamp(28px, 3.6vw, 46px);
}

.home .hub-search {
    width: 100%;
    margin-top: 26px;
}

/* ---- phones and tablets: no keyboard, so no keyboard hints ---- */
@media (hover: none), (max-width: 760px) {
    .tb-search kbd,
    .hub-search kbd,
    .sx-foot {
        display: none;
    }

    .sx-esc {
        width: 32px;
        height: 32px;
        padding: 0;
        border-radius: 99px;
        background: var(--surface-3);
        border: 0;
    }

    .sx-esc span {
        display: none;
    }

    .sx-esc::before {
        font-family: "Font Awesome 5 Free";
        font-weight: 900;
        font-size: 14px;
        content: "\f00d";
        color: var(--text-1b);
    }
}

@media (max-width: 760px) {
    .home h1 {
        white-space: normal;
        font-size: 30px;
    }
}

/* Search panel rows keep their height; only the results scroll. */
.sx-head,
.sx-scope,
.sx-topics,
.sx-subs,
.sx-foot {
    flex: none;
}

.sx-topics,
.sx-subs {
    overflow-y: hidden;
    padding-top: 2px;
}

/* The year menu lives in the panel's column, over the panel's corner. */
main article.row > .col-lg-3 {
    position: relative;
}

main article.row > .col-lg-3 > .xtra.ymore {
    top: 8px;
    right: 23px;
    transform: none;
}

main article.row > .col-lg-3 > .xtra.ymore .xmenu {
    left: 0;
    right: auto;
}

@media (max-width: 991px) {
    main article.row > .col-lg-3 > .xtra.ymore {
        top: 18px;
        right: 96px;
    }

    main article.row > .col-lg-3 > .xtra.ymore .xmenu {
        left: auto;
        right: 0;
    }
}

/* Without the count line, the title sits straight over the search. */
.hub h1 {
    margin-bottom: 22px;
}

.hub-card {
    min-height: 112px;
}

/* The List button's icon swaps from a list to a cross when the panel opens;
   the two glyphs differ in width, which nudged the whole button sideways.
   A fixed box for the icon keeps the button still. */
.listpill::before {
    display: inline-block;
    width: 16px;
    text-align: center;
}

/* Dark logo tiles (OPhO, Physics Brawl) need an edge against a dark card. */
.hub-logo {
    box-shadow: 0 0 0 1px var(--border);
}

/* Opening search (or the list on a phone) locks the page, which removed the
   scrollbar and slid everything sideways by its width. The gutter stays
   reserved whether or not there is a scrollbar in it. */
html {
    scrollbar-gutter: stable;
}

/* ---- the sidebar: every subject, each a section that folds ---- */
aside.rail .subjblock {
    display: block;
    margin: 0 0 4px;
}

aside.rail .subjblock > summary.subjname {
    display: flex !important;
    align-items: center;
    gap: 8px;
    padding: 8px 10px !important;
    border-radius: 8px;
    font-size: 14px !important;
    font-weight: 600;
    color: var(--text-1b) !important;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

aside.rail .subjblock > summary.subjname::-webkit-details-marker {
    display: none;
}

aside.rail .subjblock > summary.subjname::after {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 9px;
    content: "\f054";
    margin-left: auto;
    color: var(--text-3b);
    transition: transform .15s;
}

aside.rail .subjblock[open] > summary.subjname::after {
    transform: rotate(90deg);
}

aside.rail .subjblock > summary.subjname:hover {
    background: var(--surface-3);
    color: var(--text-max) !important;
}

aside.rail .subjblock[open] > summary.subjname {
    color: var(--text-max) !important;
}

aside.rail .subjblock .catgrp {
    padding-left: 8px;
}

@media (min-width: 992px) {
    aside.rail .raillist {
        padding-top: 14px;
    }
}

/* ---- the List button: lighter ---- */
.listpill {
    font-weight: 500;
}

.listpill b {
    min-width: 0;
    padding: 0;
    background: none;
    font-weight: 600;
}

/* Spacing in the folding sidebar: a group heading gets air above it, clear of
   the subject row's hover background, and an open subject ends with a gap
   before the next one. The group label drops its wide letter-spacing, which
   read as a different kind of text from everything around it. */
aside.rail .subjblock > .catgrp:first-of-type,
aside.rail .subjblock > summary + .catgrp {
    margin-top: 18px;
}

aside.rail .subjblock > .catgrp + .catgrp {
    padding-top: 16px;
}

aside.rail .subjblock > .catgrp .cath {
    /* Level with the names under it: same inset as an item's text. */
    padding: 0 10px 6px 10px;
    letter-spacing: .02em;
    font-weight: 600;
    font-size: 12px;
    color: var(--text-3b);
}

aside.rail .subjblock[open] {
    padding-bottom: 14px;
}

aside.rail .subjblock > summary.subjname {
    margin: 2px 0;
}

/* ---- renaming the list: a real button, in the row with the others ---- */
.ol-rename::before {
    content: "\f044";
    font-weight: 400;
    font-size: 14px;
}

/* On the page there is no icon row: the button sits beside the title. */
.ol-page .ol-head {
    gap: 10px;
    align-items: flex-start;
}

.ol-page .ol-rename {
    margin-top: 10px;
}

.ol-head:has(.ol-name[readonly]) .ol-rename {
    display: none;
}

/* ---- a subject's own pages: just that subject, as a plain heading ---- */
aside.rail .subjblock > div.subjname {
    display: block !important;
    padding: 4px 10px 0 !important;
    font-size: 14px !important;
    font-weight: 600;
    color: var(--text-max) !important;
}

aside.rail .subjblock > div.subjname + .catgrp {
    margin-top: 16px;
}

/* ---- home: a tile per subject, over a drawing of it ---- */
/* Each tile is its subject's icon and name over a line drawing of the subject.
   The drawings are monochrome SVG masks, so one hue per subject (--h) gives
   both themes their colour: light ink on a dark tint, or the reverse. */
.home-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 16px;
    margin-top: 30px;
}

.home-tile {
    --ink: oklch(80% .12 var(--h));
    --tint: oklch(21.5% .022 var(--h));
    --glow: oklch(30% .06 var(--h));
    --edge: oklch(30% .035 var(--h));
    --art-op: .5;
    position: relative;
    isolation: isolate;
    overflow: hidden;
    justify-content: flex-end;
    gap: 0;
    min-height: 176px;
    padding: 20px 22px;
    border-radius: 18px;
    background: radial-gradient(120% 130% at 100% 0%, var(--glow), transparent 70%), var(--tint);
    border: 1px solid var(--edge);
    transition: border-color .2s, transform .2s, box-shadow .2s;
}

.home-tile:hover {
    background: radial-gradient(120% 130% at 100% 0%, var(--glow), transparent 70%), var(--tint);
    border-color: color-mix(in srgb, var(--ink) 50%, transparent);
    transform: translateY(-2px);
    box-shadow: 0 10px 28px -12px var(--shadow);
}

/* the drawing: right-aligned, fading out towards the name */
.home-tile::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--ink);
    opacity: var(--art-op);
    -webkit-mask: var(--art) right center / auto 100% no-repeat, linear-gradient(100deg, transparent 12%, #000 62%);
    -webkit-mask-composite: source-in;
    mask: var(--art) right center / auto 100% no-repeat, linear-gradient(100deg, transparent 12%, #000 62%);
    mask-composite: intersect;
    transition: opacity .25s, transform .4s;
}

.home-tile:hover::before {
    opacity: calc(var(--art-op) + .25);
    transform: scale(1.03);
}

.home-t {
    font-size: 22px;
    letter-spacing: -.01em;
}

/* the icon, in a chip at the top left */
.home-t::before {
    position: absolute;
    top: 20px;
    left: 22px;
    display: grid;
    place-items: center;
    width: 40px;
    height: 40px;
    margin: 0;
    border-radius: 12px;
    font-size: 17px;
    color: var(--ink);
    background: color-mix(in srgb, var(--ink) 14%, transparent);
}

.home-tile[data-subj="Physics"] {
    grid-column: 1 / -1;
    min-height: 200px;
}

.home-tile[data-subj="Physics"] .home-t {
    font-size: 26px;
}

@media (prefers-color-scheme: light) {
    html:not(.dark) .home-tile {
        --ink: oklch(52% .15 var(--h));
        --tint: oklch(97.5% .012 var(--h));
        --glow: oklch(93% .045 var(--h));
        --edge: oklch(91% .025 var(--h));
        --art-op: .5;
    }
}

html.light .home-tile {
    --ink: oklch(52% .15 var(--h));
    --tint: oklch(97.5% .012 var(--h));
    --glow: oklch(93% .045 var(--h));
    --edge: oklch(91% .025 var(--h));
    --art-op: .5;
}

@media (max-width: 900px) {
    .home-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* Physics has a row to itself, so an even count leaves the last one alone */
    .home-tile:last-child:nth-child(even) {
        grid-column: 1 / -1;
    }
}

@media (max-width: 560px) {
    .home-grid {
        grid-template-columns: minmax(0, 1fr);
        gap: 12px;
        margin-top: 22px;
    }

    .home-tile,
    .home-tile[data-subj="Physics"] {
        min-height: 128px;
        padding: 16px 18px;
    }

    .home-t::before {
        top: 16px;
        left: 18px;
        width: 36px;
        height: 36px;
        border-radius: 11px;
        font-size: 15px;
    }

    .home-t,
    .home-tile[data-subj="Physics"] .home-t {
        font-size: 21px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .home-tile,
    .home-tile::before {
        transition: none;
    }
}

.home-tile[data-subj="Physics"] {
    --h: 305;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath opacity='0.55' d='M452 100 A91 91 0 0 1 608 100'/%3E%3Cpath opacity='0.55' d='M452 100 A91 91 0 0 0 608 100'/%3E%3Cpath opacity='0.7' d='M452 100 A100 100 0 0 1 608 100'/%3E%3Cpath opacity='0.7' d='M452 100 A100 100 0 0 0 608 100'/%3E%3Cpath opacity='0.8' d='M452 100 A118 118 0 0 1 608 100'/%3E%3Cpath opacity='0.8' d='M452 100 A118 118 0 0 0 608 100'/%3E%3Cpath opacity='0.9' d='M452 100 A150 150 0 0 1 608 100'/%3E%3Cpath opacity='0.9' d='M452 100 A150 150 0 0 0 608 100'/%3E%3Cpath opacity='1' d='M452 100 A220 220 0 0 1 608 100'/%3E%3Cpath opacity='1' d='M452 100 A220 220 0 0 0 608 100'/%3E%3Cpath opacity='1' d='M452 100 A420 420 0 0 1 608 100'/%3E%3Cpath opacity='1' d='M452 100 A420 420 0 0 0 608 100'/%3E%3Cpath d='M452 100 H608'/%3E%3Cpath opacity='0.80' d='M431 94 C390 68, 362.0 62.6, 310 53.8'/%3E%3Cpath opacity='0.80' d='M629 94 C670 68, 685.0 62.6, 724.0 53.8'/%3E%3Cpath opacity='0.62' d='M431 92 C390 42, 338.0 18.400000000000006, 270 -0.8000000000000114'/%3E%3Cpath opacity='0.62' d='M629 92 C670 42, 705.0 18.400000000000006, 756.0 -0.8000000000000114'/%3E%3Cpath opacity='0.44' d='M431 90 C390 10, 302.0 -36.0, 210 -68.0'/%3E%3Cpath opacity='0.44' d='M629 90 C670 10, 735.0 -36.0, 804.0 -68.0'/%3E%3Cpath opacity='0.80' d='M431 106 C390 132, 362.0 137.4, 310 146.2'/%3E%3Cpath opacity='0.80' d='M629 106 C670 132, 685.0 137.4, 724.0 146.2'/%3E%3Cpath opacity='0.62' d='M431 108 C390 158, 338.0 181.6, 270 200.8'/%3E%3Cpath opacity='0.62' d='M629 108 C670 158, 705.0 181.6, 756.0 200.8'/%3E%3Cpath opacity='0.44' d='M431 110 C390 190, 302.0 236.0, 210 268.0'/%3E%3Cpath opacity='0.44' d='M629 110 C670 190, 735.0 236.0, 804.0 268.0'/%3E%3Cpath d='M431 100 H290' opacity='.8'/%3E%3Cpath d='M629 100 H720' opacity='.8'/%3E%3Ccircle cx='440' cy='100' r='10' stroke-width='2.2'/%3E%3Cpath stroke-width='2.2' d='M435.5 100H444.5M440 95.5V104.5'/%3E%3Ccircle cx='440' cy='100' r='24' stroke-dasharray='2 6' opacity='.55'/%3E%3Ccircle cx='440' cy='100' r='40' stroke-dasharray='2 6' opacity='.55'/%3E%3Ccircle cx='620' cy='100' r='10' stroke-width='2.2'/%3E%3Cpath stroke-width='2.2' d='M615.5 100H624.5'/%3E%3Ccircle cx='620' cy='100' r='24' stroke-dasharray='2 6' opacity='.55'/%3E%3Ccircle cx='620' cy='100' r='40' stroke-dasharray='2 6' opacity='.55'/%3E%3Cpath opacity='.7' d='M60 160 L66 164.3 L72 168.3 L78 171.7 L84 174.2 L90 175.7 L96 176 L102 175.1 L108 173.1 L114 170.1 L120 166.4 L126 162.3 L132 157.9 L138 153.7 L144 150 L150 147 L156 145 L162 144 L168 144.3 L174 145.7 L180 148.2 L186 151.6 L192 155.5 L198 159.8 L204 164.1 L210 168.2 L216 171.6 L222 174.1 L228 175.6 L234 176 L240 175.1 L246 173.2 L252 170.3 L258 166.6 L264 162.4 L270 158.1 L276 153.9 L282 150.1 L288 147.1 L294 145'/%3E%3Cpath opacity='.35' stroke-dasharray='3 5' d='M60 169.6 L66 169.2 L72 168.2 L78 166.6 L84 164.4 L90 162 L96 159.4 L102 156.8 L108 154.5 L114 152.6 L120 151.2 L126 150.5 L132 150.5 L138 151.2 L144 152.5 L150 154.4 L156 156.7 L162 159.3 L168 161.9 L174 164.3 L180 166.5 L186 168.2 L192 169.2 L198 169.6 L204 169.3 L210 168.3 L216 166.6 L222 164.5 L228 162.1 L234 159.5 L240 156.9 L246 154.6 L252 152.6 L258 151.3 L264 150.5 L270 150.5 L276 151.1 L282 152.5 L288 154.3 L294 156.6'/%3E%3Cellipse cx='250' cy='52' rx='46' ry='16' transform='rotate(-18 250 52)' opacity='.6'/%3E%3Ccircle cx='250' cy='52' r='4' fill='%23000' opacity='.8'/%3E%3Ccircle cx='292' cy='40' r='3' fill='%23000' opacity='.8'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Mathematics"] {
    --h: 245;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 0V200M40 0V200M60 0V200M80 0V200M100 0V200M120 0V200M140 0V200M160 0V200M180 0V200M200 0V200M220 0V200M240 0V200M260 0V200M280 0V200M300 0V200M320 0V200M340 0V200M0 20H360M0 40H360M0 60H360M0 80H360M0 100H360M0 120H360M0 140H360M0 160H360M0 180H360' stroke-width='.8' opacity='.18'/%3E%3Ccircle cx='240' cy='100' r='76'/%3E%3Cpath stroke-width='2' d='M177.7 56.4 L311.4 74 L224.2 174.3Z'/%3E%3Ccircle cx='236.6' cy='102.4' r='37.9' opacity='.75'/%3E%3Cpath opacity='.7' d='M224.2 174.3L238.7 64.4'/%3E%3Cpath opacity='.7' d='M245.6 65.3L244.7 72.3L237.8 71.4'/%3E%3Cpath opacity='.8' d='M216.9 155.7A20 20 0 0 0 237.3 159.2'/%3E%3Ccircle cx='177.7' cy='56.4' r='3.2' fill='%23000'/%3E%3Ccircle cx='311.4' cy='74' r='3.2' fill='%23000'/%3E%3Ccircle cx='224.2' cy='174.3' r='3.2' fill='%23000'/%3E%3Ccircle cx='238.7' cy='64.4' r='3.2' fill='%23000'/%3E%3Cpath opacity='.55' d='M40 170H150M60 190V70'/%3E%3Cpath opacity='.6' d='M62 164 L66 149.1 L70 135.7 L74 123.7 L78 113.1 L82 104 L86 96.4 L90 90.2 L94 85.5 L98 82.2 L102 80.4 L106 80 L110 81.1 L114 83.7 L118 87.7 L122 93.1 L126 100 L130 108.4 L134 118.2 L138 129.5 L142 142.2 L146 156.4'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Informatics"] {
    --h: 155;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='40' y='34' width='70' height='6' rx='3' fill='%23000' stroke='none' opacity='0.35'/%3E%3Crect x='56' y='52' width='96' height='6' rx='3' fill='%23000' stroke='none' opacity='0.44999999999999996'/%3E%3Crect x='72' y='70' width='58' height='6' rx='3' fill='%23000' stroke='none' opacity='0.55'/%3E%3Crect x='72' y='88' width='84' height='6' rx='3' fill='%23000' stroke='none' opacity='0.35'/%3E%3Crect x='56' y='106' width='30' height='6' rx='3' fill='%23000' stroke='none' opacity='0.44999999999999996'/%3E%3Crect x='56' y='124' width='76' height='6' rx='3' fill='%23000' stroke='none' opacity='0.55'/%3E%3Crect x='72' y='142' width='110' height='6' rx='3' fill='%23000' stroke='none' opacity='0.35'/%3E%3Cpath opacity='.8' d='M246.7 41.6L289.3 32.4'/%3E%3Cpath opacity='.8' d='M306.3 39L331.7 75'/%3E%3Cpath opacity='.8' d='M243.1 52.4L274.9 89.6'/%3E%3Cpath opacity='.8' d='M327.3 86.7L292.7 95.3'/%3E%3Cpath opacity='.8' d='M271.4 101.1L224.6 114.9'/%3E%3Cpath opacity='.8' d='M225.4 46.9L188.6 57.1'/%3E%3Cpath opacity='.8' d='M221.9 125.6L254.1 156.4'/%3E%3Cpath opacity='.8' d='M278.8 108.5L265.2 153.5'/%3E%3Cpath opacity='.8' d='M272.9 162.7L319.1 157.3'/%3E%3Cpath opacity='.8' d='M336.8 94.9L331.2 145.1'/%3E%3Cpath opacity='.8' d='M209.8 128.2L194.2 165.8'/%3E%3Cpath opacity='.8' d='M183.8 69.3L208.2 108.7'/%3E%3Ccircle cx='236' cy='44' r='8' fill='%23000'/%3E%3Ccircle cx='300' cy='30' r='8' stroke-width='2'/%3E%3Ccircle cx='338' cy='84' r='8' stroke-width='2'/%3E%3Ccircle cx='282' cy='98' r='8' fill='%23000'/%3E%3Ccircle cx='214' cy='118' r='8' stroke-width='2'/%3E%3Ccircle cx='262' cy='164' r='8' fill='%23000'/%3E%3Ccircle cx='330' cy='156' r='8' stroke-width='2'/%3E%3Ccircle cx='178' cy='60' r='8' stroke-width='2'/%3E%3Ccircle cx='190' cy='176' r='8' stroke-width='2'/%3E%3Cpath stroke-width='2.4' opacity='.8' d='M58 176l-12-10 12-10M86 176l12-10-12-10M78 152l-12 28'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Chemistry"] {
    --h: 210;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cdefs%3E%3Cpattern id='c' width='27.7' height='48' patternUnits='userSpaceOnUse'%3E%3Cpath d='M0 8L13.9 0L27.7 8V24L13.9 32L0 24ZM13.9 32V48' stroke-width='.8'/%3E%3C/pattern%3E%3C/defs%3E%3Crect width='360' height='200' fill='url(%23c)' stroke='none' opacity='.14'/%3E%3Cpath stroke-width='2.2' d='M214 66L236.5 79'/%3E%3Cpath stroke-width='2.2' d='M236.5 79L236.5 105'/%3E%3Cpath stroke-width='2.2' d='M236.5 105L214 118'/%3E%3Cpath stroke-width='2.2' d='M214 118L191.5 105'/%3E%3Cpath stroke-width='2.2' d='M191.5 105L191.5 79'/%3E%3Cpath stroke-width='2.2' d='M191.5 79L214 66'/%3E%3Cpath stroke-width='2.2' d='M259 66L281.5 79'/%3E%3Cpath stroke-width='2.2' d='M281.5 79L281.5 105'/%3E%3Cpath stroke-width='2.2' d='M281.5 105L259 118'/%3E%3Cpath stroke-width='2.2' d='M259 118L236.5 105'/%3E%3Cpath stroke-width='2.2' d='M236.5 79L259 66'/%3E%3Cpath stroke-width='2.2' d='M281.5 105L304.1 118'/%3E%3Cpath stroke-width='2.2' d='M304.1 118L304.1 144'/%3E%3Cpath stroke-width='2.2' d='M304.1 144L281.5 157'/%3E%3Cpath stroke-width='2.2' d='M281.5 157L259 144'/%3E%3Cpath stroke-width='2.2' d='M259 144L259 118'/%3E%3Cpath stroke-width='1.8' d='M216.5 73.1L229.1 80.4'/%3E%3Cpath stroke-width='1.8' d='M229.1 103.6L216.5 110.9'/%3E%3Cpath stroke-width='1.8' d='M196.4 99.3L196.4 84.7'/%3E%3Cpath stroke-width='1.8' d='M276.6 84.7L276.6 99.3'/%3E%3Cpath stroke-width='1.8' d='M256.6 110.9L243.9 103.6'/%3E%3Cpath stroke-width='1.8' d='M284 112.1L296.7 119.4'/%3E%3Cpath stroke-width='1.8' d='M296.7 142.6L284 149.9'/%3E%3Cpath stroke-width='1.8' d='M264 138.3L264 123.7'/%3E%3Cpath stroke-width='2.2' d='M214 66l0 -26'/%3E%3Ccircle cx='214' cy='32' r='7' stroke-width='2'/%3E%3Cpath stroke-width='2.2' d='M191.5 105l-24 -14l-24 14l-22 -13'/%3E%3Cpath stroke-width='2.2' d='M304.1 144l22 13'/%3E%3Ccircle cx='333.1' cy='161' r='7' stroke-width='2'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Biology"] {
    --h: 125;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath opacity='.55' stroke-width='2' d='M126 123.3L126 110.8'/%3E%3Cpath opacity='.55' stroke-width='2' d='M136 134.7L136 96.1'/%3E%3Cpath opacity='.55' stroke-width='2' d='M146 140.2L146 87.5'/%3E%3Cpath opacity='.55' stroke-width='2' d='M156 137.8L156 86.7'/%3E%3Cpath opacity='.55' stroke-width='2' d='M166 127.7L166 93.6'/%3E%3Cpath opacity='.55' stroke-width='2' d='M176 112.3L176 105.8'/%3E%3Cpath opacity='.55' stroke-width='2' d='M186 101.2L186 113.7'/%3E%3Cpath opacity='.55' stroke-width='2' d='M196 86.5L196 125.1'/%3E%3Cpath opacity='.55' stroke-width='2' d='M206 77.9L206 130.6'/%3E%3Cpath opacity='.55' stroke-width='2' d='M216 77.1L216 128.2'/%3E%3Cpath opacity='.55' stroke-width='2' d='M226 84L226 118.1'/%3E%3Cpath opacity='.55' stroke-width='2' d='M236 96.2L236 102.7'/%3E%3Cpath opacity='.55' stroke-width='2' d='M246 104.1L246 91.6'/%3E%3Cpath opacity='.55' stroke-width='2' d='M256 115.5L256 76.9'/%3E%3Cpath opacity='.55' stroke-width='2' d='M266 121L266 68.3'/%3E%3Cpath opacity='.55' stroke-width='2' d='M276 118.6L276 67.5'/%3E%3Cpath opacity='.55' stroke-width='2' d='M286 108.5L286 74.4'/%3E%3Cpath opacity='.55' stroke-width='2' d='M296 93.1L296 86.6'/%3E%3Cpath opacity='.55' stroke-width='2' d='M306 82L306 94.5'/%3E%3Cpath opacity='.55' stroke-width='2' d='M316 67.3L316 105.9'/%3E%3Cpath opacity='.55' stroke-width='2' d='M326 58.7L326 111.4'/%3E%3Cpath opacity='.55' stroke-width='2' d='M336 57.9L336 109'/%3E%3Cpath opacity='.55' stroke-width='2' d='M346 64.8L346 98.9'/%3E%3Cpath opacity='.55' stroke-width='2' d='M356 77L356 83.5'/%3E%3Cpath opacity='.55' stroke-width='2' d='M366 84.9L366 72.4'/%3E%3Cpath stroke-width='2.6' d='M120 118 L124 123.6 L128 128.9 L132 133.7 L136 137.7 L140 140.8 L144 142.7 L148 143.4 L152 142.7 L156 140.8 L160 137.6 L164 133.3 L168 128 L172 121.9 L176 115.3 L180 108.4 L184 101.5 L188 94.9 L192 88.8 L196 83.5 L200 79.2 L204 76 L208 74.1 L212 73.4 L216 74.1 L220 76 L224 79.1 L228 83.1 L232 87.9 L236 93.2 L240 98.8 L244 104.4 L248 109.7 L252 114.5 L256 118.5 L260 121.6 L264 123.5 L268 124.2 L272 123.5 L276 121.6 L280 118.4 L284 114.1 L288 108.8 L292 102.7 L296 96.1 L300 89.2 L304 82.3 L308 75.7 L312 69.6 L316 64.3 L320 60 L324 56.8 L328 54.9 L332 54.2 L336 54.9 L340 56.8 L344 59.9 L348 63.9 L352 68.7 L356 74 L360 79.6 L364 85.2 L368 90.5'/%3E%3Cpath stroke-width='2.6' opacity='.7' d='M120 118 L124 111.1 L128 104.5 L132 98.4 L136 93.1 L140 88.8 L144 85.6 L148 83.7 L152 83 L156 83.7 L160 85.6 L164 88.7 L168 92.7 L172 97.5 L176 102.8 L180 108.4 L184 114 L188 119.3 L192 124.1 L196 128.1 L200 131.2 L204 133.1 L208 133.8 L212 133.1 L216 131.2 L220 128 L224 123.7 L228 118.4 L232 112.3 L236 105.7 L240 98.8 L244 91.9 L248 85.3 L252 79.2 L256 73.9 L260 69.6 L264 66.4 L268 64.5 L272 63.8 L276 64.5 L280 66.4 L284 69.5 L288 73.5 L292 78.3 L296 83.6 L300 89.2 L304 94.8 L308 100.1 L312 104.9 L316 108.9 L320 112 L324 113.9 L328 114.6 L332 113.9 L336 112 L340 108.8 L344 104.5 L348 99.2 L352 93.1 L356 86.5 L360 79.6 L364 72.7 L368 66.1'/%3E%3Ccircle cx='70' cy='46' r='24' opacity='0.7'/%3E%3Ccircle cx='74.8' cy='42.4' r='8.6' fill='%23000' opacity='0.49'/%3E%3Ccircle cx='112' cy='150' r='17' opacity='0.55'/%3E%3Ccircle cx='115.4' cy='147.4' r='6.1' fill='%23000' opacity='0.39'/%3E%3Ccircle cx='40' cy='150' r='12' opacity='0.4'/%3E%3Ccircle cx='42.4' cy='148.2' r='4.3' fill='%23000' opacity='0.28'/%3E%3Ccircle cx='190' cy='28' r='13' opacity='0.45'/%3E%3Ccircle cx='192.6' cy='26.1' r='4.7' fill='%23000' opacity='0.32'/%3E%3Ccircle cx='330' cy='176' r='15' opacity='0.5'/%3E%3Ccircle cx='333.0' cy='173.8' r='5.4' fill='%23000' opacity='0.35'/%3E%3Cg opacity='.75' transform='translate(300 40) rotate(-30)'%3E%3Cpath d='M-34 0C-20 -22 20 -22 34 0C20 22 -20 22 -34 0Z'/%3E%3Cpath d='M-40 0H30M-8 0l12 -10M-8 0l12 10M10 0l10 -8M10 0l10 8M-24 0l10 -9M-24 0l10 9' stroke-width='1.3'/%3E%3C/g%3E%3C/svg%3E");
}

.home-tile[data-subj="Astronomy"] {
    --h: 278;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cg fill='%23000' stroke='none'%3E%3Ccircle cx='116.6' cy='30.2' r='0.7' opacity='0.39'/%3E%3Ccircle cx='192.9' cy='73.1' r='0.7' opacity='0.85'/%3E%3Ccircle cx='77.3' cy='17.2' r='1.1' opacity='0.39'/%3E%3Ccircle cx='32.7' cy='84.9' r='1.4' opacity='0.42'/%3E%3Ccircle cx='80.4' cy='125.5' r='0.7' opacity='0.67'/%3E%3Ccircle cx='142.8' cy='195.3' r='0.7' opacity='0.66'/%3E%3Ccircle cx='47.9' cy='83.8' r='1.4' opacity='0.41'/%3E%3Ccircle cx='111.1' cy='163.2' r='0.7' opacity='0.41'/%3E%3Ccircle cx='205.6' cy='37.6' r='0.7' opacity='0.65'/%3E%3Ccircle cx='22.6' cy='11.9' r='0.7' opacity='0.62'/%3E%3Ccircle cx='191.4' cy='155.4' r='1.1' opacity='0.67'/%3E%3Ccircle cx='163.1' cy='60' r='0.7' opacity='0.73'/%3E%3Ccircle cx='87.9' cy='114.9' r='1.4' opacity='0.62'/%3E%3Ccircle cx='123.7' cy='89.8' r='1.4' opacity='0.89'/%3E%3Ccircle cx='42.5' cy='83.6' r='0.9' opacity='0.43'/%3E%3Ccircle cx='176' cy='7.8' r='0.7' opacity='0.77'/%3E%3Ccircle cx='206.3' cy='175.1' r='0.9' opacity='0.54'/%3E%3Ccircle cx='126.1' cy='99.3' r='1.1' opacity='0.39'/%3E%3Ccircle cx='33.7' cy='54' r='0.7' opacity='0.38'/%3E%3Ccircle cx='252.5' cy='129.4' r='1.1' opacity='0.51'/%3E%3Ccircle cx='138.9' cy='133.7' r='0.7' opacity='0.87'/%3E%3Ccircle cx='128' cy='122.2' r='1.1' opacity='0.38'/%3E%3Ccircle cx='276.6' cy='25.9' r='0.7' opacity='0.57'/%3E%3Ccircle cx='330.1' cy='99.3' r='0.7' opacity='0.60'/%3E%3Ccircle cx='197.8' cy='176.7' r='1.1' opacity='0.83'/%3E%3Ccircle cx='100.2' cy='83.1' r='0.9' opacity='0.73'/%3E%3Ccircle cx='137' cy='46.2' r='0.7' opacity='0.45'/%3E%3Ccircle cx='83.5' cy='46.7' r='1.1' opacity='0.81'/%3E%3Ccircle cx='65.6' cy='56.4' r='0.7' opacity='0.58'/%3E%3Ccircle cx='132.9' cy='113.3' r='0.7' opacity='0.73'/%3E%3Ccircle cx='185.6' cy='123.5' r='0.7' opacity='0.60'/%3E%3Ccircle cx='313.6' cy='190.4' r='1.4' opacity='0.57'/%3E%3Ccircle cx='143.6' cy='20.7' r='1.1' opacity='0.38'/%3E%3Ccircle cx='24.2' cy='41.8' r='0.7' opacity='0.41'/%3E%3Ccircle cx='216.3' cy='20.5' r='1.4' opacity='0.43'/%3E%3Ccircle cx='36.5' cy='72.7' r='0.7' opacity='0.39'/%3E%3Ccircle cx='74.9' cy='75.2' r='0.9' opacity='0.88'/%3E%3Ccircle cx='216.8' cy='94.8' r='0.7' opacity='0.82'/%3E%3Ccircle cx='357.5' cy='93.2' r='1.1' opacity='0.52'/%3E%3Ccircle cx='51.9' cy='149.9' r='0.9' opacity='0.61'/%3E%3Ccircle cx='249.1' cy='103.3' r='0.7' opacity='0.87'/%3E%3Ccircle cx='190.2' cy='29.3' r='1.4' opacity='0.85'/%3E%3Ccircle cx='272.9' cy='59.6' r='0.7' opacity='0.73'/%3E%3Ccircle cx='94' cy='73.3' r='0.7' opacity='0.55'/%3E%3Ccircle cx='80.2' cy='108.3' r='1.4' opacity='0.53'/%3E%3Ccircle cx='80.3' cy='162.3' r='0.7' opacity='0.79'/%3E%3Ccircle cx='294.6' cy='148' r='0.7' opacity='0.46'/%3E%3Ccircle cx='177.4' cy='146.2' r='0.7' opacity='0.78'/%3E%3Ccircle cx='170' cy='38.7' r='1.4' opacity='0.88'/%3E%3Ccircle cx='161' cy='187.4' r='0.9' opacity='0.88'/%3E%3Ccircle cx='131.3' cy='44.1' r='0.7' opacity='0.61'/%3E%3Ccircle cx='121.6' cy='96.5' r='1.4' opacity='0.81'/%3E%3Ccircle cx='172.6' cy='130.6' r='0.7' opacity='0.81'/%3E%3Ccircle cx='43.2' cy='77.7' r='0.7' opacity='0.61'/%3E%3Ccircle cx='64.3' cy='157.8' r='0.9' opacity='0.40'/%3E%3Ccircle cx='340.6' cy='144.4' r='1.1' opacity='0.57'/%3E%3Ccircle cx='340.8' cy='145' r='0.7' opacity='0.90'/%3E%3Ccircle cx='9.9' cy='118.2' r='1.1' opacity='0.79'/%3E%3Ccircle cx='52.6' cy='165.3' r='1.1' opacity='0.71'/%3E%3Ccircle cx='126.1' cy='109.7' r='0.7' opacity='0.36'/%3E%3C/g%3E%3Cpath fill='%23000' stroke='none' d='M120 32Q120 40 128 40Q120 40 120 48Q120 40 112 40Q120 40 120 32Z'/%3E%3Cpath fill='%23000' stroke='none' d='M344 112Q344 118 350 118Q344 118 344 124Q344 118 338 118Q344 118 344 112Z'/%3E%3Cpath fill='%23000' stroke='none' d='M200 167Q200 172 205 172Q200 172 200 177Q200 172 195 172Q200 172 200 167Z'/%3E%3Cpath fill='%23000' stroke='none' d='M60 115Q60 120 65 120Q60 120 60 125Q60 120 55 120Q60 120 60 115Z'/%3E%3Cpath stroke-width='1.3' opacity='.6' d='M168 70 L200 62 L226 74 L252 88L262 122L310 128L318 90L252 88'/%3E%3Ccircle cx='168' cy='70' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='200' cy='62' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='226' cy='74' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='252' cy='88' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='262' cy='122' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='310' cy='128' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='318' cy='90' r='3.2' fill='%23000' stroke='none'/%3E%3Ccircle cx='300' cy='40' r='15' stroke-width='2'/%3E%3Cellipse cx='300' cy='40' rx='30' ry='8' transform='rotate(-20 300 40)' stroke-width='1.8'/%3E%3Cellipse cx='110' cy='160' rx='90' ry='26' transform='rotate(-8 110 160)' stroke-dasharray='2 5' opacity='.55'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Science"] {
    --h: 62;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath stroke-width='2.4' d='M244 22H284M250 22V70L206 168Q203 178 214 178H314Q325 178 322 168L278 70V22'/%3E%3Cpath stroke-width='2' opacity='.8' d='M225 126Q240 120 255 126T285 126T306 126'/%3E%3Cpath stroke-width='1.3' opacity='.5' d='M251 92H259M251 106H256'/%3E%3Ccircle cx='244' cy='150' r='5' opacity='.75'/%3E%3Ccircle cx='266' cy='142' r='3.5' opacity='.75'/%3E%3Ccircle cx='286' cy='158' r='6' opacity='.75'/%3E%3Ccircle cx='258' cy='164' r='3' opacity='.75'/%3E%3Ccircle cx='270' cy='102' r='3.2' opacity='.75'/%3E%3Ccircle cx='266' cy='84' r='4.2' opacity='.75'/%3E%3Ccircle cx='272' cy='58' r='2.6' opacity='.75'/%3E%3Ccircle cx='110' cy='92' r='6' fill='%23000' stroke='none'/%3E%3Cellipse cx='110' cy='92' rx='56' ry='19' transform='rotate(0 110 92)' stroke-width='1.8'/%3E%3Cellipse cx='110' cy='92' rx='56' ry='19' transform='rotate(60 110 92)' stroke-width='1.8'/%3E%3Cellipse cx='110' cy='92' rx='56' ry='19' transform='rotate(120 110 92)' stroke-width='1.8'/%3E%3Ccircle cx='158.5' cy='101.5' r='4' fill='%23000' stroke='none'/%3E%3Ccircle cx='89.3' cy='43.2' r='4' fill='%23000' stroke='none'/%3E%3Ccircle cx='110.2' cy='124.5' r='4' fill='%23000' stroke='none'/%3E%3Cg opacity='.6'%3E%3Ccircle cx='340' cy='60' r='16' stroke-width='2'/%3E%3Cpath stroke-width='3' d='M352 72l12 12'/%3E%3C/g%3E%3C/svg%3E");
}

.home-tile[data-subj="Linguistics"] {
    --h: 350;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath opacity='.5' d='M150 26H250Q262 26 262 38V76Q262 88 250 88H186L170 102V88H150Q138 88 138 76V38Q138 26 150 26Z'/%3E%3Cpath opacity='.5' d='M236 116H330Q342 116 342 128V164Q342 176 330 176H318V190L302 176H236Q224 176 224 164V128Q224 116 236 116Z'/%3E%3Cpath stroke-width='3' d='M160 78L178 36L196 78M166 64H190M210 38Q220 38 224 50L240 80M230 58L212 80M252 128V166M238 128Q250 146 252 147Q254 146 266 128M236 166Q246 150 252 147Q258 150 268 166M296 124V132M280 138H314M286 138Q292 158 318 170M308 138Q300 158 276 170M92 34L128 82M126 32Q128 48 110 55M107 58Q96 64 96 82M88 82H104M120 82H134M190 144H160Q158 164 174 166Q188 166 190 152Q190 128 172 128Q162 128 160 136'/%3E%3Cpath opacity='.55' stroke-width='2' d='M154 124L144 170M206 124L196 170'/%3E%3Cpath opacity='.45' stroke-dasharray='1 7' stroke-width='2.4' d='M24 94H130'/%3E%3C/svg%3E");
}

.home-tile[data-subj="Economics"] {
    --h: 25;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='40' y='140' width='16' height='40' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Crect x='64' y='118' width='16' height='62' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Crect x='88' y='128' width='16' height='52' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Crect x='112' y='96' width='16' height='84' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Crect x='136' y='104' width='16' height='76' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Crect x='160' y='72' width='16' height='108' rx='2' fill='%23000' fill-opacity='.18' opacity='.7'/%3E%3Cpath opacity='.8' d='M48 128L72 108L96 116L120 86L144 94L168 58L184 46M174 44H186V56' stroke-width='2'/%3E%3Cpath stroke-width='1.8' d='M214 24V176H344'/%3E%3Cpath stroke-width='2.4' d='M228 50Q262 110 336 160'/%3E%3Cpath stroke-width='2.4' d='M228 160Q280 130 334 40'/%3E%3Cpath stroke-dasharray='3 4' opacity='.65' d='M214 115.7H280.3V176'/%3E%3Ccircle cx='280.3' cy='115.7' r='4.2' fill='%23000'/%3E%3Cg opacity='.55'%3E%3Cellipse cx='100' cy='30' rx='16' ry='5'/%3E%3Cpath d='M84 30V38Q100 46 116 38V30M84 38V46Q100 54 116 46V38'/%3E%3C/g%3E%3C/svg%3E");
}

.home-tile[data-subj="Geography"] {
    --h: 182;
    --art: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 360 200' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath opacity='0.75' stroke-width='1.3' d='M126.2 130 L126.6 132.3 L125.6 134.5 L123.9 136.4 L122 138.1 L120.1 139.6 L117.8 140.8 L115.2 141.4 L112.4 141.1 L110 140.3 L107.9 139.7 L105.6 139.6 L102.8 140 L99.4 140.1 L96.3 139.2 L94.3 137.3 L94 134.7 L95 132.1 L96.2 130 L97 128.2 L97.4 126.3 L97.9 124.4 L99 122.6 L100.8 121.2 L102.8 120 L104.9 118.8 L107.2 117.5 L110 116.3 L113.1 116 L115.9 117 L117.8 119.2 L118.7 121.7 L119.2 123.8 L120.3 125.3 L122.2 126.5 L124.5 128 L126.2 130Z'/%3E%3Cpath opacity='0.67' stroke-width='1.3' d='M146.1 130 L148.4 135.4 L146.6 140.7 L141.5 144.5 L135.1 146.9 L129.3 148.4 L124.6 150.2 L120.1 152.3 L115.3 154 L110 154.6 L104.7 154 L99.5 153 L94 152.2 L87.6 151.3 L81.2 149.3 L76.4 145.5 L75 140.2 L77.1 134.6 L81.1 130 L84.4 126.4 L85.5 122.9 L85.2 118.5 L85.3 113.4 L87.5 108.6 L92.1 105.1 L97.9 103.4 L104 102.8 L110 102.6 L116 102.8 L121.8 104.2 L126.5 107.2 L129.4 111.5 L131 115.9 L132.7 119.5 L136.1 122.4 L141.1 125.6 L146.1 130Z'/%3E%3Cpath opacity='0.59' stroke-width='1.3' d='M166.9 130 L170.7 138.6 L169.3 147.3 L161.9 154 L150.7 157.3 L139.7 158.3 L131.1 159.2 L124.6 162.2 L118.2 167.1 L110 171.9 L100.3 174.1 L90.5 172.8 L81.9 169 L74.2 164.1 L67 158.8 L60.8 152.7 L57.1 145.4 L57.6 137.4 L61.9 130 L67.3 124 L70.6 118.5 L70.9 112 L70.3 103.4 L72.2 93.9 L78.6 86.4 L88.7 83.2 L99.9 84.4 L110 87.9 L118.5 91.4 L126.4 93.9 L134.4 96.2 L141.7 99.8 L147.5 104.8 L151.7 110.7 L155.8 116.7 L161 122.8 L166.9 130Z'/%3E%3Cpath opacity='0.51' stroke-width='1.3' d='M189.5 130 L190 141.3 L187.8 152.6 L180.4 162.5 L168 168.9 L153.4 171.4 L140.4 172.1 L130.4 174.8 L121.3 181.3 L110 190.1 L95.3 196.7 L79.4 197.2 L66.1 190.8 L57.5 180 L52.5 168.6 L48.5 158.4 L44 149.2 L40.3 139.8 L39.5 130 L42.3 120.5 L46.8 111.6 L50.5 102.5 L53.1 91.8 L57 79.5 L65.4 68.2 L79.1 62 L95.3 63.1 L110 70.1 L121.3 78.5 L130.7 84.4 L141.1 86.8 L154 88.1 L167.4 91.4 L178.4 98.4 L185 108.2 L188 119 L189.5 130Z'/%3E%3Cpath opacity='0.43' stroke-width='1.3' d='M217.5 130 L208.6 143.9 L200.5 156.3 L192.9 168.3 L183.2 179.1 L170.1 187.3 L154.9 192.3 L140 196 L125.8 201.5 L110 209.8 L90.6 217.9 L68.9 220.3 L49.7 213.5 L38 198.6 L34.6 180.6 L35 164.6 L33.2 152.4 L26.5 141.8 L17.5 130 L11.6 116.1 L12.5 101.6 L19.7 88.3 L30 76.3 L41.6 64.8 L54.9 53.7 L71.6 45.6 L91 44 L110 49.8 L125.4 60.3 L137.3 70 L149.7 75 L166.6 76.1 L187.8 77.8 L208.2 84.6 L221.1 97.6 L223.5 114 L217.5 130Z'/%3E%3Cpath opacity='0.35' stroke-width='1.3' d='M251.1 130 L232.1 147.2 L213.7 160.2 L201.6 172.3 L194.6 186.8 L186.7 203.1 L173.4 217.8 L154.4 227.7 L132.7 232.8 L110 235.6 L86.3 237.7 L61.2 237.3 L37.4 230.6 L20.1 215.7 L13.1 195.1 L14 174.3 L15.4 157.5 L9.8 144.1 L-3.9 130 L-19 111.8 L-26 90.4 L-19.2 70.3 L-0.8 55.6 L22.8 46.8 L45.9 41.1 L67.2 36 L88.4 31.9 L110 31.6 L130.5 36.8 L148.4 45.6 L164.9 53.9 L184.1 59.4 L208.4 63.9 L234.9 72.3 L255 87.8 L260.9 108.7 L251.1 130Z'/%3E%3Ccircle cx='274' cy='96' r='72' stroke-width='2.2'/%3E%3Cellipse cx='274' cy='96' rx='23' ry='72' opacity='.75'/%3E%3Cellipse cx='274' cy='96' rx='50.4' ry='72' opacity='.75'/%3E%3Cpath opacity='.75' d='M274 24V168'/%3E%3Cpath opacity='.75' d='M227.7 40.8H320.3'/%3E%3Cpath opacity='.75' d='M208.7 65.6H339.3'/%3E%3Cpath opacity='.75' d='M202 96H346'/%3E%3Cpath opacity='.75' d='M208.7 126.4H339.3'/%3E%3Cpath opacity='.75' d='M227.7 151.2H320.3'/%3E%3Cpath stroke-dasharray='3 5' stroke-width='1.8' d='M150 60Q190 20 236 62'/%3E%3Cpath stroke-width='2' d='M236 70C230 62 228 58 228 54A8 8 0 0 1 244 54C244 58 242 62 236 70Z'/%3E%3Ccircle cx='236' cy='54' r='2.4' fill='%23000'/%3E%3C/svg%3E");
}

/* ---- the list page on a wide screen: the list, and beside it the PDF ----
 * The page was one narrow column in a wide window. Now the list and its actions
 * keep the left, and the right holds what Download PDF would produce, built as
 * the list changes (list.js, bookPane). Below 1100px, and in the popup, there
 * is no preview: the list keeps the whole width. */
.ol-book {
    display: none;
}

@media (min-width: 1100px) {
    #ol-page {
        max-width: 1400px;
    }

    #ol-page.ol-with-book {
        display: grid;
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
        column-gap: 48px;
        align-items: start;
        padding: 26px 32px 48px;
    }

    .ol-book {
        position: sticky;
        top: 20px;
        display: flex;
        flex-direction: column;
        /* The bar and the page's top padding sit above it at rest; stuck, it
           keeps the same height, so it never runs past the window. */
        height: calc(100vh - 96px);
        min-height: 420px;
        border-radius: 18px;
        background: var(--surface);
        overflow: hidden;
    }

    /* Beside the preview the actions keep to one row: the AI button takes the
       short label the phone's panel uses. */
    .ol-page .ol-long { display: none; }
    .ol-page .ol-short { display: inline; }
}

.ol-bookhead {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 12px 10px 20px;
}

.ol-booktitle {
    display: flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    margin: 0;
    font-size: 13.5px;
    color: var(--text-2);
    white-space: nowrap;
}

.ol-booktitle b {
    font-weight: 600;
    color: var(--text-0);
}

.ol-bookcount {
    font-variant-numeric: tabular-nums;
}

.ol-bookbusy {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    margin-left: 8px;
    font-size: 12.5px;
    color: var(--text-3b);
}

.ol-bookbusy[hidden] {
    display: none;
}

.ol-bookbusy::before {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 99px;
    border: 1.5px solid var(--text-5);
    border-top-color: var(--accent);
    animation: ol-spin .8s linear infinite;
}

@keyframes ol-spin {
    to { transform: rotate(360deg); }
}

/* Problems / with solutions: the same choice as the menu by Download PDF. */
.ol-seg {
    flex: none;
    display: inline-flex;
    padding: 3px;
    border-radius: 99px;
    background: var(--surface-3);
}

.ol-seg button {
    padding: 4px 12px;
    font: inherit;
    font-size: 12.5px;
    color: var(--text-2);
    background: none;
    border: 0;
    border-radius: 99px;
    cursor: pointer;
    transition: background .15s, color .15s;
}

.ol-seg button:focus {
    outline: none;
}

.ol-seg button:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -2px;
}

.ol-seg button:hover {
    color: var(--text-max);
}

.ol-seg button[aria-checked="true"] {
    color: var(--text-max);
    background: var(--surface-6);
}

.ol-bookscroll {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    scrollbar-gutter: stable both-edges;
    overscroll-behavior: contain;
    padding: 6px 18px 28px;
    /* Pages slip under the header softly rather than being cut on a line. */
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 12px);
    mask-image: linear-gradient(to bottom, transparent 0, #000 12px);
}

.ol-bookpages {
    max-width: 640px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.ol-offstage {
    position: absolute;
    top: 0;
    left: 0;
    visibility: hidden;
    pointer-events: none;
}

.ol-bpage {
    width: 100%;
    border-radius: 4px;
    background: #fff;
    box-shadow: 0 2px 14px var(--shadow);
    overflow: hidden;
}

.ol-bpage:not(.done) {
    background: color-mix(in srgb, #fff 92%, var(--surface));
}

.ol-bpage canvas {
    display: block;
    width: 100%;
    height: 100%;
}

/* A rebuild keeps the old pages, a little faded; a quick one never fades. */
.ol-bookpages {
    transition: opacity .2s;
}

.ol-updating .ol-bookpages {
    opacity: .55;
    transition: opacity .25s .3s;
}

/* Loading: the shape of a contents page, shimmering, and what is happening. */
.ol-bookstate:empty {
    display: none;
}

.ol-ghost {
    display: flex;
    flex-direction: column;
    max-width: 640px;
    margin: 0 auto;
    aspect-ratio: 595 / 842;
    padding: 9% 9%;
    border-radius: 4px;
    background: var(--surface-3);
}

.ol-ghost i {
    display: block;
    height: 9px;
    margin-bottom: 20px;
    border-radius: 99px;
    background: linear-gradient(90deg, var(--surface-5) 0%, var(--surface-6) 45%, var(--surface-5) 90%);
    background-size: 300% 100%;
    animation: ol-shimmer 1.4s ease-in-out infinite;
}

.ol-ghost i:first-child {
    width: 55%;
    height: 18px;
    margin-bottom: 14px;
}

.ol-ghost i:nth-child(2) {
    width: 38%;
    height: 7px;
    margin-bottom: 36px;
}

.ol-ghost i:nth-child(3) { width: 88%; }
.ol-ghost i:nth-child(4) { width: 76%; }
.ol-ghost i:nth-child(5) { width: 84%; }
.ol-ghost i:nth-child(6) { width: 70%; }
.ol-ghost i:nth-child(7) { width: 80%; }

@keyframes ol-shimmer {
    from { background-position: 100% 0; }
    to { background-position: 0 0; }
}

.ol-ghost .ol-booknote {
    margin: auto 0;
    padding-bottom: 14%;
    text-align: center;
    font-size: 13px;
    color: var(--text-3b);
}

.ol-bookmsg {
    max-width: 340px;
    margin: 18vh auto 0;
    text-align: center;
    font-size: 14px;
    color: var(--text-2);
}

.ol-bookmsg p {
    margin: 0 0 14px;
    font-size: 14px;
    line-height: 1.5;
}

.ol-bookmsg .ol-bookmsg-h {
    font-size: 16px;
    font-weight: 600;
    color: var(--text-max);
    margin-bottom: 6px !important;
}

@media (prefers-reduced-motion: reduce) {
    .ol-ghost i,
    .ol-bookbusy::before {
        animation: none;
    }
}

/* A shared paper whose titles are listed in the card spans the whole row, with
   or without a topic filter. Filtering turns a shared band into a card of its
   own, one grid cell wide -- right for the old heading-only card, but it
   squeezed the three columns of titles into a third of the row. */
html.v-cards .band.titled:not(.per),
html.v-cards .band.expanded:not(.per) {
    grid-column: 1 / -1;
}

html[class*="t-"].v-cards .band.titled:not(.per) > .cols,
html[class*="t-"].v-cards .band.expanded:not(.per) > .cols {
    font-size: inherit;
    margin-top: 8px;
}

/* The topic and subtopic filters hide non-matching titles with display:none;
   the card layouts above set a display of their own on the same rows and won.
   These restate the filters with the priority they need. */
html.t-mechanics .cols li:not([data-t~="mechanics"]),
html.t-electromagnetism .cols li:not([data-t~="electromagnetism"]),
html.t-thermodynamics .cols li:not([data-t~="thermodynamics"]),
html.t-waves-optics .cols li:not([data-t~="waves-optics"]),
html.t-modern-quantum .cols li:not([data-t~="modern-quantum"]),
html.t-relativity .cols li:not([data-t~="relativity"]),
html.t-astrophysics .cols li:not([data-t~="astrophysics"]),
html.s-atomic .cols li:not([data-s~="atomic"]),
html.s-circuits .cols li:not([data-s~="circuits"]),
html.s-cosmology .cols li:not([data-s~="cosmology"]),
html.s-diffraction .cols li:not([data-s~="diffraction"]),
html.s-dynamics .cols li:not([data-s~="dynamics"]),
html.s-electrostatics .cols li:not([data-s~="electrostatics"]),
html.s-em-waves .cols li:not([data-s~="em-waves"]),
html.s-entropy .cols li:not([data-s~="entropy"]),
html.s-fluids .cols li:not([data-s~="fluids"]),
html.s-gas-laws .cols li:not([data-s~="gas-laws"]),
html.s-general-relativity .cols li:not([data-s~="general-relativity"]),
html.s-geometrical-optics .cols li:not([data-s~="geometrical-optics"]),
html.s-gravitation .cols li:not([data-s~="gravitation"]),
html.s-heat-transfer .cols li:not([data-s~="heat-transfer"]),
html.s-induction .cols li:not([data-s~="induction"]),
html.s-interference .cols li:not([data-s~="interference"]),
html.s-kinematics .cols li:not([data-s~="kinematics"]),
html.s-kinetic-theory .cols li:not([data-s~="kinetic-theory"]),
html.s-magnetostatics .cols li:not([data-s~="magnetostatics"]),
html.s-nuclear .cols li:not([data-s~="nuclear"]),
html.s-observational .cols li:not([data-s~="observational"]),
html.s-orbital-mechanics .cols li:not([data-s~="orbital-mechanics"]),
html.s-oscillations .cols li:not([data-s~="oscillations"]),
html.s-particle .cols li:not([data-s~="particle"]),
html.s-phase-transitions .cols li:not([data-s~="phase-transitions"]),
html.s-polarization .cols li:not([data-s~="polarization"]),
html.s-quantum-mechanics .cols li:not([data-s~="quantum-mechanics"]),
html.s-rigid-body .cols li:not([data-s~="rigid-body"]),
html.s-solid-state .cols li:not([data-s~="solid-state"]),
html.s-sound .cols li:not([data-s~="sound"]),
html.s-special-relativity .cols li:not([data-s~="special-relativity"]),
html.s-statics .cols li:not([data-s~="statics"]),
html.s-statistical-mechanics .cols li:not([data-s~="statistical-mechanics"]),
html.s-stellar .cols li:not([data-s~="stellar"]),
html.s-wave-propagation .cols li:not([data-s~="wave-propagation"]) {
    display: none !important;
}

/* ...and a round with nothing left in it goes too, heading and links. */
html.t-mechanics .band:has(> .cols, > .rows):not(:has(li[data-t~="mechanics"])),
html.t-electromagnetism .band:has(> .cols, > .rows):not(:has(li[data-t~="electromagnetism"])),
html.t-thermodynamics .band:has(> .cols, > .rows):not(:has(li[data-t~="thermodynamics"])),
html.t-waves-optics .band:has(> .cols, > .rows):not(:has(li[data-t~="waves-optics"])),
html.t-modern-quantum .band:has(> .cols, > .rows):not(:has(li[data-t~="modern-quantum"])),
html.t-relativity .band:has(> .cols, > .rows):not(:has(li[data-t~="relativity"])),
html.t-astrophysics .band:has(> .cols, > .rows):not(:has(li[data-t~="astrophysics"])),
html.s-atomic .band:has(> .cols, > .rows):not(:has(li[data-s~="atomic"])),
html.s-circuits .band:has(> .cols, > .rows):not(:has(li[data-s~="circuits"])),
html.s-cosmology .band:has(> .cols, > .rows):not(:has(li[data-s~="cosmology"])),
html.s-diffraction .band:has(> .cols, > .rows):not(:has(li[data-s~="diffraction"])),
html.s-dynamics .band:has(> .cols, > .rows):not(:has(li[data-s~="dynamics"])),
html.s-electrostatics .band:has(> .cols, > .rows):not(:has(li[data-s~="electrostatics"])),
html.s-em-waves .band:has(> .cols, > .rows):not(:has(li[data-s~="em-waves"])),
html.s-entropy .band:has(> .cols, > .rows):not(:has(li[data-s~="entropy"])),
html.s-fluids .band:has(> .cols, > .rows):not(:has(li[data-s~="fluids"])),
html.s-gas-laws .band:has(> .cols, > .rows):not(:has(li[data-s~="gas-laws"])),
html.s-general-relativity .band:has(> .cols, > .rows):not(:has(li[data-s~="general-relativity"])),
html.s-geometrical-optics .band:has(> .cols, > .rows):not(:has(li[data-s~="geometrical-optics"])),
html.s-gravitation .band:has(> .cols, > .rows):not(:has(li[data-s~="gravitation"])),
html.s-heat-transfer .band:has(> .cols, > .rows):not(:has(li[data-s~="heat-transfer"])),
html.s-induction .band:has(> .cols, > .rows):not(:has(li[data-s~="induction"])),
html.s-interference .band:has(> .cols, > .rows):not(:has(li[data-s~="interference"])),
html.s-kinematics .band:has(> .cols, > .rows):not(:has(li[data-s~="kinematics"])),
html.s-kinetic-theory .band:has(> .cols, > .rows):not(:has(li[data-s~="kinetic-theory"])),
html.s-magnetostatics .band:has(> .cols, > .rows):not(:has(li[data-s~="magnetostatics"])),
html.s-nuclear .band:has(> .cols, > .rows):not(:has(li[data-s~="nuclear"])),
html.s-observational .band:has(> .cols, > .rows):not(:has(li[data-s~="observational"])),
html.s-orbital-mechanics .band:has(> .cols, > .rows):not(:has(li[data-s~="orbital-mechanics"])),
html.s-oscillations .band:has(> .cols, > .rows):not(:has(li[data-s~="oscillations"])),
html.s-particle .band:has(> .cols, > .rows):not(:has(li[data-s~="particle"])),
html.s-phase-transitions .band:has(> .cols, > .rows):not(:has(li[data-s~="phase-transitions"])),
html.s-polarization .band:has(> .cols, > .rows):not(:has(li[data-s~="polarization"])),
html.s-quantum-mechanics .band:has(> .cols, > .rows):not(:has(li[data-s~="quantum-mechanics"])),
html.s-rigid-body .band:has(> .cols, > .rows):not(:has(li[data-s~="rigid-body"])),
html.s-solid-state .band:has(> .cols, > .rows):not(:has(li[data-s~="solid-state"])),
html.s-sound .band:has(> .cols, > .rows):not(:has(li[data-s~="sound"])),
html.s-special-relativity .band:has(> .cols, > .rows):not(:has(li[data-s~="special-relativity"])),
html.s-statics .band:has(> .cols, > .rows):not(:has(li[data-s~="statics"])),
html.s-statistical-mechanics .band:has(> .cols, > .rows):not(:has(li[data-s~="statistical-mechanics"])),
html.s-stellar .band:has(> .cols, > .rows):not(:has(li[data-s~="stellar"])),
html.s-wave-propagation .band:has(> .cols, > .rows):not(:has(li[data-s~="wave-propagation"])) {
    display: none !important;
}

/* The examples in an empty search are buttons: pressing one searches it. */
.sx-ex {
    padding: 0;
    font: inherit;
    font-weight: 600;
    color: var(--text-1b);
    background: none;
    border: 0;
    border-bottom: 1px dotted var(--text-4);
    cursor: pointer;
}

.sx-ex:hover {
    color: var(--text-max);
    border-bottom-color: var(--text-2);
}

.sx-topics[hidden] {
    display: none;
}

.sx-scope[hidden] {
    display: none;
}

/* ---- hub cards, second pass: three across, one height everywhere ----
   A card is a head (initials and logo), a text box holding the full name and
   the first sentence of "about", and a footer with the years. The text
   box is a fixed four lines clamped as one, so a long name only takes a line
   from the description and every card on every subject page is the same
   height. The grid never goes past three columns and drops to two, then one,
   as the page narrows. */
.hub-grid {
    grid-template-columns: repeat(auto-fill, minmax(max(270px, calc((100% - 32px) / 3)), 1fr));
    gap: 16px;
}

.hub-card {
    gap: 0;
    min-height: 0;
    padding: 18px 20px 14px;
}

.hc-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    height: 40px;
    margin-bottom: 10px;
}

.hc-head .hub-logo,
.hc-head .hub-flag {
    position: static;
    flex: none;
}

.hc-text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    overflow: hidden;
    height: 80px;
    line-height: 20px;
}

.hub-card .hub-name {
    display: block;
    padding-right: 0;
    font-size: 14.5px;
    font-weight: 600;
    line-height: 20px;
    color: var(--text);
}

.hc-desc {
    display: block;
    font-size: 13.5px;
    line-height: 20px;
    color: var(--text-2);
}

.hub-card .hub-meta {
    margin-top: 14px;
    padding-top: 12px;
    border-top: 1px solid var(--line-faint);
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* ---- the list page in the site's frame; the List button in two halves ----
 * /list/ used to have a bar of its own, a wordmark and "Back to the problems".
 * It now has the top bar every page has (build.py, render_list_page), with a
 * List link lit as its tab, and a Back link above the list only when there is
 * a page of ours to go back to (list.js). The floating List button gains a
 * second half that opens the list page directly. */
.tb-list {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    flex: none;
    padding: 7px 12px;
    border-radius: 99px;
    font-size: 14px;
    color: var(--text-2);
    white-space: nowrap;
    text-decoration: none;
}

.tb-list::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    content: "\f03a";
}

.tb-list b {
    min-width: 2ch;
    margin-right: -4px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--accent);
}

.tb-list:hover,
.tb-list.on {
    color: var(--text-max);
    background: var(--surface-3);
    text-decoration: none;
}

.tb-list.on {
    font-weight: 600;
}

/* The top bar is sticky, so the preview sticks below it. */
@media (min-width: 1100px) {
    #ol-page.ol-with-book {
        padding-top: 18px;
    }

    .ol-book {
        top: 78px;
        height: calc(100vh - 96px);
    }
}

.ol-page .ol-back {
    order: -4;
    align-self: flex-start;
    margin: 0 0 10px;
    font-size: 13.5px;
    color: var(--text-3b);
    text-decoration: none;
}

.ol-page .ol-back:hover {
    color: var(--text-max);
}

.listpill {
    padding: 0;
    gap: 0;
    overflow: hidden;
}

.listpill::before {
    content: none;
    display: none;
}

.lp-open {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    padding: 9px 14px 9px 18px;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}

.lp-open::before {
    display: inline-block;
    width: 16px;
    text-align: center;
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    content: "\f03a";
}

html.ol-open .lp-open::before {
    content: "\f00d";
}

.lp-full {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
    width: 42px;
    color: inherit;
    border-left: 1px solid rgba(255, 255, 255, .28);
    text-decoration: none;
}

.lp-full::before {
    font-family: "Font Awesome 5 Free";
    font-weight: 900;
    font-size: 13px;
    content: "\f065";
}

.lp-open:hover,
.lp-full:hover {
    color: inherit;
    background: rgba(0, 0, 0, .12);
}

.lp-open:focus-visible,
.lp-full:focus-visible {
    outline: 2px solid var(--on-accent);
    outline-offset: -4px;
}

/* Materials that are not PDFs say what they are. */
.xmenu a.x-zip::before { content: "\f1c6"; }
.xmenu a.x-xls::before { content: "\f1c3"; }

/* A hub's note on what is deliberately not archived, under "Coming". */
.hub-absent {
    max-width: 640px;
    margin-top: 28px;
    font-size: 13px;
    line-height: 1.5;
    color: var(--text-3b);
}

/* ---- home tiles: the fade without mask-composite ----
 * The drawing's fade was a second mask layer intersected with the drawing.
 * Safari combines the two layers the other way round wherever the drawing
 * doesn't reach, so every tile narrower than its card (all but Physics)
 * showed a solid block of colour on its left. The mask is now the drawing
 * alone, and the fade lives in the colour it's filled with. */
.home-tile::before {
    background: linear-gradient(100deg, transparent 12%, var(--ink) 62%);
    -webkit-mask: var(--art) right center / auto 100% no-repeat;
    -webkit-mask-composite: source-over;
    mask: var(--art) right center / auto 100% no-repeat;
    mask-composite: add;
}

/* ---- the list: Open PDF where there is no preview ----
 * Below 1100px the list page has no booklet beside it, so Open PDF builds it
 * on request and opens it in the browser's PDF viewer (list.js, openBook). In
 * the phone's panel it leads the row of compact buttons, making five. The
 * tablet-width panel has no room for it; "Open as a page" is one tap away. */
.ol-openpdf {
    display: none;
}

.ol-ic-open::before { content: "\f06e"; }

@media (max-width: 1099px) {
    .ol-page .ol-openpdf {
        display: inline-flex;
    }
}

@media (max-width: 760px) {
    .ol-popup .ol-openpdf {
        display: flex;
    }

    .ol-popup .ol-actions {
        grid-template-columns: repeat(5, 1fr);
    }
}
