[x-cloak] { display: none !important; }

/*
 * One set of scales for both halves of the interface.
 *
 * Bulma renders roughly half these pages and derives every shade from the HSL parts below,
 * so setting those retints its buttons, tags and inputs to match. The hand-written
 * components underneath used forty-five ad-hoc hex values, seventeen font sizes and eleven
 * radii; they now draw from the same short lists, which is most of what makes a page look
 * deliberate rather than assembled.
 */
:root {
    /* Bulma's brand hooks. Everything it draws follows from these three numbers. */
    --bulma-primary-h: 240deg;
    --bulma-primary-s: 41%;
    --bulma-primary-l: 30%;
    --bulma-link-h: 240deg;
    --bulma-link-s: 41%;
    --bulma-link-l: 30%;
    --bulma-warning-h: 37deg;
    --bulma-warning-s: 80%;
    --bulma-warning-l: 55%;
    --bulma-success-h: 144deg;
    --bulma-success-s: 42%;
    --bulma-success-l: 39%;
    --bulma-radius-small: 4px;
    --bulma-radius: 8px;
    --bulma-radius-large: 12px;

    /* Brand. */
    --accent: hsl(240deg 41% 30%);
    --accent-light: hsl(240deg 35% 93%);
    --accent-border: hsl(240deg 28% 83%);
    --orange: hsl(37deg 80% 55%);
    --green: hsl(144deg 42% 39%);
    --red: hsl(0deg 55% 52%);

    /* The colour a button takes when it does not say where it is going. */
    --tone: var(--accent);
    --tone-glow: rgba(45, 45, 107, 0.3);

    /* Neutrals, replacing nine improvised greys. Named by role, not by darkness, so a
       change of mind about the palette does not require re-reading every rule. */
    --ink: hsl(240deg 20% 16%);
    --ink-soft: hsl(240deg 10% 38%);
    --ink-faint: hsl(240deg 8% 55%);
    --rule: hsl(240deg 15% 88%);
    --rule-strong: hsl(240deg 15% 78%);
    --surface: #fff;
    --surface-sunken: hsl(240deg 20% 97%);

    /* Feedback tints, previously three unrelated pastels. */
    --tint-good: hsl(144deg 42% 95%);
    --tint-bad: hsl(0deg 55% 96%);

    /* Type scale: six steps instead of a seventeen-value continuum. */
    --text-xs: 0.75rem;
    --text-sm: 0.85rem;
    --text-md: 1rem;
    --text-lg: 1.15rem;
    --text-xl: 1.4rem;
    --text-2xl: 1.9rem;

    /* Spacing, in the same 4px rhythm Bulma uses. */
    --space-1: 0.25rem;
    --space-2: 0.5rem;
    --space-3: 0.75rem;
    --space-4: 1rem;
    --space-5: 1.5rem;
    --space-6: 2rem;
    --space-7: 3rem;

    /* Height reserved for the fixed bottom bar, so pages can clear it. */
    --bottom-bar-height: 80px;

    --radius-sm: 4px;
    --radius: 8px;
    --radius-lg: 12px;
    --radius-pill: 999px;

    --shadow-sm: 0 1px 2px hsl(240deg 20% 16% / 0.06);
    --shadow: 0 2px 8px hsl(240deg 20% 16% / 0.08);
}

/* Header */
.site-header {
    padding: var(--space-2) 0;
    border-bottom: 2px solid var(--accent);
    margin-bottom: var(--space-3);
}

.site-header .level {
    margin-bottom: 0;
}

.site-title {
    color: var(--accent);
    font-size: var(--text-xl);
    font-weight: 700;
}

.site-title a {
    color: inherit;
    text-decoration: none;
}

.site-subtitle {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    margin-left: var(--space-2);
}

.lang-select {
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    color: var(--accent);
    font-size: var(--text-sm);
    padding: 2px var(--space-2);
    background: transparent;
}

.user-menu {
    position: relative;
}

.user-email {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    cursor: pointer;
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius);
    list-style: none;
}

.user-email:hover {
    background: var(--accent-light);
    color: var(--accent);
}

.user-email::marker,
.user-email::-webkit-details-marker {
    display: none;
}

.user-dropdown {
    position: absolute;
    right: 0;
    top: 100%;
    margin-top: var(--space-1);
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    min-width: 140px;
    z-index: 100;
}

.user-dropdown-item {
    display: block;
    padding: var(--space-2) var(--space-3);
    font-size: var(--text-sm);
    color: var(--ink);
    text-decoration: none;
}

.user-dropdown-item:hover {
    background: var(--accent-light);
    color: var(--accent);
}

/* Login button in header */
.btn-login {
    background: var(--accent) !important;
    color: var(--surface) !important;
    border-color: var(--accent) !important;
    font-size: var(--text-sm) !important;
    padding: 2px var(--space-3);
    height: auto;
    line-height: 1.5;
}

.btn-login:hover {
    opacity: 0.9;
}

/* Tighter navbar items */
.navbar-end .navbar-item {
    padding-left: var(--space-1);
    padding-right: var(--space-1);
}

/* Supported languages section */
.supported-langs {
    text-align: center;
    margin-bottom: var(--space-4);
}

.supported-langs .lang-tags {
    justify-content: center;
}

.supported-langs .title {
    color: var(--accent);
}

/* Guest navbar mobile controls */
.navbar-end-mobile {
    display: none;
    align-items: center;
    gap: var(--space-2);
    margin-left: auto;
    padding-right: var(--space-3);
}

@media (max-width: 1023px) {
    .navbar-end-mobile {
        display: flex;
    }

    /*
     * Room down the sides of the page.
     *
     * Bulma gives the container a width to sit inside from 1024px and leaves the margins to
     * do the rest, so below that the page needs padding of its own — and this had it only
     * below 768px. In between, everything from the heading to the cards of the list ran
     * flush into both edges of the window.
     */
    .container {
        padding-left: var(--space-4);
        padding-right: var(--space-4);
    }
}

/* Intro card */
/*
 * The first-run guide on the dashboard.
 *
 * It used to show everything at once — heading, blurb, all four steps and the button — so
 * collapsing it revealed more rather than less. Now the summary is only the heading and one
 * sentence, and the steps are what opens. It starts open while the library is empty and
 * closed once there is something in it, so it stops asking for attention once it is no
 * longer useful.
 */
.intro-card {
    background: linear-gradient(180deg, var(--accent-light) 0%, var(--surface-sunken) 100%);
    border: 1px solid var(--accent-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-4);
}

.intro-summary {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    cursor: pointer;
    list-style: none;
}

.intro-summary::marker,
.intro-summary::-webkit-details-marker {
    display: none;
}

.intro-lead h2 {
    color: var(--accent);
    font-size: var(--text-lg);
    font-weight: 700;
    line-height: 1.25;
    margin-bottom: var(--space-2);
}

.intro-lead p {
    color: var(--ink-soft);
    font-size: var(--text-sm);
    line-height: 1.5;
    margin: 0;
}

.intro-toggle-text {
    /* Drawn first, so that a float can work on a narrow screen; shown last in the row. */
    order: 2;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
    border: 1px solid var(--accent-border);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--accent);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-xs);
    font-weight: 600;
    white-space: nowrap;
}

/* The chevron points at where the guide will go: down to unfold it, up to put it away. */
.intro-toggle-text .icon {
    width: 1em;
    height: 1em;
    vertical-align: 0;
    transition: transform 0.15s ease;
}

details.intro-card[open] .intro-toggle-text .icon {
    transform: rotate(180deg);
}

/* One step per row: number, then what it is and what it does. */
.steps {
    list-style: none;
    margin: var(--space-4) 0 0;
    padding-top: var(--space-4);
    border-top: 1px solid var(--accent-border);
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-4);
}

/* Two real columns on a wide screen, so both start at a fixed edge. They used to be flex
   items sized by their own text, which left each row beginning wherever the row above
   happened to end. */
@media (min-width: 769px) {
    .steps {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: var(--space-6);
    }
}

.steps li {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

.steps li > div {
    padding-top: 2px;
}

/*
 * Step markers.
 *
 * The drawing arrives as a CSS mask rather than an <i> or an <img>, so the template carries
 * only a name and the colour stays a styling decision. Each step wears the colour of the
 * control it describes: the vocabulary is indigo, the learning list amber, exercises green.
 */
.steps .step-icon {
    flex-shrink: 0;
    width: 34px;
    height: 34px;
    border-radius: var(--radius);
    display: inline-block;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: 30px 30px;
    mask-size: 30px 30px;
}

.steps .step-icon.is-film {
    color: var(--accent);
    -webkit-mask-image: url("/static/img/icons/film-upload.svg");
    mask-image: url("/static/img/icons/film-upload.svg");
}

.steps .step-icon.is-word {
    color: var(--accent);
    -webkit-mask-image: url("/static/img/icons/word-in-line.svg");
    mask-image: url("/static/img/icons/word-in-line.svg");
}

.steps .step-icon.is-list {
    color: var(--orange);
    -webkit-mask-image: url("/static/img/icons/list-add.svg");
    mask-image: url("/static/img/icons/list-add.svg");
}

.steps .step-icon.is-repeat {
    color: var(--green);
    -webkit-mask-image: url("/static/img/icons/repeat-arrows.svg");
    mask-image: url("/static/img/icons/repeat-arrows.svg");
}

.steps strong {
    display: block;
    color: var(--ink);
    margin-bottom: 2px;
}

.steps p {
    color: var(--ink-soft);
    font-size: var(--text-sm);
    line-height: 1.45;
    margin: 0;
}


/* Hero section (guest) */
.hero-section {
    text-align: center;
    padding: var(--space-5) var(--space-4);
    margin-bottom: var(--space-4);
    border-left: 3px solid var(--accent);
    border-radius: 0 var(--radius) var(--radius) 0;
    background: var(--surface-sunken);
}

.hero-title {
    color: var(--accent);
    font-size: var(--text-xl);
    font-weight: 700;
    margin-bottom: var(--space-3);
}

.hero-subtitle {
    color: var(--ink-soft);
    font-size: var(--text-md);
    max-width: 600px;
    margin: 0 auto var(--space-4);
    line-height: 1.5;
}

/* Colour is set here rather than by a Bulma modifier in the template. */
.hero-cta {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
    font-weight: 600;
    padding: var(--space-3) var(--space-5);
    font-size: var(--text-md);
}

.hero-cta:hover,
.hero-cta:focus {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
}

.hero-cta:hover {
    opacity: 0.9;
}

.hero-pricing {
    margin-top: var(--space-4);
    color: var(--ink-faint);
    font-size: var(--text-sm);
}

/* Step cards (guest) */
.step-cards {
    margin-bottom: var(--space-4);
}

.step-card {
    text-align: center;
    padding: var(--space-4) var(--space-3);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    height: 100%;
    position: relative;
    transition: box-shadow 0.2s;
}

.step-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}

/* Guest page cards. Named apart from the dashboard marker, which used to share this class
   and silently inherit a 56px box. */
.step-card-icon {
    width: 56px;
    height: 56px;
    border-radius: var(--radius-lg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-xl);
    margin: 0 auto var(--space-3);
}

.step-cards .column:nth-child(1) .step-card-icon {
    background: #eef0ff;
    color: #4a4fc4;
}

.step-cards .column:nth-child(1) .step-card {
    border-top: 3px solid #4a4fc4;
}

.step-cards .column:nth-child(2) .step-card-icon {
    background: #fff4e6;
    color: #d4880f;
}

.step-cards .column:nth-child(2) .step-card {
    border-top: 3px solid #d4880f;
}

.step-cards .column:nth-child(3) .step-card-icon {
    background: #e8f8ee;
    color: #2ea55a;
}

.step-cards .column:nth-child(3) .step-card {
    border-top: 3px solid #2ea55a;
}

.step-cards .column:nth-child(4) .step-card-icon {
    background: #fce8ee;
    color: #c43a5e;
}

.step-cards .column:nth-child(4) .step-card {
    border-top: 3px solid #c43a5e;
}

.step-card-num {
    background: var(--accent);
    color: var(--surface);
    border-radius: 50%;
    width: 24px;
    height: 24px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-xs);
    font-weight: 700;
    position: absolute;
    top: 0.75rem;
    right: 0.75rem;
}

.step-card h3 {
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--accent);
    margin-bottom: var(--space-2);
}

.step-card p {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    line-height: 1.4;
}

/* Login banner */
.login-banner {
    background: var(--rule);
    border-radius: var(--radius);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-5);
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    color: var(--ink-soft);
    font-size: var(--text-md);
}

/* Language filter tags */
.lang-tags {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    margin-bottom: var(--space-4);
}

/*
 * The same filter for a narrow screen, as a panel of check marks rather than a native
 * picker: this filter is a set, and two languages at once is something a `select` cannot
 * say. Hidden wherever the tags fit.
 */
.lang-dropdown {
    display: none;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--surface);
    margin-bottom: var(--space-4);
}

.lang-dropdown-summary {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-md);
    color: var(--ink-soft);
    cursor: pointer;
    list-style: none;
}

.lang-dropdown-summary::-webkit-details-marker {
    display: none;
}

.lang-dropdown-value {
    color: var(--ink);
    font-weight: 600;
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.lang-dropdown-summary .icon {
    color: var(--ink-faint);
    transition: transform 0.15s ease;
}

.lang-dropdown[open] .lang-dropdown-summary .icon {
    transform: rotate(180deg);
}

.lang-option {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 44px;
    padding: var(--space-2) var(--space-4);
    border-top: 1px solid var(--rule);
    cursor: pointer;
    font-size: var(--text-md);
}

/* The tick holds its place whether or not the language is chosen, so the labels line up. */
.lang-option .icon {
    color: var(--accent);
    visibility: hidden;
}

.lang-option.is-chosen {
    color: var(--accent);
    font-weight: 600;
}

.lang-option.is-chosen .icon {
    visibility: visible;
}

.lang-tag {
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-4);
    font-size: var(--text-sm);
    cursor: pointer;
    background: var(--surface);
    color: var(--ink);
}

.lang-tag.is-active {
    background: var(--accent);
    color: var(--surface);
    border-color: var(--accent);
}

/* Show card */
.show-card {
    border: 1px solid var(--rule);
    border-left: 3px solid var(--accent-border);
    border-radius: 0 var(--radius) var(--radius) 0;
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-3);
    display: flex;
    align-items: center;
    gap: var(--space-4);
    background: var(--surface);
    cursor: pointer;
    text-decoration: none;
    color: inherit;
}

.show-card:hover {
    background: var(--accent-light);
}

.show-lang-badge {
    color: var(--surface);
    border-radius: var(--radius);
    padding: 2px var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    background: var(--lang-color, var(--accent));
}

/* Language colors — muted tones matching --accent (var(--accent)) palette */
.show-card.lang-en { border-left-color: #4a6fa5; --lang-color: #4a6fa5; }
.show-card.lang-fr { border-left-color: #b05050; --lang-color: #b05050; }
.show-card.lang-de { border-left-color: #c8923a; --lang-color: #c8923a; }
.show-card.lang-es { border-left-color: #b87a3d; --lang-color: #b87a3d; }
.show-card.lang-it { border-left-color: var(--green); --lang-color: var(--green); }
.show-card.lang-ru { border-left-color: #7b5e99; --lang-color: #7b5e99; }
.show-card.lang-ja { border-left-color: #9e6178; --lang-color: #9e6178; }
.show-card.lang-ko { border-left-color: #4a8a8f; --lang-color: #4a8a8f; }

.show-title {
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--accent);
}

.show-model {
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-sm);
    padding: 1px var(--space-2);
    font-size: var(--text-xs);
    color: var(--ink-faint);
}

.show-years {
    margin-left: auto;
    color: var(--ink-faint);
    font-size: var(--text-sm);
}

/* Breadcrumbs */
.breadcrumb {
    margin-bottom: var(--space-4);
}

/*
 * The trail stays on one line, and the film's title is what gives way.
 *
 * A title is somebody else's filename and as long as they made it, while the labels around
 * it are ours and are short; wrapped, the trail took a second line on a phone and pushed the
 * page down under it. So nothing wraps, only the title may shrink, and it ends in an
 * ellipsis when the room runs out. min-width is what lets a flex item shrink below the width
 * of its own text at all.
 */
.breadcrumb ul {
    flex-wrap: nowrap;
}

.breadcrumb li {
    min-width: 0;
}

.breadcrumb li:not(.is-elastic) {
    flex-shrink: 0;
}

.breadcrumb li.is-elastic a {
    display: block;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.breadcrumb li a {
    color: var(--ink-faint);
}

.breadcrumb li a:hover {
    color: var(--accent);
}

.breadcrumb li.is-active a {
    color: var(--ink);
    cursor: default;
}

/* Tab navigation (Словарь / Список / Упражнения) */
.tab-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin-bottom: var(--space-5);
}

.tab-btn {
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius-pill);
    padding: var(--space-2) 24px;
    font-size: var(--text-sm);
    cursor: pointer;
    background: var(--surface);
    color: var(--ink);
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.tab-btn.is-active {
    background: var(--accent);
    color: var(--surface);
    border-color: var(--accent);
}

.tab-arrow {
    color: var(--ink-faint);
    font-size: var(--text-lg);
}

/* CEFR level pills */
.level-pills {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
    align-items: center;
}

/*
 * The same filter as a dropdown, for a screen too narrow to lay the choices out in a row.
 * Drawn on every screen and hidden on the wide ones — it is a form field as well as a
 * control, and it is what carries the standing filter along with anything typed in the
 * search box.
 */
.filter-select {
    display: none;
    align-items: center;
    gap: var(--space-3);
    margin-bottom: var(--space-2);
}

.filter-dropdown {
    flex: 1;
    min-width: 0;
    border: 1px solid var(--accent-border);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--accent);
    font-size: var(--text-md);
    font-weight: 500;
    padding: var(--space-2) var(--space-3);
}

.level-pill {
    background: var(--accent);
    color: var(--surface);
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-4);
    font-size: var(--text-sm);
    font-weight: 500;
    white-space: nowrap;
}

/*
 * The filter block above the word list.
 *
 * Three rows of unlabelled pills sitting on top of a list read as part of the list. One word
 * says what the block is, on a phone as much as on a desk, and each row keeps the name of
 * what it filters by.
 */
.filter-bar {
    margin-bottom: var(--space-3);
}

.filter-bar-title {
    display: block;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink-faint);
    margin-bottom: var(--space-2);
}

/*
 * A colour per row, so which filter a pill belongs to is answered by looking rather than by
 * reading. One rule for the pill and a tone set per row, rather than a copy of the rule for
 * each colour — the third row is what showed the copies up.
 */
.filter-row {
    --tone: var(--accent);
    --tone-soft: var(--accent-light);
    --tone-edge: var(--accent-border);

    margin-bottom: var(--space-2);
}

.filter-pills {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
}

/*
 * Where there is room, the pills of all three rows start on one line.
 *
 * "Уровень:", "Тема:" and "Вид:" are three different widths, so each row began somewhere
 * else and the block read as three unrelated things. The rows share one grid — subgrid, so
 * the column is as wide as the longest label in whatever language is being read, rather
 * than a number in the stylesheet that a translation would eventually break.
 *
 * Narrow screens keep out of it: there the rows are dropdowns and the pills are not drawn.
 */
@media (min-width: 769px) {
    .filter-bar {
        display: grid;
        grid-template-columns: max-content 1fr;
    }

    .filter-bar-title {
        grid-column: 1 / -1;
    }

    /*
     * The rows sit in from the heading above them, so that "Уровень", "Тема" and "Вид" read
     * as three parts of the block called Filters rather than three headings of their own.
     * The same step for each, so what they line up with is each other.
     */
    .filter-row {
        display: grid;
        grid-column: 1 / -1;
        grid-template-columns: subgrid;
        align-items: baseline;
        padding-left: var(--space-5);
    }
}

.filter-row.is-topic {
    --tone: var(--green);
    --tone-soft: hsl(144deg 40% 94%);
    --tone-edge: hsl(144deg 28% 80%);
}

.filter-row.is-kind {
    --tone: hsl(28deg 68% 45%);
    --tone-soft: hsl(32deg 80% 94%);
    --tone-edge: hsl(32deg 55% 78%);
}

/*
 * Smaller than the pills that label a word, because there are thirty of these and one of
 * those. The count is set apart and lighter: it matters when choosing between two topics and
 * is noise the rest of the time, and run together as "Быт 87" it read as one long word.
 */
.filter-pill {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
    cursor: pointer;
    white-space: nowrap;
    border: 1px solid var(--tone-edge);
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--tone);
    padding: 2px var(--space-3);
    font-size: var(--text-xs);
    font-weight: 500;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}

.filter-pill:hover {
    background: var(--tone-soft);
}

.filter-pill-count {
    font-size: 0.9em;
    opacity: 0.65;
    font-variant-numeric: tabular-nums;
}

.filter-pill.is-active {
    background: var(--tone);
    border-color: var(--tone);
    color: var(--surface);
    font-weight: 600;
}

.filter-pill.is-active .filter-pill-count {
    opacity: 0.8;
}

/* The tail of a long row, and the way back. Plainer than a pill: it filters nothing. */
.filter-more {
    cursor: pointer;
    font-size: var(--text-xs);
    font-weight: 600;
    color: var(--ink-faint);
    padding: 2px var(--space-2);
    border-bottom: 1px dashed var(--rule-strong);
}

.filter-more:hover {
    color: var(--tone);
    border-bottom-color: var(--tone);
}

.word-level {
    border-radius: var(--radius-sm);
}

/* Filter buttons right side */
.filter-btns {
    display: flex;
    gap: var(--space-2);
}

.filter-btn {
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--space-1) var(--space-4);
    font-size: var(--text-sm);
    cursor: pointer;
    background: var(--surface);
}

/* Word list */
/*
 * Columns that hold their place down the whole list.
 *
 * As a flex row every word pushed what followed it: the part of speech began where the term
 * ended, the level where the part of speech ended, and a list read top to bottom had nothing
 * to read down. The term gets a column wide enough for most of them and an ellipsis for the
 * rest; everything after it starts at the same place in every row.
 */
.word-row {
    border: 1px solid var(--rule);
    border-left: 3px solid var(--accent-border);
    border-radius: 0 var(--radius) var(--radius) 0;
    padding: var(--space-3) var(--space-4);
    margin-bottom: 0;
    display: grid;
    grid-template-columns: auto minmax(0, 16rem) auto 5.5rem 2.6rem 1fr auto;
    align-items: center;
    gap: var(--space-3);
    background: var(--surface);
    cursor: pointer;
}

.word-row:hover {
    background: var(--accent-light);
}

.word-row.is-expanded {
    border-left-color: var(--accent);
    margin-bottom: 0;
    border-bottom: none;
    border-radius: 0 var(--radius) 0 0;
}

/*
 * A word the exercises have decided is known.
 *
 * Only the stripe is coloured, and only so the eye catches it while scrolling — the word
 * itself stays black, because a green term says nothing on its own and the button at the end
 * of the row is where the state is spelled out. It stays green when the row is opened, so
 * this has to come after the expanded rule.
 */
.word-row.is-learned,
.word-row.is-learned.is-expanded {
    border-left-color: var(--green);
}

/* The opened body is a sibling, so it needs telling too: one word, one stripe, one colour. */
.word-row.is-learned + .word-detail {
    border-left-color: var(--green);
}

.word-row + .word-detail {
    margin-top: 0;
}

.word-row + .word-row,
.word-detail + .word-row {
    margin-top: var(--space-2);
}

.word-expand {
    color: var(--ink-faint);
    cursor: pointer;
    font-size: var(--text-sm);
}

.word-name {
    font-weight: 700;
    font-size: var(--text-md);
    color: #222;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*
 * The article a noun is learned with. German is not learned as "Abgang" but as "der Abgang",
 * and the gender the model returns is no use to anybody sitting in the database. Lighter than
 * the word itself, because the word is what is being looked up.
 */
.word-article {
    color: var(--ink-faint);
    font-weight: 400;
}

.word-speaker {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    background: none;
    border: none;
    padding: var(--space-1) var(--space-2);
    border-radius: 50%;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.word-speaker:hover {
    background: var(--accent-light);
    color: var(--accent);
}

.word-pos {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    font-style: italic;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.word-level {
    background: var(--accent);
    color: var(--surface);
    border-radius: var(--radius-sm);
    padding: 1px var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    min-width: 30px;
    text-align: center;
}

.word-example {
    color: var(--ink-soft);
    font-size: var(--text-sm);
    font-style: italic;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/*
 * Adding a word to the learning list.
 *
 * This was an outline star, which said neither that it was a control nor what it would do.
 * A bordered circle reads as a button, the plus says what pressing it achieves, and the
 * filled tick afterwards says the word is in the list rather than merely favourited.
 */
.word-add {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1.5px solid var(--accent-border);
    border-radius: 50%;
    background: var(--surface);
    color: var(--accent);
    font-size: var(--text-sm);
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s, transform 0.1s;
}

.word-add:hover {
    border-color: var(--accent);
    background: var(--accent-light);
}

.word-add:active {
    transform: scale(0.92);
}

/*
 * On the list — which is a choice the reader made, not a verdict on how well they know the
 * word. It fills with the accent that marks every other chosen thing on the site: an active
 * pill, an open tab. Green it cannot be, because green now says "known" on this same row, and
 * a colour that means two things a hand's width apart means neither.
 */
.word-add.is-added {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--surface);
}

.word-add.is-added:hover {
    background: var(--ink-soft);
    border-color: var(--ink-soft);
}

/*
 * And learned: still on the list, so still filled, but green and wearing a cap. This is the
 * third of three states one control carries, and the only one that asks before it changes —
 * the hover therefore darkens the green rather than promising the usual removal.
 */
.word-add.is-added.is-learned,
.word-add.is-added.is-learned:hover {
    background: var(--green);
    border-color: var(--green);
    color: var(--surface);
}

.word-add.is-added.is-learned:hover {
    filter: brightness(0.9);
}

/* The question, and then the way out of it. */
.forget-question {
    margin: var(--space-3) 0 var(--space-4);
    line-height: 1.5;
}

.forget-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* Report button (fixed right) */
.report-btn {
    position: fixed;
    right: 0;
    top: 50%;
    transform: translateY(-50%);
    background: var(--accent);
    color: var(--surface);
    border: none;
    border-radius: var(--radius) 0 0 var(--radius);
    padding: var(--space-3) var(--space-2);
    cursor: pointer;
    font-size: var(--text-md);
    z-index: 50;
    box-shadow: -2px 0 8px rgba(0,0,0,0.1);
}

/* Word detail (expanded) */
.word-detail {
    padding: var(--space-4) var(--space-5);
    margin-bottom: 0;
    border: 1px solid var(--rule);
    border-top: none;
    border-left: 3px solid var(--accent);
    border-radius: 0 0 var(--radius) 0;
    background: var(--surface);
}

.word-detail-label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    color: var(--ink-faint);
    letter-spacing: 0.05em;
    margin-bottom: var(--space-1);
}

.word-detail-context {
    background: var(--accent-light);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius);
    font-style: italic;
    color: var(--ink);
    margin-bottom: var(--space-3);
}

.word-detail-meta {
    color: var(--accent);
    font-size: var(--text-sm);
}

/* Episode info */
.episode-section {
    border-bottom: 1px solid var(--rule);
    padding-bottom: var(--space-5);
    margin-bottom: var(--space-5);
}

.episode-section h3 {
    color: var(--accent);
    font-size: var(--text-lg);
    font-weight: 600;
    margin-bottom: var(--space-3);
}

.stat-pills {
    display: flex;
    gap: 0;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    overflow: hidden;
    width: fit-content;
    margin-bottom: var(--space-3);
}

.stat-pill {
    padding: var(--space-2) var(--space-4);
    text-align: center;
    border-right: 1px solid var(--rule);
    font-size: var(--text-sm);
}

.stat-pill:last-child {
    border-right: none;
}

.stat-pill-label {
    font-size: var(--text-xs);
    text-transform: uppercase;
    color: var(--ink-faint);
}

.stat-pill-value {
    font-weight: 700;
    color: var(--ink);
}

/* Movie detail navigation */
.movie-nav {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

.movie-nav-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-4) var(--space-6);
    font-size: var(--text-md);
    font-weight: 600;
    border-radius: var(--radius-lg);
    text-decoration: none;
    cursor: pointer;
    transition: background 0.2s, box-shadow 0.2s;
}

.movie-nav-btn {
    background: var(--tone);
    color: var(--surface);
    border: 2px solid var(--tone);
}

.movie-nav-btn:hover {
    box-shadow: 0 4px 12px var(--tone-glow);
    color: var(--surface);
}

.movie-nav-btn.is-disabled {
    opacity: 0.5;
    pointer-events: none;
}

/*
 * Where a button goes decides what colour it is, and decides it once.
 *
 * A film opens into three places, and each keeps its hue wherever it is offered: the
 * vocabulary is indigo, the list of words being learned amber, the exercises green. The same
 * three run through the steps on the front page, so the colour is learned before the first
 * film is uploaded. These classes say nothing about shape — a pill in the bottom bar and a
 * slab on the film page both take their colour from the door they open.
 */
.to-vocabulary {
    --tone: var(--accent);
    --tone-glow: rgba(45, 45, 107, 0.3);
}

.to-list {
    --tone: var(--orange);
    --tone-glow: rgba(232, 160, 48, 0.3);
}

.to-exercises {
    --tone: var(--green);
    --tone-glow: rgba(58, 143, 92, 0.3);
}

/* Topic pills */
.topic-pill {
    background: var(--accent-light);
    color: var(--accent);
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-4);
    font-size: var(--text-sm);
    font-weight: 500;
    white-space: nowrap;
}

/* Bar chart */
.bar-chart-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-1);
}

.bar-chart-label {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--ink-soft);
    flex-shrink: 0;
    text-align: right;
}

.bar-chart-track {
    flex: 1;
    background: var(--rule);
    border-radius: var(--radius-sm);
    height: 18px;
}

.bar-chart-fill {
    border-radius: var(--radius-sm);
    height: 18px;
    min-width: 2px;
}

.bar-chart-fill-level {
    background: #8a9cc5;
}

.bar-chart-fill-topic {
    background: #9ab5a0;
}

.bar-chart-count {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    min-width: 2rem;
    text-align: right;
    flex-shrink: 0;
}

/* Topic pill filter */

/* Detail toggle link */
.detail-toggle {
    color: var(--accent);
    font-size: var(--text-sm);
    cursor: pointer;
    text-decoration: none;
}

.detail-toggle:hover {
    text-decoration: underline;
    color: var(--accent);
}

/* Detail progress bar */
.detail-progress-bar {
    background: var(--rule);
    border-radius: var(--radius);
    height: 8px;
    overflow: hidden;
}

.detail-progress-fill {
    background: var(--accent);
    height: 100%;
    border-radius: var(--radius);
}

/* Cast tags */
.cast-tags {
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.cast-tag {
    border: 1px solid var(--rule-strong);
    border-radius: var(--radius);
    padding: var(--space-2) var(--space-4);
    font-size: var(--text-sm);
}

/* Study list */
.study-card {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-3);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.study-word {
    font-weight: 700;
    font-size: var(--text-md);
}

.study-meaning {
    color: var(--ink-faint);
    font-size: var(--text-sm);
}

.study-meta {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Exercise */
/*
 * The card wears the colour of the tab above it, so the drill is recognisable without reading.
 * The wash alone is too faint to carry that at a glance, so the edge and the instruction above
 * the answers take the colour at full strength.
 */
.exercise-card {
    border-left: 4px solid var(--type-ink, var(--accent));
    border-radius: 0 var(--radius) var(--radius) 0;
    padding: var(--space-5);
    background: var(--type-wash, var(--surface));
}

.exercise-card h4 {
    color: var(--type-ink, var(--accent));
    font-size: var(--text-lg);
    font-weight: 600;
    margin-bottom: var(--space-4);
}

.exercise-answer {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-md);
    color: var(--ink);
}

.exercise-options {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

/* Answers stay white on the tinted card, which is what keeps them looking pressable. */
.exercise-option {
    border: 1px solid var(--type-edge, var(--rule));
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-md);
    color: var(--ink);
    background: var(--surface);
    cursor: pointer;
    text-align: left;
    transition: border-color 0.2s, box-shadow 0.2s;
}

.exercise-option:hover {
    border-color: var(--type-ink, var(--accent));
    box-shadow: var(--shadow-sm);
}

.exercise-option.is-correct {
    border-color: var(--green);
    background: var(--tint-good);
    color: var(--green);
    font-weight: 600;
}

.exercise-option.is-wrong {
    border-color: var(--red);
    background: var(--tint-bad);
    color: var(--red);
}

/*
 * The verdict: a band above the whole card, sharing the card's shape so the two read as one
 * column — same four-pixel stripe down the left, same radius, and the tints an option wears
 * when it turns out to be right or wrong. As a bare coloured sentence it was a colour and a
 * line of text with nothing holding them together; as a band the eye lands on it before
 * reading a word of it.
 *
 * It takes its space only once there is a verdict to print, so the card moves down on
 * answering. By then the answer is given and the card is there to be read, not pressed.
 */
.exercise-feedback {
    margin-bottom: var(--space-4);
    padding: var(--space-3) var(--space-5);
    border-left: 4px solid;
    border-radius: 0 var(--radius) var(--radius) 0;
}

.exercise-feedback-text {
    font-weight: 600;
    font-size: var(--text-md);
    margin-bottom: 0;
}

.exercise-feedback.is-correct {
    border-left-color: var(--green);
    background: var(--tint-good);
}

.exercise-feedback.is-correct .exercise-feedback-text {
    color: var(--green);
}

.exercise-feedback.is-wrong {
    border-left-color: var(--red);
    background: var(--tint-bad);
}

.exercise-feedback.is-wrong .exercise-feedback-text {
    color: var(--red);
}

.match-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
}

.match-col {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.match-item {
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    padding: 0.6rem var(--space-3);
    font-size: var(--text-sm);
    background: var(--surface);
    cursor: pointer;
    text-align: center;
    transition: border-color 0.2s, background 0.2s;
}

.match-item:hover {
    border-color: var(--accent);
}

.match-item.is-selected {
    border-color: var(--accent);
    background: var(--accent-light);
    font-weight: 600;
}

.match-item.is-matched {
    border-color: var(--green);
    background: var(--tint-good);
    color: var(--green);
    cursor: default;
    opacity: 0.7;
}

.match-item.is-wrong {
    border-color: var(--red);
    background: var(--tint-bad);
}

.flashcard-scene {
    perspective: 800px;
    cursor: pointer;
    margin-bottom: var(--space-5);
    max-width: 480px;
    margin-left: auto;
    margin-right: auto;
}

/*
 * The two faces are stacked in one grid cell rather than laid on top of each other.
 *
 * They used to be absolutely positioned, which took them out of the flow: the box was
 * whatever min-height said and nothing else, so a long idiom pushed "show the answer" out
 * through the bottom border. Sharing a cell, the box is as tall as the taller face — and it
 * is the taller of the two, so turning the card over cannot spill it either.
 */
.flashcard-inner {
    position: relative;
    display: grid;
    min-height: 200px;
    transition: transform 0.5s ease;
    transform-style: preserve-3d;
}

.flashcard-inner.is-flipped {
    transform: rotateY(180deg);
}

.flashcard-face {
    grid-area: 1 / 1;
    backface-visibility: hidden;
    border-radius: var(--radius-lg);
    padding: var(--space-5) var(--space-5);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}

/*
 * The card takes the colour of its tab, but as an edge rather than a field.
 *
 * It was two hex values belonging to no palette; retinting them per drill only moved the
 * problem, because a hand-sized block of saturated colour is tiring to sit in front of, and
 * this is a screen people work through fifty words at a time. The colour now names the drill
 * from the border and the word itself, and the rest of the card is as quiet as every other
 * card on the site. The flip still reads: tinted face turning to a white one.
 */
.flashcard-front {
    background: var(--type-wash, var(--surface-sunken));
    border: 2px solid var(--type-edge, var(--rule));
    color: var(--ink);
    box-shadow: var(--shadow-sm);
}

.flashcard-back {
    background: var(--surface);
    border: 2px solid var(--type-ink, var(--accent));
    color: var(--ink);
    box-shadow: var(--shadow-sm);
    transform: rotateY(180deg);
}

.flashcard-pos {
    background: var(--surface);
    border: 1px solid var(--type-edge, var(--rule));
    color: var(--type-ink, var(--accent));
    border-radius: var(--radius-pill);
    padding: 2px var(--space-4);
    font-size: var(--text-sm);
    margin-bottom: var(--space-3);
}

.flashcard-pos-back {
    background: var(--type-wash, var(--accent-light));
}

.flashcard-term {
    font-size: var(--text-2xl);
    font-weight: 700;
    color: var(--ink);
    margin-bottom: var(--space-3);
}

.flashcard-term-back {
    color: var(--type-ink, var(--accent));
}

.flashcard-speak {
    background: none;
    border: none;
    color: var(--type-ink, var(--accent));
    opacity: 0.55;
    cursor: pointer;
    font-size: var(--text-lg);
    vertical-align: middle;
}

.flashcard-speak:hover {
    opacity: 1;
}

.flashcard-context {
    font-size: var(--text-md);
    color: var(--ink-soft);
    font-style: italic;
    margin-bottom: var(--space-3);
}

.flashcard-hint {
    font-size: var(--text-sm);
    color: var(--ink-faint);
}

.flashcard-meaning {
    font-size: var(--text-lg);
    font-weight: 600;
    margin-bottom: var(--space-2);
}

.flashcard-context-meaning {
    font-size: var(--text-sm);
    color: var(--ink-soft);
    font-style: italic;
}

.flashcard-buttons {
    display: flex;
    justify-content: center;
    gap: var(--space-4);
}

.flashcard-btn {
    border-radius: var(--radius-pill);
    padding: var(--space-2) var(--space-6);
    font-size: var(--text-md);
    font-weight: 600;
    background: var(--surface);
    cursor: pointer;
}

.flashcard-btn-learn {
    border: 2px solid var(--red);
    color: var(--red);
}

.flashcard-btn-learn:hover {
    background: var(--tint-bad);
}

.flashcard-btn-know {
    border: 2px solid var(--green);
    color: var(--green);
}

.flashcard-btn-know:hover {
    background: var(--tint-good);
}

.exercise-context {
    border-top: 1px solid var(--rule);
    padding-top: var(--space-3);
    font-size: var(--text-md);
    color: var(--ink-soft);
    line-height: 1.5;
}

.exercise-context-inline {
    font-size: var(--text-md);
    color: var(--ink-soft);
    line-height: 1.5;
    margin-top: var(--space-3);
}

/* Upload */
.upload-zone {
    border: 2px dashed var(--rule-strong);
    border-radius: var(--radius-lg);
    padding: var(--space-7);
    text-align: center;
    color: var(--ink-faint);
    cursor: pointer;
}

.upload-zone .plus {
    font-size: var(--text-2xl);
    color: var(--ink-faint);
    margin-bottom: var(--space-2);
}

.credits-bar {
    background: var(--rule);
    border-radius: var(--radius);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-5);
    font-size: var(--text-sm);
}

/* The ticket says what the number counts: one credit is one film, not a sum of money. */
.credits-bar .icon {
    margin-right: var(--space-2);
    color: var(--orange);
}

/* Pricing */
.pricing-card {
    background: var(--surface-sunken);
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    margin-bottom: var(--space-5);
    text-align: center;
}

.pricing-head {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
}

.pricing-art {
    width: 116px;
    height: auto;
}

/*
 * A package is a card, not a dark pill: the count, the price and what one credit works out
 * to each are three different sizes so the eye can compare packages down a column instead of
 * reading three identical strings. The cheapest credit wears the badge.
 */
.pricing-options {
    display: flex;
    gap: var(--space-3);
    justify-content: center;
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
}

.plan-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    min-width: 132px;
    padding: var(--space-5) var(--space-4) var(--space-4);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    background: var(--surface);
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
}

.plan-card:hover {
    transform: translateY(-2px);
    border-color: var(--accent-border);
    box-shadow: var(--shadow);
}

.plan-card.is-featured {
    border: 2px solid var(--accent);
    background: linear-gradient(hsl(240deg 35% 98%), var(--surface));
}

.plan-badge {
    position: absolute;
    top: calc(-1 * var(--space-3));
    left: 50%;
    transform: translateX(-50%);
    padding: 2px var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--orange);
    color: var(--surface);
    font-size: var(--text-xs);
    font-weight: 700;
    white-space: nowrap;
}

.plan-credits {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--accent);
    font-size: var(--text-2xl);
    font-weight: 700;
    line-height: 1;
}

.plan-credits .icon {
    width: 1.1em;
    height: 1.1em;
    vertical-align: 0;
    color: var(--orange);
}

.plan-label {
    color: var(--ink-faint);
    font-size: var(--text-xs);
    text-transform: lowercase;
}

.plan-price {
    margin-top: var(--space-2);
    color: var(--ink);
    font-size: var(--text-lg);
    font-weight: 700;
}

.plan-unit {
    color: var(--ink-faint);
    font-size: var(--text-xs);
}

/* The welcome offer is the one price a new reader should see first. */
.welcome-offer {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-5) var(--space-4);
    margin-bottom: var(--space-5);
    border-radius: var(--radius-lg);
    background: linear-gradient(135deg, var(--accent) 0%, hsl(260deg 45% 42%) 100%);
    color: var(--surface);
}

.welcome-offer-text {
    margin: 0;
    font-size: var(--text-md);
    font-weight: 700;
    text-align: center;
}

.welcome-offer-btn.button {
    background: var(--surface);
    border-color: var(--surface);
    color: var(--accent);
    font-weight: 700;
}

.welcome-offer-btn .icon {
    color: var(--orange);
}

/* Promo code: a green aside, so it reads as a bonus rather than a second price list. */
.promo-box {
    padding: var(--space-4);
    border: 1px dashed hsl(144deg 30% 70%);
    border-radius: var(--radius-lg);
    background: var(--tint-good);
}

.promo-lead {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    color: var(--green);
}

.promo-title {
    font-size: var(--text-md);
    font-weight: 700;
}

.promo-lead .icon {
    width: 1.3em;
    height: 1.3em;
    vertical-align: 0;
}

.promo-submit.button {
    background: var(--green);
    border-color: var(--green);
    color: var(--surface);
    font-weight: 600;
}

.promo-submit.button:hover {
    opacity: 0.9;
}

.promo-row {
    display: flex;
    gap: var(--space-2);
    max-width: 400px;
    margin: 0 auto;
}

.promo-row .input {
    flex: 1;
}

/* Bottom bar */
.bottom-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: var(--surface);
    box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
    padding: var(--space-3);
    display: flex;
    justify-content: center;
    gap: var(--space-4);
    z-index: 100;
}

.bottom-bar.is-new {
    animation: slideUp 0.3s ease-out;
}

@keyframes slideUp {
    from { transform: translateY(100%); opacity: 0; }
    to { transform: translateY(0); opacity: 1; }
}

.bottom-btn {
    border-radius: var(--radius-pill);
    padding: var(--space-3) var(--space-6);
    font-size: var(--text-md);
    font-weight: 600;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--surface);
    background: var(--tone);
}

/* Footer */
.site-footer {
    border-top: 1px solid var(--rule);
    padding: var(--space-5) 0;
    margin-top: var(--space-7);
    text-align: center;
    color: var(--ink-faint);
    font-size: var(--text-sm);
    margin-bottom: var(--bottom-bar-height);
}

.site-footer a {
    color: var(--accent);
    text-decoration: none;
}

/* Counter pill */
.counter-pill {
    border: 1px solid var(--rule);
    border-radius: var(--radius-pill);
    padding: var(--space-2) var(--space-5);
    font-size: var(--text-sm);
    color: var(--ink-faint);
    text-align: center;
}

/* Progress bar */
.progress-bar {
    background: var(--rule);
    border-radius: var(--radius);
    height: 8px;
    overflow: hidden;
}

.progress-fill {
    background: var(--accent);
    height: 100%;
    border-radius: var(--radius);
}

/* Exercise type pills */
/*
 * Exercises: one hue per type.
 *
 * Five kinds of drill are told apart by colour before anyone reads the label, so the tab, the
 * bar under it and the card behind the question all draw from the same three variables. Those
 * are carried down from the nearest `data-type`, which means announcing "we are in Matching
 * now" is a single attribute on the scope and nothing else.
 *
 * The hues are held at one saturation and lightness so the row reads as a family rather than a
 * paintbox. Red is not among them: here it means a wrong answer.
 */
/*
 * One column for every drill.
 *
 * Left to the page container, an answer of three words stretched across eleven hundred
 * pixels, and moving from Multiple choice to Flashcards threw the content from full width to
 * a 480px box and back. Holding the whole scope to one measure fixes both: a readable line
 * length, and a column that stays put while you move along the row of tabs.
 */
.exercise-scope {
    --type-ink: var(--accent);
    --type-wash: var(--accent-light);
    --type-edge: var(--accent-border);

    max-width: 680px;
    margin: 0 auto;
}

/* Multiple choice is the brand's own hue, so it takes the brand's own value. A near miss —
   the same hue fourteen points lighter — reads as a mistake next to anything using --accent. */
[data-type="choice"] {
    --type-ink: var(--accent);
    --type-wash: hsl(240deg 44% 97.5%);
    --type-edge: hsl(240deg 30% 87%);
}

[data-type="gap"] {
    --type-ink: hsl(190deg 52% 34%);
    --type-wash: hsl(190deg 48% 97%);
    --type-edge: hsl(190deg 34% 85%);
}

[data-type="listen"] {
    --type-ink: hsl(275deg 34% 47%);
    --type-wash: hsl(275deg 40% 97.5%);
    --type-edge: hsl(275deg 28% 88%);
}

[data-type="match"] {
    --type-ink: hsl(144deg 42% 34%);
    --type-wash: hsl(144deg 40% 97%);
    --type-edge: hsl(144deg 28% 85%);
}

[data-type="flashcards"] {
    --type-ink: hsl(30deg 62% 40%);
    --type-wash: hsl(35deg 62% 97%);
    --type-edge: hsl(35deg 45% 86%);
}

.exercise-types {
    display: flex;
    justify-content: center;
    gap: var(--space-1);
    margin-bottom: var(--space-5);
    border-bottom: 1px solid var(--rule);
}

/*
 * A tab is an icon over a label with a rule under it, not a button. Five outlined pills side
 * by side read as five things to press; only one of them does anything on arrival, and the
 * one already chosen is not a button at all.
 */
.exercise-type {
    display: flex;
    flex: 0 1 auto;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-2) var(--space-3);
    margin-bottom: -1px;
    border: none;
    border-bottom: 2px solid transparent;
    border-radius: var(--radius) var(--radius) 0 0;
    background: none;
    color: var(--ink-soft);
    font-size: var(--text-md);
    line-height: 1.25;
    text-align: center;
    cursor: pointer;
    transition: color 0.15s, background 0.15s, border-color 0.15s;
}

.exercise-type .icon {
    width: 24px;
    height: 24px;
    color: var(--type-ink);
    opacity: 0.5;
    transition: opacity 0.15s;
}

/* Each drill is named twice; the wide screen takes the full name. */
.exercise-type-label.is-short {
    display: none;
}

.exercise-type:hover {
    background: var(--type-wash);
}

.exercise-type:hover .icon {
    opacity: 0.8;
}

.exercise-type.is-active {
    background: var(--type-wash);
    border-bottom-color: var(--type-ink);
    color: var(--type-ink);
    font-weight: 600;
}

.exercise-type.is-active .icon {
    opacity: 1;
}

/* Level label */
.level-label {
    color: var(--ink-faint);
    margin-right: var(--space-2);
}

/* Progress area */
.progress-area {
    flex: 1;
}

.progress-item {
    flex: 1;
    max-width: 400px;
}

.progress-item-inner {
    width: 100%;
}

/* Credits variants */
.credits-bar-flex {
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.credits-empty {
    color: var(--red);
    font-size: var(--text-sm);
}

.credits-variant {
    margin-top: var(--space-7);
    padding-top: var(--space-6);
    border-top: 2px dashed var(--rule);
}

.upload-file-selected {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--space-4) var(--space-5);
    cursor: default;
}

.extraction-form {
    background: #f9f9f9;
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    margin-top: var(--space-4);
}

.select-full {
    width: 100%;
    padding: var(--space-2);
    border-radius: var(--radius-sm);
    border: 1px solid #dbdbdb;
    font-size: var(--text-md);
}

/* Upload disabled */
.upload-zone-disabled {
    opacity: 0.4;
    pointer-events: none;
}

/* Close button */
.close-btn {
    cursor: pointer;
}

/* Mobile burger lang select */
.burger-lang-select {
    width: 100%;
}

/* Login modal */
.login-modal-card {
    max-width: 420px;
}

.login-modal-body {
    border-radius: var(--radius);
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: var(--space-4);
}

.login-success-text {
    color: var(--green);
    font-weight: 600;
}

/* htmx loading state */
form.htmx-request input,
form.htmx-request textarea,
form.htmx-request select,
form.htmx-request button[type="submit"] {
    opacity: 0.5;
    pointer-events: none;
}

form.htmx-request button[type="submit"]::after {
    content: " ...";
}

/* Filename analysis overlay */
.analyze-overlay {
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0.85);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    z-index: 10;
    border-radius: var(--radius);
    opacity: 0;
    transition: opacity 200ms;
}

.analyze-overlay[class*="htmx-request"] {
    opacity: 1;
}

.analyze-overlay .loader {
    width: 2rem;
    height: 2rem;
    border: 3px solid #dbdbdb;
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.6s linear infinite;
}

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

/* Intro card */
.intro-summary > div {
    flex: 1;
}

.intro-card .hero-cta {
    display: block;
    width: fit-content;
    margin: var(--space-4) auto 0;
}

/*
 * Intro card toggle.
 *
 * The summary is a flex row: the upload button sits centred in the left column and this
 * label sits on the right. Swapping the label with display:none resized the label, which
 * resized the left column, which slid the button sideways by about twenty pixels on every
 * open and close. Both labels are therefore always laid out, stacked in one grid cell so
 * the box stays as wide as the longer of the two, and only visibility changes.
 */
.intro-toggle-labels {
    display: grid;
    justify-items: center;
}

.intro-toggle-closed,
.intro-toggle-open {
    grid-area: 1 / 1;
}

.intro-toggle-open {
    visibility: hidden;
}

details.intro-card[open] .intro-toggle-closed {
    visibility: hidden;
}

details.intro-card[open] .intro-toggle-open {
    visibility: visible;
}

/* ==================== Mobile ==================== */
@media (max-width: 768px) {
    .site-subtitle {
        display: none;
    }

    .site-title {
        font-size: var(--text-xl);
    }

    /* Container padding */
    /* Force ALL levels horizontal on mobile */
    .level {
        display: flex !important;
        flex-direction: row !important;
        flex-wrap: wrap;
    }

    .level-left, .level-right {
        flex-shrink: 0;
    }

    .level-right {
        margin-left: auto;
    }

    /* Header: email hidden on mobile, keep title + lang */
    .user-menu {
        display: none;
    }

    /* Intro card */
    .intro-card {
        padding: var(--space-4);
    }

    .intro-toggle {
        font-size: var(--text-xs);
        padding: 3px var(--space-2);
    }

    .intro-toggle-text {
        font-size: var(--text-xs);
        padding: 3px var(--space-2);
    }

    .intro-card h2 {
        font-size: var(--text-md);
    }

    .steps {
        font-size: var(--text-sm);
    }


    /* Hide step cards on mobile */
    .step-cards {
        display: none;
    }

    /* Filters: stack vertically */
    .filter-btns {
        margin-top: var(--space-2);
    }

    /* Packages stay side by side: three prices are compared across, not scrolled through. */
    .pricing-options {
        gap: var(--space-2);
        flex-wrap: nowrap;
    }

    .plan-card {
        flex: 1;
        min-width: 0;
        padding: var(--space-5) var(--space-2) var(--space-3);
    }

    .plan-credits {
        font-size: var(--text-xl);
    }

    .plan-price {
        font-size: var(--text-md);
    }

    .plan-unit,
    .plan-badge {
        font-size: 0.7rem;
    }

    /* Hero */
    .hero-title {
        font-size: var(--text-lg);
    }

    .hero-subtitle {
        font-size: var(--text-sm);
    }

    .hero-section {
        padding: var(--space-5) var(--space-4);
    }

    /* Step cards: 2 columns */
    .step-cards .column {
        width: 50%;
        flex: none;
    }

    .step-card {
        padding: var(--space-3) var(--space-2);
        border-top-width: 2px;
    }

    .step-card h3 {
        font-size: var(--text-sm);
    }

    .step-card p {
        font-size: var(--text-xs);
    }

    .step-icon {
        width: 36px;
        height: 36px;
        font-size: var(--text-md);
    }

    .step-card-num {
        width: 20px;
        height: 20px;
        font-size: var(--text-xs);
        top: 0.5rem;
        right: 0.5rem;
    }

    /* Tags smaller */
    .lang-tags {
        gap: var(--space-2);
    }

    .lang-tag {
        font-size: var(--text-xs);
        padding: 3px var(--space-3);
    }

    /* Show cards */
    .show-card {
        padding: var(--space-3) var(--space-4);
        gap: var(--space-2);
    }

    .show-title {
        font-size: var(--text-md);
    }

    .show-model {
        display: none;
    }

    .show-years {
        font-size: var(--text-sm);
    }

    /* Word rows */
    /*
     * A word over its own description, rather than five things abreast.
     *
     * On one line the term, the speaker, the part of speech, the level and the button that
     * adds the word were each squeezed to whatever was left, and the term — the thing being
     * read — came off worst. Two rows: the word and the button that acts on it above, what
     * the word is below. The triangle and the button span both, so they stay where a thumb
     * expects them.
     */
    /*
     * Four columns that hold their place down the whole list: the triangle, a narrow one for
     * the speaker over the level, then the word over what it is, and the button at the end.
     * Anything that followed a word instead of standing in a column — the level after the
     * part of speech, the speaker after the term — moved with the length of its neighbour and
     * wandered from row to row.
     */
    .word-row {
        display: grid;
        grid-template-columns: auto 2.6rem auto 1fr auto;
        grid-template-areas:
            "expand speaker name .  add"
            "expand level   pos  .  add";
        align-items: center;
        column-gap: var(--space-2);
        row-gap: 2px;
        padding: var(--space-2) var(--space-3);
    }

    .word-expand {
        grid-area: expand;
    }

    .word-name {
        grid-area: name;
        font-size: var(--text-md);
        min-width: 0;
    }

    /*
     * The speaker and the level share a column, so they share its middle: the button spreads
     * to the full width and centres its drawing, and the badge centres too. Aligned by their
     * left edges instead, the drawing sat eight pixels in from the badge — the button's own
     * side padding, which is there to be hit with a thumb and cannot simply be dropped.
     */
    .word-speaker {
        grid-area: speaker;
        justify-self: stretch;
        padding-left: 0;
        padding-right: 0;
        text-align: center;
    }

    .word-pos {
        grid-area: pos;
        font-size: var(--text-xs);
        min-width: auto;
    }

    /*
     * The level goes first on the line and gets a column of its own, wide enough for any of
     * the six. Behind the part of speech it began wherever that word happened to end — after
     * "noun" in one row and after "adjective" in the next — so the badges wandered left and
     * right down a list read as a column. The width is fixed rather than a minimum so that
     * the term above, which spans this column, cannot stretch it either.
     */
    .word-level {
        grid-area: level;
        justify-self: center;
    }

    .word-add {
        grid-area: add;
    }

    /* The sentence has no room on a phone; it is one tap away, inside the opened word. */
    .word-example {
        display: none;
    }

    /* Tabs */
    .tab-nav {
        gap: var(--space-1);
    }

    .tab-btn {
        padding: var(--space-2) var(--space-3);
        font-size: var(--text-xs);
    }

    .tab-arrow {
        font-size: var(--text-sm);
    }

    /* Movie nav */
    .movie-nav {
        flex-direction: column;
    }

    .movie-nav-btn {
        padding: var(--space-3) var(--space-5);
        font-size: var(--text-sm);
        justify-content: center;
    }

    /* Level pills */
    .level-pills {
        gap: var(--space-2);
    }

    .level-pill {
        font-size: var(--text-xs);
        padding: 2px var(--space-2);
    }

    .topic-pill {
        font-size: var(--text-xs);
        padding: 2px var(--space-2);
    }

    /* Stat pills wrap */
    /*
     * Four counts as two rows of two, rather than three across and one dropped underneath.
     * Wrapped, the fourth cell left a hole beside it and the third kept a divider running
     * into nothing — a table with a corner missing.
     */
    .stat-pills {
        display: grid;
        grid-template-columns: 1fr 1fr;
        width: 100%;
    }

    .stat-pill {
        border-right: 1px solid var(--rule);
        border-bottom: 1px solid var(--rule);
    }

    .stat-pill:nth-child(2n) {
        border-right: none;
    }

    .stat-pill:nth-last-child(-n + 2) {
        border-bottom: none;
    }

    .filter-btns {
        gap: var(--space-2);
    }

    .filter-btn {
        font-size: var(--text-xs);
        padding: 3px var(--space-2);
    }

    /* Bottom bar */
    /*
     * One line per destination, count and all.
     *
     * The count broke onto a second line inside the button — "Список изучения" over "(12)" —
     * which made the bar two rows tall and the two buttons different shapes. A name and its
     * count are one phrase and are not worth breaking, so the pair gives up width instead.
     */
    .bottom-btn {
        padding: var(--space-2) 14px;
        font-size: var(--text-sm);
        white-space: nowrap;
    }

    /* Login banner */
    .login-banner {
        font-size: var(--text-sm);
        flex-direction: column;
        gap: var(--space-3);
        text-align: center;
    }

    /* Modal */
    .modal-card {
        margin: var(--space-4);
        max-width: calc(100vw - 2rem) !important;
    }

    /* Footer */
    .site-footer {
        font-size: var(--text-xs);
    }

    /* Breadcrumbs */
    .breadcrumb {
        font-size: var(--text-sm);
    }

    /* Stat pills */
    .stat-pill {
        padding: 0.35rem var(--space-2);
        font-size: var(--text-xs);
    }

    /* Study card */
    /*
     * The word on the left and the way to remove it on the right, as on a wide screen. Made
     * a column, the cross ended up below the meaning, looking like part of the entry rather
     * than the control that deletes it.
     */
    .study-card {
        align-items: flex-start;
        gap: var(--space-3);
    }

    .study-card > div:first-child {
        flex: 1;
        min-width: 0;
    }

    /*
     * The whole summary on one line: the bar, how far along it is, and two exports as
     * drawings. Bulma stacks a level on a phone, which left the bar squeezed into half the
     * screen and the buttons in a column beside it; spelled out on a line of their own they
     * cost a row for two words nobody reads twice. The name stays on the button for a
     * pointer to find and a screen reader to read.
     */
    .study-summary {
        flex-direction: row;
        align-items: center;
        gap: var(--space-2);
    }

    .export-label {
        display: none;
    }

    .export-btn {
        padding-left: var(--space-2);
        padding-right: var(--space-2);
    }

    .study-summary .progress-area {
        display: flex;
        flex-direction: row;
        align-items: center;
        gap: var(--space-3);
        width: 100%;
    }

    .study-summary .progress-item {
        flex: 1;
        min-width: 0;
        margin: 0;
    }

    .study-summary .progress-bar {
        width: 100%;
    }

    /* Half the width each, rather than a narrow pair huddled at the right edge. Bulma sizes
       a level's end to its contents, which is right beside a bar and wrong on its own line. */
    .study-summary .level-right {
        display: flex;
        flex-direction: row;
        gap: var(--space-2);
        margin-top: 0;
        flex-shrink: 0;
    }

    .study-summary .level-right .level-item {
        margin: 0;
    }


    /* Exercise */
    /* Five tabs across a phone: the labels give up their padding before they give up wrapping. */
    .exercise-types {
        gap: 0;
    }

    /*
     * Five names across a phone, one word each.
     *
     * The full names do not fit a fifth of the row and were made to: broken across two lines
     * mid-phrase — "Слушай и / выбирай" — and on a narrow phone hyphenated mid-word, which
     * left five ragged blocks of text where five labels should be. Each drill therefore has
     * a one-word name for this width, and the tabs are one line again.
     */
    .exercise-type {
        flex: 1 1 0;
        font-size: var(--text-xs);
        padding: var(--space-2) 2px;
    }

    .exercise-type-label {
        display: none;
    }

    .exercise-type-label.is-short {
        display: block;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        max-width: 100%;
    }

    .exercise-type .icon {
        width: 22px;
        height: 22px;
    }

    /*
     * The flag pinned to the middle of the right edge covers the buttons that add a word, so
     * on a phone it goes and the burger carries the same action as a row.
     */
    .report-btn {
        display: none;
    }

    /* Prevent horizontal overflow */
    body {
        overflow-x: hidden;
    }

    /*
     * Nothing on a phone scrolls sideways.
     *
     * These rows used to: a gesture that hides most of the choices behind an edge and is easy
     * to miss altogether. What a reader chooses from — the level and the topic — becomes a
     * dropdown instead. What is only there to be read — the film's own counts, which the bar
     * charts under "Details" repeat — simply wraps onto as many lines as it needs.
     */
    .level-pills.is-filter,
    .lang-tags.is-filter {
        display: none;
    }

    /*
     * Two dropdowns on one line, and no labels over them.
     *
     * "Уровень:" over a box that reads "Все уровни" is the same word twice, and each label
     * cost a line of a screen that has few to spare. The unfiltered option names the filter,
     * which is all the label was doing.
     */
    #word-filters {
        display: flex;
        flex-wrap: wrap;
        gap: var(--space-2);
    }

    /*
     * The word "filters" gets its own line rather than a share of the row.
     *
     * Beside the dropdowns it took width three controls were already short of, and squeezed
     * them to "Bce ypo~". It costs one short line and it is the only thing on the page saying
     * what the row of boxes is for.
     */
    .filter-bar-title {
        flex-basis: 100%;
        margin-bottom: 0;
    }

    .filter-select {
        display: flex;
        flex: 1;
        min-width: 0;
        margin-bottom: var(--space-3);
    }

    .filter-select .level-label {
        display: none;
    }

    .lang-dropdown {
        display: block;
    }

    .level-pills {
        flex-wrap: wrap;
    }

}
/*
 * The header's own breakpoint, which is not Bulma's.
 *
 * Bulma folds a navbar into a burger below 1024px, so a tablet or a narrowed window got the
 * burger while there was room for the header itself several times over. The header therefore
 * takes the desktop shape from 769px — the same line every other rule in this stylesheet
 * turns on — and the burger is left to phones. That means restating the four rules Bulma
 * keeps for its own width, and turning its two visibility helpers round: they follow the
 * 1024px line, and inside this header the line has moved.
 */
@media (min-width: 769px) {
    .site-header,
    .site-header .navbar-menu,
    .site-header .navbar-end {
        display: flex;
        align-items: stretch;
    }

    /* Bulma dresses the menu as a dropped panel below its own breakpoint: a white card with
       a shadow. Above ours it is simply the right-hand end of the header. */
    .site-header .navbar-menu {
        flex-grow: 1;
        flex-shrink: 0;
        background: none;
        box-shadow: none;
        padding: 0;
        max-height: none;
        overflow: visible;
    }

    .site-header .navbar-end {
        justify-content: flex-end;
        margin-inline-start: auto;
    }

    .site-header .navbar-item {
        display: flex;
        align-items: center;
    }

    .site-header .navbar-burger {
        display: none;
    }

    .site-header .is-hidden-touch {
        display: flex !important;
    }

    .site-header .is-hidden-desktop {
        display: none !important;
    }
}

/*
 * The burger's own breakpoint.
 *
 * The burger is now a phone's control, so what it opens is styled at a phone's width.
 * These rules used to sit below 768px while Bulma brought the burger out below 1024px,
 * and in between the menu opened as an unstyled list of rows with no card around them.
 */
@media (max-width: 768px) {
    /*
     * The burger and what it opens.
     *
     * It used to open a white slab with a note, a stretched outlined button and a loose
     * dropdown in it — three shapes, three sizes, none of them lining up. Now it opens a
     * card of rows: every entry is the same height, the same padding, and divided from the
     * next by a hairline, which is what a menu on a phone is expected to look like.
     */
    .navbar-burger {
        color: var(--accent);
    }

    .navbar-burger span {
        background-color: var(--accent) !important;
        height: 2px !important;
    }

    /* A tap leaves :hover behind on a touch screen, and with it a grey box round the cross. */
    .navbar-burger:hover,
    .navbar-burger:active,
    .navbar-burger.is-active {
        background-color: transparent !important;
    }

    .navbar-menu.is-active {
        padding: 0;
        /* A menu of three rows has no business being a thousand pixels wide: it stops at a
           readable width and hangs under the burger that opened it. */
        max-width: min(100%, 22rem);
        margin-top: var(--space-2);
        margin-left: auto;
        border: 1px solid var(--rule);
        border-radius: var(--radius);
        box-shadow: var(--shadow);
        overflow: hidden;
    }

    .navbar-menu .navbar-item {
        padding: 0;
    }

    .navbar-menu .navbar-item + .navbar-item {
        border-top: 1px solid var(--rule);
    }

    /* One row, one shape: label on the left, whatever it names on the right. */
    .menu-row {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
        width: 100%;
        min-height: 44px;
        padding: var(--space-2) var(--space-4);
        font-size: var(--text-sm);
        background: none;
        border: none;
        text-align: left;
        color: var(--ink);
    }

    .menu-row.is-quiet {
        color: var(--ink-faint);
    }

    .menu-row-label {
        color: var(--ink-soft);
    }

    /* The one row that does something rather than showing something. */
    .menu-action {
        color: var(--accent);
        font-weight: 600;
        font-size: var(--text-md);
        cursor: pointer;
    }

    .menu-row .icon {
        width: 1.1em;
        height: 1.1em;
        color: var(--ink-faint);
    }

    .menu-action .icon {
        color: inherit;
    }

    .menu-action:active {
        background: var(--accent-light);
    }
}


/* A bottom-bar destination that needs words before it means anything. */
.bottom-btn.is-disabled {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: auto;
}

/*
 * Exercises.
 *
 * The templates carry a block name and nothing else: every spacing, alignment and colour
 * decision lives here, so the screen can be reworked without touching Kotlin.
 */
/*
 * How far through the session you are — the one line of status the page needs.
 *
 * It replaces a bordered pill reading "Exercises — 12 words", which looked like a button
 * nobody could press and answered a question nobody asked: the count of words in the list
 * says nothing about where you are in it.
 */
.exercise-progress {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

/* The bar yields its width to the label and the button, but never all of it: a longer
   translation should push the row onto two lines, not quietly delete the gauge. */
.exercise-progress-track {
    flex: 1 1 60px;
    min-width: 60px;
    height: 6px;
    border-radius: var(--radius-pill);
    background: var(--rule);
    overflow: hidden;
}

.exercise-progress-fill {
    height: 100%;
    border-radius: inherit;
    background: var(--type-ink, var(--accent));
    transition: width 0.3s ease;
}

.exercise-progress-text {
    flex-shrink: 0;
    color: var(--ink-soft);
    font-size: var(--text-md);
    white-space: nowrap;
}

/*
 * The way onward, kept out of the card.
 *
 * Outlined rather than filled: it is the only thing left to press, so it has nothing to
 * compete with and no reason to be the loudest mark on a pale card. Sitting in the progress
 * row it also stays put — the card below can grow by a line of feedback and a line of
 * dialogue without the button moving out from under the pointer.
 */
.exercise-advance {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
    margin-left: auto;
    min-height: 40px;
    padding: var(--space-1) var(--space-4);
    border: 1px solid var(--type-ink, var(--accent));
    border-radius: var(--radius-pill);
    background: var(--surface);
    color: var(--type-ink, var(--accent));
    font-size: var(--text-md);
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
}

.exercise-advance .icon {
    width: 1.1em;
    height: 1.1em;
}

.exercise-advance:hover:not([disabled]) {
    background: var(--type-ink, var(--accent));
    color: var(--surface);
}

/* Present from the first question, so the row never changes shape; plainly not yet pressable. */
.exercise-advance[disabled] {
    border-color: var(--rule);
    color: var(--ink-faint);
    cursor: default;
}

.listen-control {
    text-align: center;
    margin-bottom: var(--space-4);
}

/*
 * The play button, in one piece.
 *
 * It used to be declared twice, a thousand lines apart: one half gave it 60x60 and a large
 * type size, the other overrode the type size and added padding meant for a button with a
 * word in it. The icon was then laid out as a letter on a text line — `.icon` carries a
 * `vertical-align` for sitting beside words — inside a box whose content area was 12px wide,
 * so it printed high and to the left of the circle. Centring an icon is a job for the box,
 * not for the baseline.
 */
.listen-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 60px;
    height: 60px;
    padding: 0;
    border: none;
    border-radius: var(--radius-pill);
    background: var(--type-ink, var(--accent));
    color: var(--surface);
    cursor: pointer;
}

.listen-btn .icon {
    width: 26px;
    height: 26px;
    vertical-align: baseline;
}

.gap-sentence {
    margin-bottom: var(--space-4);
    color: var(--ink);
    line-height: 1.6;
}


.exercise-score {
    text-align: center;
    margin-top: var(--space-4);
}

.exercise-score.is-final {
    margin-top: var(--space-5);
}

.exercise-score > p:first-child {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: var(--space-3);
}

.exercise-score.is-final > p:first-child {
    font-size: var(--text-xl);
}

.exercise-score-detail {
    color: var(--ink-faint);
    font-size: var(--text-sm);
    margin-bottom: var(--space-4);
}

.score-bar {
    max-width: 300px;
    margin: 0 auto var(--space-4);
    height: 8px;
    border-radius: var(--radius-pill);
    background: var(--rule);
    overflow: hidden;
}

.score-bar-fill {
    height: 100%;
    background: var(--green);
}

.exercise-empty,
.exercise-empty-state {
    text-align: center;
    color: var(--ink-faint);
}

.exercise-empty {
    margin-top: var(--space-5);
}

.exercise-empty-state {
    margin-top: var(--space-6);
}

.exercise-empty-state p {
    margin-bottom: var(--space-4);
}

.exercise-empty-state .button i {
    margin-right: var(--space-2);
}

/*
 * Buttons inside an exercise take their colour from context, not from a modifier class.
 *
 * The bare `button` on a score screen also takes its shape here: it carries no Bulma class,
 * so it was given a colour and nothing else and arrived as a line of dark text with no edges.
 * A score screen is the whole of what is on offer at that moment, so unlike the advance
 * button in the progress row, this one is filled.
 */
.exercise-score button,
.exercise-empty-state .button {
    background: var(--type-ink, var(--accent));
    border: 1px solid var(--type-ink, var(--accent));
    color: var(--surface);
    font-weight: 600;
}

.exercise-score button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: var(--space-2) var(--space-5);
    border-radius: var(--radius-pill);
    font-size: var(--text-md);
    cursor: pointer;
}

.exercise-score button:hover {
    filter: brightness(1.12);
}

/* Vocabulary page: spacing that used to be utility classes in the template. */
.level-pills,
.topic-pills {
    margin-bottom: var(--space-3);
}

.word-detail-context {
    margin-bottom: var(--space-3);
}

.words-filter .field,
.field {
    margin-bottom: var(--space-4);
}

.filter-count {
    text-align: center;
    color: var(--ink-faint);
    margin-bottom: var(--space-4);
}

/*
 * Blocks that replaced Bulma component and utility classes in the templates.
 *
 * The markup now names what a thing is; everything about how it looks is decided here.
 * These reproduce what box, tag, notification, level and title used to provide.
 */

/* Cards that group a section of the movie page. */
.panel {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5);
    margin-bottom: var(--space-5);
}

.panel-title,
.section-title {
    font-size: var(--text-lg);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: var(--space-3);
}

.panel-subtitle {
    font-size: var(--text-md);
    font-weight: 600;
    color: var(--ink);
    margin-bottom: var(--space-2);
}

.panel-note {
    color: var(--ink-faint);
}

.panel-actions {
    text-align: center;
}

.page-title {
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--ink);
    margin-bottom: var(--space-4);
}

.section-title.is-centered,
.notice.is-centered {
    text-align: center;
}

/*
 * The film's name, and room for it to be long.
 *
 * An episode title is "Jeeves And Wooster — In Court After The Boat Race", and Bulma's level
 * refuses to shrink either side, so on a phone the name ran off the card and was cut mid-word
 * — on the one page whose entire job is to say which film you are looking at. The left side
 * is allowed to shrink and the name to wrap; below the breakpoint the tags go under it rather
 * than competing for the same line.
 */
.movie-header .level-left {
    flex-shrink: 1;
    min-width: 0;
}

.movie-title {
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--ink);
    margin-bottom: 0;
    overflow-wrap: break-word;
}

@media (max-width: 768px) {
    .movie-header {
        flex-direction: column;
        align-items: flex-start;
        gap: var(--space-2);
    }

    .movie-header .level-right {
        margin-top: 0;
        justify-content: flex-start;
    }

    .movie-title {
        font-size: var(--text-lg);
    }
}

.movie-meta,
.study-meta {
    color: var(--ink-faint);
    font-size: var(--text-sm);
}

.movie-section {
    margin: var(--space-4) 0;
}

/* Small labels: language, model, level. */
.movie-tag {
    display: inline-block;
    background: var(--accent-light);
    color: var(--accent);
    border-radius: var(--radius-sm);
    padding: var(--space-1) var(--space-2);
    font-size: var(--text-xs);
    font-weight: 600;
    margin-right: var(--space-2);
}

.movie-tag.is-muted {
    background: var(--surface-sunken);
    color: var(--ink-soft);
}

.stat-row {
    margin-bottom: var(--space-3);
}

.stat-row-label {
    font-weight: 600;
}

.level-pills.is-tight {
    margin-bottom: var(--space-2);
}

/* Home. */
.home-heading {
    text-align: center;
    margin-bottom: var(--space-5);
}

.home-empty {
    text-align: center;
    color: var(--ink-faint);
}

.intro-cta {
    text-align: center;
    margin: 0 0 var(--space-4);
}

.lang-tags {
    margin-bottom: var(--space-4);
}

/* Upload. */
.upload-lead {
    text-align: center;
    font-weight: 700;
    margin-bottom: var(--space-3);
}

.is-strong {
    font-weight: 700;
}

.notice {
    position: relative;
    border-radius: var(--radius);
    padding: var(--space-4);
    margin-bottom: var(--space-4);
    background: var(--surface-sunken);
    color: var(--ink);
}

/*
 * The close button. Bulma only parks it out of the way inside its own .notification, and
 * this is our own box, so it sat against the first word of the message. It belongs in the
 * right-hand corner, with room reserved so a long message cannot run underneath it.
 */
.notice:has(.delete) {
    padding-right: var(--space-7);
}

.notice .delete {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
}

.notice.is-danger {
    background: var(--tint-bad);
    color: var(--red);
}

.notice.is-success {
    background: var(--tint-good);
    color: var(--green);
}

.notice.is-info {
    background: var(--accent-light);
    color: var(--accent);
}

.promo-apply {
    margin-top: var(--space-2);
}

/*
 * A finished extraction. The words have landed in a film the reader has not seen yet, so the
 * row carries the way there rather than leaving them to find it in the library.
 */
.job-done .level-right {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.job-open.button {
    background: var(--green);
    border-color: var(--green);
    color: var(--surface);
    font-weight: 600;
    white-space: nowrap;
}

.job-open.button:hover {
    opacity: 0.9;
    color: var(--surface);
}

/* Study. */
.study-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-4);
}

.study-progress-row {
    margin-bottom: var(--space-3);
}

.study-note {
    color: var(--ink-faint);
    margin-bottom: var(--space-4);
}

.study-empty {
    text-align: center;
    margin-top: var(--space-6);
}

.study-empty .button i,
.exercise-empty-state .button i {
    margin-right: var(--space-2);
}

/* Shared chrome: navbar, modals, extraction progress. */
.job-card {
    background: var(--surface);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
    padding: var(--space-4);
    margin-bottom: var(--space-3);
}

.job-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--space-2);
}

.job-title {
    font-weight: 600;
}

.job-detail {
    font-size: var(--text-xs);
    margin-top: var(--space-1);
    color: var(--ink-soft);
}

/* What the film cost, beside how many words it bought. */
/*
 * What the film cost us, on a banner that is otherwise all good news.
 *
 * It was the faintest grey in the palette, at small size, over pale green — the one number
 * on the strip that had to be squinted at. It now sits in a white chip: dark ink on its own
 * background rather than borrowed contrast from whatever the banner is coloured, which also
 * separates it from the word count beside it instead of running the two figures together.
 * Even-width figures so a column of these lines up as jobs stack.
 */
.job-cost {
    display: inline-flex;
    align-items: center;
    margin-left: var(--space-3);
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--rule);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font-size: var(--text-xs);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* The count it stands next to, so the pair reads as two facts and not one long number. */
.job-words {
    color: var(--ink-soft);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.job-hint {
    text-align: center;
    font-size: var(--text-xs);
    color: var(--ink-faint);
}

/*
 * A bar that is working, whether or not it has moved.
 *
 * A chunk of a film takes a minute and a half, so the count underneath stands still long
 * enough for the whole thing to look dead. The stripes travel while the work is out; the
 * filled part still says honestly how much is done.
 *
 * The colour is the vocabulary's own indigo — this bar is a film's words being made — rather
 * than the cyan Bulma paints anything called "info", which belongs to no palette here.
 */
.job-card .progress {
    background-color: var(--accent-light);
}

.job-card .progress::-webkit-progress-bar {
    background-color: var(--accent-light);
}

/* In Chrome the track paints over the element's own background, so the stripes of an empty
   bar have to be laid on the track itself or they are never seen. */
.job-card .progress.is-working::-webkit-progress-bar {
    background-image: repeating-linear-gradient(
        45deg,
        var(--accent-border) 0 8px,
        var(--accent-light) 8px 16px
    );
    background-size: 22.7px 100%;
    animation: progress-stripes 1s linear infinite;
}

.job-card .progress::-webkit-progress-value {
    background-color: var(--accent);
}

.job-card .progress::-moz-progress-bar {
    background-color: var(--accent);
}
.progress.is-working::-webkit-progress-value {
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.25) 0 8px,
        transparent 8px 16px
    );
    animation: progress-stripes 1s linear infinite;
}

.progress.is-working::-moz-progress-bar {
    background-image: repeating-linear-gradient(
        45deg,
        rgba(255, 255, 255, 0.25) 0 8px,
        transparent 8px 16px
    );
    animation: progress-stripes 1s linear infinite;
}

/* Nothing done yet is the longest wait of all, so the track carries the stripes itself. */
.progress.is-working {
    background-image: repeating-linear-gradient(
        45deg,
        var(--accent-border) 0 8px,
        var(--accent-light) 8px 16px
    );
    background-size: 22.7px 100%;
    animation: progress-stripes 1s linear infinite;
}

@keyframes progress-stripes {
    from {
        background-position: 0 0;
    }

    to {
        background-position: 22.7px 0;
    }
}

.notice.is-warning {
    background: hsl(37deg 80% 95%);
    color: hsl(37deg 70% 32%);
}

.navbar-note,
.lang-note,
.modal-note {
    color: var(--ink-faint);
    font-size: var(--text-sm);
}

.lang-note {
    margin-top: var(--space-2);
}

.modal-note,
.login-success-text {
    margin-bottom: var(--space-4);
}

.breadcrumb {
    margin-bottom: var(--space-4);
}

.help {
    margin-bottom: var(--space-4);
}

.callback-state {
    text-align: center;
    margin-top: var(--space-6);
}

.panel-actions .fas.fa-chevron-down {
    margin-left: var(--space-1);
}

/* Illustrations. */
/*
 * The drawing is allowed to be large, but not larger than the screen it is on.
 *
 * At its natural size it filled a laptop by itself and pushed the four steps — which are the
 * page's actual argument for signing up — off the bottom. The cap is against the window
 * rather than a fixed number of pixels, so a short window gives up picture instead of losing
 * the rest of the page.
 */
.hero-art {
    display: block;
    width: 100%;
    max-width: 560px;
    max-height: 42vh;
    height: auto;
    object-fit: contain;
    margin: 0 auto var(--space-4);
}

/*
 * Wide enough for two columns, so the drawing stands beside the words rather than on top of
 * them. Stacked, the hero was a screenful on its own and everything below it was a scroll
 * away; side by side it is half as tall and the steps are simply there.
 *
 * 800 rather than the usual desk breakpoint: a small laptop and a tablet held sideways are
 * both wide enough to stand the two side by side, and both short enough to need it most.
 */
@media (min-width: 800px) {
    .hero-section {
        display: grid;
        grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
        align-items: center;
        gap: var(--space-6);
        text-align: left;
        padding: var(--space-4) var(--space-5);
    }

    .hero-art {
        margin: 0;
        max-width: 100%;
    }

    .hero-subtitle {
        margin-left: 0;
        margin-right: 0;
    }
}

.empty-state {
    text-align: center;
    padding: var(--space-6) var(--space-4);
    color: var(--ink-faint);
}

.empty-state img {
    display: block;
    width: 100%;
    max-width: 200px;
    height: auto;
    margin: 0 auto var(--space-4);
    transition: transform 0.15s ease;
}

/* The empty library is a route to the upload page, not just a picture of one. */
.empty-state-link {
    display: block;
    color: inherit;
    text-decoration: none;
}

.empty-state-link:hover img,
.empty-state-link:focus-visible img {
    transform: scale(1.04);
}

.empty-state-link:hover p {
    color: var(--accent);
}

.empty-state p {
    max-width: 34ch;
    margin: 0 auto;
    line-height: 1.5;
}

/* The vocabulary hint sits under a long list, so it stays quieter than a true empty state. */
.empty-state.is-hint {
    padding-top: var(--space-4);
}

.empty-state.is-hint img {
    max-width: 140px;
}

@media (max-width: 768px) {
    .hero-art {
        max-width: 100%;
        margin-bottom: var(--space-3);
    }

    .empty-state img {
        max-width: 160px;
    }
}

/*
 * The narrowest phones still in use give each drill tab 54 pixels, and «Карточки» at the
 * size above wants 55 — one letter over, and it ends in an ellipsis. A point smaller is
 * cheaper than a sixth name for the same drill.
 */
@media (max-width: 360px) {
    .exercise-type {
        font-size: 0.7rem;
    }
}

/*
 * The icon set, drawn for this project and served from here.
 *
 * Each is a CSS mask filled with the current text colour, so an icon inside a dark button is
 * light and one beside grey text is grey, without the template saying anything about it.
 * This replaced Font Awesome: 100KB from a CDN for a dozen glyphs in someone else's hand.
 */
.icon {
    display: inline-block;
    width: 1.35em;
    height: 1.35em;
    vertical-align: -0.28em;
    background-color: currentColor;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    -webkit-mask-size: contain;
    mask-size: contain;
    flex-shrink: 0;
}

.icon-book-open {
    -webkit-mask-image: url("/static/img/icons/book-open.svg");
    mask-image: url("/static/img/icons/book-open.svg");
}

.icon-speaker {
    -webkit-mask-image: url("/static/img/icons/speaker.svg");
    mask-image: url("/static/img/icons/speaker.svg");
}

.icon-ticket {
    -webkit-mask-image: url("/static/img/icons/ticket.svg");
    mask-image: url("/static/img/icons/ticket.svg");
}

.icon-gift {
    -webkit-mask-image: url("/static/img/icons/gift.svg");
    mask-image: url("/static/img/icons/gift.svg");
}

.icon-arrow-right {
    -webkit-mask-image: url("/static/img/icons/arrow-right.svg");
    mask-image: url("/static/img/icons/arrow-right.svg");
}

.icon-clipboard-list {
    -webkit-mask-image: url("/static/img/icons/clipboard-list.svg");
    mask-image: url("/static/img/icons/clipboard-list.svg");
}

.icon-upload {
    -webkit-mask-image: url("/static/img/icons/upload.svg");
    mask-image: url("/static/img/icons/upload.svg");
}

.icon-plus {
    -webkit-mask-image: url("/static/img/icons/plus.svg");
    mask-image: url("/static/img/icons/plus.svg");
}

.icon-check {
    -webkit-mask-image: url("/static/img/icons/check.svg");
    mask-image: url("/static/img/icons/check.svg");
}

.icon-download {
    -webkit-mask-image: url("/static/img/icons/download.svg");
    mask-image: url("/static/img/icons/download.svg");
}

.icon-graduation-cap {
    -webkit-mask-image: url("/static/img/icons/graduation-cap.svg");
    mask-image: url("/static/img/icons/graduation-cap.svg");
}

.icon-play {
    -webkit-mask-image: url("/static/img/icons/play.svg");
    mask-image: url("/static/img/icons/play.svg");
}

.icon-flag {
    -webkit-mask-image: url("/static/img/icons/flag.svg");
    mask-image: url("/static/img/icons/flag.svg");
}

.icon-film {
    -webkit-mask-image: url("/static/img/icons/film.svg");
    mask-image: url("/static/img/icons/film.svg");
}

.icon-chevron-down {
    -webkit-mask-image: url("/static/img/icons/chevron-down.svg");
    mask-image: url("/static/img/icons/chevron-down.svg");
}

.icon-star {
    -webkit-mask-image: url("/static/img/icons/star.svg");
    mask-image: url("/static/img/icons/star.svg");
}

.icon-film-upload {
    -webkit-mask-image: url("/static/img/icons/film-upload.svg");
    mask-image: url("/static/img/icons/film-upload.svg");
}

.icon-word-in-line {
    -webkit-mask-image: url("/static/img/icons/word-in-line.svg");
    mask-image: url("/static/img/icons/word-in-line.svg");
}

.icon-list-add {
    -webkit-mask-image: url("/static/img/icons/list-add.svg");
    mask-image: url("/static/img/icons/list-add.svg");
}

.icon-repeat-arrows {
    -webkit-mask-image: url("/static/img/icons/repeat-arrows.svg");
    mask-image: url("/static/img/icons/repeat-arrows.svg");
}

.icon-list-check {
    -webkit-mask-image: url("/static/img/icons/list-check.svg");
    mask-image: url("/static/img/icons/list-check.svg");
}

.icon-link-pairs {
    -webkit-mask-image: url("/static/img/icons/link-pairs.svg");
    mask-image: url("/static/img/icons/link-pairs.svg");
}

.icon-cards-stack {
    -webkit-mask-image: url("/static/img/icons/cards-stack.svg");
    mask-image: url("/static/img/icons/cards-stack.svg");
}

/*
 * Icons inside buttons.
 *
 * Bulma tugs them sideways with negative margins sized for a Font Awesome glyph, which sat in
 * a box wider than its drawing; ours fill their box, so the tug printed the icon on top of the
 * first character. Cancel those margins at Bulma's own specificity and let the button's flex
 * gap do the spacing — that works whether the label is a span or bare text, which no sibling
 * selector can tell apart.
 */
.button {
    gap: var(--space-2);
}

.button .icon,
.button .icon:first-child:not(:last-child),
.button .icon:last-child:not(:first-child),
.button .icon:first-child:last-child {
    margin: 0;
}

.movie-nav-btn .icon,
.bottom-btn .icon {
    margin: 0;
}

/*
 * Below this the Russian pair — the longest of the three languages — no longer fits a line at
 * the size above, and a wrapped count was what we were getting rid of.
 */
@media (max-width: 380px) {
    .bottom-btn {
        font-size: var(--text-xs);
        padding: var(--space-2) 8px;
        gap: var(--space-1);
    }
}

/*
 * The guide's heading and its toggle, on a screen too narrow to stand them side by side.
 *
 * As two columns of a row the button took a third of the width and held it for the whole
 * height of the card, so four lines of explanation ran down a gutter beside a large empty
 * square. Floated instead, the button keeps its corner and the words close over underneath
 * it — which is what the space was for.
 */
@media (max-width: 480px) {
    .intro-summary {
        display: block;
    }

    .intro-toggle-text {
        display: inline-flex;
        float: right;
        margin: 0 0 var(--space-2) var(--space-3);
    }

    .intro-lead p {
        /* A float is not cleared by the paragraph itself; without this the last line can sit
           under the button and look like a second column. */
        overflow: hidden;
    }
}

/*
 * The view from behind the counter: plain tables, because the questions are plain ones — who
 * signed up, what they made, what it cost. Nothing here is for a customer to admire.
 */
/*
 * Six columns of figures on a screen that fits three.
 *
 * The table was simply cut off at the edge of its panel on a phone: films, words and spending
 * were drawn, and unreachable. It scrolls inside its own box now, so the columns can be got
 * at without the whole page sliding about — the row of headings stays put beside them, which
 * is the difference between reading a table and guessing at one.
 *
 * A table is the right shape here. This page is read to compare accounts against each other,
 * and a stack of cards is a shape you cannot compare down.
 */
.admin-table-box {
    overflow-x: auto;
    margin-bottom: var(--space-4);
    -webkit-overflow-scrolling: touch;
}

.admin-table {
    width: 100%;
    min-width: 30rem;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.admin-table th {
    text-align: left;
    font-weight: 600;
    color: var(--ink-faint);
    text-transform: uppercase;
    font-size: var(--text-xs);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--rule);
}

.admin-table td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--rule);
}

.admin-table tr:last-child td {
    border-bottom: none;
}

.admin-table .is-quiet {
    color: var(--ink-faint);
}

.admin-balance-value {
    font-size: var(--text-2xl);
    font-weight: 700;
    color: var(--accent);
    margin-bottom: var(--space-3);
}

/* The way in, in the header, for the one account that has it. */
.admin-link {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: 600;
}
